page ,132 ; SCCSID = @(#)tbatch.asm 4.5 85/10/01 ; SCCSID = @(#)tbatch.asm 4.5 85/10/01 TITLE Batch processing routines ;/* ; * Microsoft Confidential ; * Copyright (C) Microsoft Corporation 1991 ; * All Rights Reserved. ; */ ; ; Revision History ; ================ ; ; M006 SR 07/20/90 Changed BatCom to understand batch ; segments in UMBs. Check only for ; overlap. ; M017 MD 08/10/90 Eliminate extra DEC, to fix bug #1 ; ; M037 SR 11/1/90 Bug #1745 & #3438 fixed. Fixed ReadBat ; to check if we have hit EOF on ; batchfile and if so, just clear everything ; and return finishing batch processing. ; .xlist .xcref include comsw.asm include dossym.inc include syscall.inc include comseg.asm include comequ.asm include doscntry.inc ;an000; include version.inc .list .cref DATARES SEGMENT PUBLIC BYTE ;AC000; EXTRN BATCH:WORD EXTRN Batch_Abort:byte EXTRN call_batch_flag:byte EXTRN ECHOFLAG:BYTE EXTRN forflag:byte EXTRN forptr:word EXTRN IFFlag:BYTE EXTRN In_Batch:byte EXTRN LTPA:WORD EXTRN Nest:word EXTRN next_batch:word EXTRN nullflag:byte EXTRN PIPEFLAG:BYTE EXTRN RES_TPA:WORD EXTRN SINGLECOM:WORD EXTRN SUPPRESS:BYTE ;AC000; DATARES ENDS TRANDATA SEGMENT PUBLIC BYTE ;AC000; EXTRN BADBAT_PTR:WORD EXTRN Extend_buf_ptr:word ;AC000; EXTRN Extend_buf_sub:byte ;AN022; EXTRN msg_disp_class:byte ;AC000; EXTRN NEEDBAT_PTR:WORD EXTRN pausemes_ptr:word ;AC000; TRANDATA ENDS TRANSPACE SEGMENT PUBLIC BYTE ;AC000; EXTRN BatBufPos:WORD EXTRN BATHAND:WORD EXTRN bwdbuf:byte ;AN022; EXTRN BYTCNT:WORD EXTRN COMBUF:BYTE EXTRN EXECPATH:BYTE EXTRN ID:BYTE EXTRN RCH_ADDR:DWORD EXTRN RESSEG:WORD EXTRN string_ptr_2:word ;AC000; EXTRN TPA:WORD EXTRN TRAN_TPA:WORD extrn TranSpaceEnd:byte ; M006 TRANSPACE ENDS TRANCODE SEGMENT PUBLIC BYTE ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING EXTRN cerror:near EXTRN tcommand:near ;--------------- TRANSPACE SEGMENT PUBLIC BYTE ;AC000; extrn arg:byte ; the arg structure! transpace ends ;--------------- Break ; ; Open the batch file. If we cannot find the batch file. If the media is ; changeable, we prompt for the change. Otherwise, we terminate the batch ; file. Leave segment registers alone. ; Procedure PromptBat,NEAR ASSUME DS:ResGroup,ES:NOTHING invoke BATOPEN ; attempt to open batch file retnc cmp dx,error_file_not_found ;AN022; Ask for diskette if file not found jz Bat_Remcheck ;AN022; cmp dx,error_path_not_found ;AN022; Ask for diskette if path not found jz Bat_Remcheck ;AN022; Otherwise, issue message and exit invoke output_batch_name ;AN022; set up batch name in bwdbuf jmp short BatDie ;AN022; Bat_Remcheck: ;AN022; Go see if media is removable CALL [RCH_ADDR] ; DX has error number JZ AskForBat ; Media is removable ; ; The media is not changeable. Turn everything off. ; invoke ForOff invoke PipeOff MOV IfFlag,AL ; No If in progress. MOV DX,OFFSET TRANGROUP:BADBAT_ptr BatDie: call BatchOff PUSH CS POP DS ASSUME DS:TranGroup invoke std_eprintf ;AC022; display message ; ; TCOMMAND resets the stack. This is the equivalent of a non-local goto. ; JMP TCOMMAND ; he cleans off stack ; ; Ask the user to reinsert the batch file ; ASKFORBAT: ASSUME DS:ResGroup PUSH DS PUSH CS POP DS ASSUME DS:TranGroup MOV DX,OFFSET TRANGROUP:NEEDBAT_ptr ;AN022; invoke std_eprintf ;Prompt for batch file on stderr mov dx,offset trangroup:pausemes_ptr ;AN000; get second part of message invoke std_eprintf ;AN000; print it to stderr CALL GetKeystroke POP DS ASSUME DS:ResGroup jmp PromptBat EndProc PromptBat ;**************************************************************** ;* ;* ROUTINE: Output_batch_name ;* ;* FUNCTION: Sets up batch name to be printed on extended error ;* ;* INPUT: DX - extended error number ;* ;* OUTPUT: Ready to call print routine ;* ;**************************************************************** public output_batch_name ;AN022; Output_batch_name proc near ;AN022; push ds ;AN022; save resident segment mov ds,[batch] ;AN022; get batch file segment assume DS:nothing ;AN022; mov SI,BatFile ;AN022; get offset of batch file invoke dstrlen ;AN022; get length of string mov di,offset Trangroup:bwdbuf ;AN022; target for batch name rep movsb ;AN022; move the name push cs ;AN022; get local segment pop ds ;AN022; assume DS:trangroup ;AN022; mov extend_buf_ptr,dx ;AN022; put message number in block mov msg_disp_class,ext_msg_class ;AN022; set up extended error msg class mov dx,offset TranGroup:Extend_Buf_ptr ;AN022; get extended message pointer mov string_ptr_2,offset trangroup:bwdbuf ;AN022; point to substitution mov extend_buf_sub,one_subst ;AN022; set up for one subst pop ds ;AN022; restore data segment ret ;AN022; return Output_batch_name endp ;AN022; Break ; ; read the next keystroke. Since there may be several characters in the queue ; after the one we ask for (function keys/Kanji), we need to flush the queue ; AFTER waiting. ; Procedure GetKeyStroke,NEAR ; ; read any character at any mode, interim mode or not. ; assume ds:trangroup PUSH DX ;AN000; 3/3/KK MOV AX,(ECS_call SHL 8) OR GetInterimMode ;AN000; 3/3/KK INT 21h ;AN000; 3/3/KK PUSH DX ;AN000; save interim state 3/3/KK MOV AX,(ECS_call SHL 8) OR SetInterimMode ;AN000; 3/3/KK MOV DL,InterimMode ;AN000; 3/3/KK INT 21h ;AN000; 3/3/KK MOV AX,(STD_CON_INPUT_FLUSH SHL 8) OR STD_CON_INPUT_no_echo INT 21h ; Get character with KB buffer flush MOV AX,(STD_CON_INPUT_FLUSH SHL 8) + 0 INT 21h MOV AX,(ECS_call SHL 8) OR SetInterimMode ;AN000; 3/3/KK POP DX ;AN000; restore interim state 3/3/KK INT 21h ;AN000; 3/3/KK POP DX ;AN000; 3/3/KK return EndProc GetKeyStroke Break ; ; ReadBat - read a single line from the batch file. Perform all substitutions ; as appropriate ; Procedure ReadBat,NEAR ASSUME DS:ResGroup,ES:TranGroup mov suppress,yes_echo ;g initialize line suppress status test byte ptr [Batch_Abort],-1 jnz Trying_To_Abort mov byte ptr [In_Batch],1 ; set flag to indicate batch job ; ;M037; Start of changes ; We check here if we have set the flag indicating that the batchfile is at ;EOF. In this case, we do not want to continue with the normal processing. ;We call GetBatByt once more so that the batch segment gets freed up, the ;batch file gets closed etc. and then return as if everything is done. ; push ds mov ds,Batch cmp ds:BatchEOF,0 ; are we at EOF in batchfile pop ds jz contbat ; no, continue normal processing invoke GetBatByt ; frees up batchseg mov es:ComBuf+2,al ; stuff CR into command buffer ; as a dummy command invoke CrLf2 ; print a CR-LF return ; done batch processing contbat: ; ;M037; End of changes ; CALL PromptBat Trying_To_Abort: MOV DI,OFFSET TRANGROUP:COMBUF+2 ; ; Save position and try to scan for first non delimiter. ; TESTNOP: MOV AX,DS MOV DS,Batch ASSUME DS:NOTHING PUSH WORD PTR DS:[BatSeek] PUSH WORD PTR DS:[BatSeek+2] ; save current location. MOV DS,AX ASSUME DS:ResGroup invoke SkipDelim ; skip to first non-delim ; ; If the first non-delimiter is not a : (label), we reseek back to the ; beginning and read the line. ; CMP AL,':' ; is it a label? POP CX POP DX ; restore position in bat file JZ NopLine ; yes, resync everything. TEST [BATCH],-1 ; are we done with the batch file? JZ RdBat CMP AL, NO_ECHO_CHAR ;g see if user wants to suppress line JNZ SET_BAT_POS ;g no - go and set batch file position MOV SUPPRESS, NO_ECHO ;g yes set flag to indicate jmp short Rdbat ;g go read batch file SET_BAT_POS: ;g PUSH DS MOV DS,Batch ASSUME DS:NOTHING MOV WORD PTR DS:[BatSeek],DX ; reseek back to beginning MOV WORD PTR DS:[BatSeek+2],CX POP DS ASSUME DS:ResGroup MOV AX,(LSEEK SHL 8) + 0 ; seek back INT 21h MOV BatBufPos,-1 ; nuke batch buffer position xor cx,cx ; Initialize line length to zero JMP SHORT RdBat ; ; The first non-delimiter is a :. This line is not echoed and is ignored. ; We eat characters until a CR is seen. ; NOPLINE: CALL SkipToEOL invoke GetBatByt ; eat trailing LF TEST [BATCH],-1 ; are we done with the batch file? JNZ TESTNOP ; no, go get another line return ; Hit EOF ; ; Read a line into the buffer pointed to by ES:DI. If any %s are seen in the ; input, we are to consider two special cases: ; ; %0 to %9 These represent replaceable parameters from the batch segment ; %sym% This is a symbol from the environment ; RDBAT: invoke GetBatByt inc cx ; Inc the line length invoke testkanj ; MSKK04 07/14/89 jz rdbat1 ; cmp cx, COMBUFLEN-1 ; jae TooLong ; can't start DBCS char at last pos'n stosb ; invoke GetBatByt ; inc cx ; jmp short SAVBATBYT ; rdbat1: ; cmp cx,COMBUFLEN ; Is it too long? jae TooLong ; Yes - handle it, handle it ; ; See if we have a parameter character. ; CMP AL,'%' ; Check for parameter JZ NEEDPARM ; ; no parameter character. Store it as usual and see if we are done. ; SAVBATBYT: STOSB CMP AL,0DH ; End of line found? JNZ RDBAT ; no, go for more ; ; We have read in an entire line. Decide whether we should echo the command ; line or not. ; Found_EOL: SUB DI,OFFSET TRANGROUP:COMBUF+3 MOV AX,DI ; remember that we've not counted the CR MOV ES:[COMBUF+1],AL ; Set length of line invoke GetBatByt ; Eat linefeed invoke BATCLOSE CMP SUPPRESS, NO_ECHO ;G JZ Reset ;G test [echoflag],1 ; To echo or not to echo, that is the jnz try_nextflag Reset: PUSH CS ; question. (Profound, huh?) POP DS ; Go back to local segment retz ; no echoing here... ; ; Echo the command line with appropriate CRLF... ; try_nextflag: cmp nullflag,nullcommand ;G was there a command last time? jz No_crlf_print ;G no - don't print crlf invoke CRLF2 ;G Print out prompt no_crlf_print: invoke PRINT_PROMPT ;G PUSH CS ;G change data segment POP DS ;G ASSUME DS:TRANGROUP mov dx,OFFSET TRANGROUP:COMBUF+2 ; get command line for echoing invoke CRPRINT invoke CRLF2 return ; ; The line was too long. Eat remainder of input text up until the CR ; TooLong: ASSUME DS:ResGroup cmp al,0dh ; Has the end of the line been reached? jz Ltlcont ; Yes, continue CALL SkipToEOL ; Eat remainder of line Ltlcont: stosb ; Terminate the command jmp Found_EOL ; Go process the valid part of the line ; ; We have found a parameter lead-in character. Check for the 0-9 case first ; NEEDPARM: invoke GetBatByt ; get next character CMP AL,'%' ; Check for two consecutive % JZ SAVBATBYT ; if so, replace with a single % CMP AL,0Dh ; Check for end-of-line JZ SAVBATBYT ; yes, treat it normally ; ; We have found %. If the is in the range 0-9, we ; retrieve the appropriate parameter from the batch segment. Otherwise we ; see if the has a terminating % and then look up the contents ; in the environment ; PAROK: SUB AL,'0' JB NEEDENV ; look for parameter in the environment CMP AL,9 JA NEEDENV ; ; We have found %. This is taken from the parameters in the ; allocated batch area. ; CBW MOV BX,AX ; move index into AX SHL BX,1 ; convert word index into byte ptr SaveReg MOV ES,Batch ; ; The structure of the batch area is: ; ; BYTE type of segment ; DWORD offset for next line ; 10 WORD pointers to parameters. -1 is empty parameter ; ASCIZ file name (with . and ..) ; BYTES CR-terminated parameters ; BYTE 0 flag to indicate end of parameters ; ; Get pointer to BX'th argument ; MOV SI,ES:BatParm[BX] RestoreReg ; ; Is there a parameter here? ; CMP SI,-1 ; Check if parameter exists JNZ Yes_there_is ;G Yes go get it JMP RDBAT ; Ignore if it doesn't ; ; Copy in the found parameter from batch segment ; Yes_there_is: PUSH DS MOV DS,Batch ASSUME DS:NOTHING dec cx ; Don't count '%' in line length CopyParm: LODSB ; From resident segment CMP AL,0DH ; Check for end of parameter JZ EndParam inc cx ; Inc the line length cmp cx,COMBUFLEN ; Is it too long? jae LineTooL ; Yes - handle it, handle it STOSB JMP CopyParm ; ; We have copied up to the limit. Stop copying and eat remainder of batch ; line. We need to make sure that the tooLong code isn't fooled into ; believing that we are at EOL. Clobber AL too. ; LineTooL: XOR AL,AL POP DS ASSUME DS:RESGROUP JMP TooLong ; ; We have copied in an entire parameter. Go back for more ; EndParam: POP DS JMP RDBat ; ; We have found % followed by something other than 0-9. We presume that there ; will be a following % character. In between is an environment variable that ; we will fetch and replace in the batch line with its value. ; NEEDENV: dec cx ;AN070; Don't count "%" SaveReg MOV DI,OFFSET TRANGROUP:ID ; temp spot for name ADD AL,'0' ; reconvert character STOSB ; store it in appropriate place ; ; loop getting characters until the next % is found or until EOL ; GETENV1: invoke GetBatByt ; get the byte STOSB ; store it CMP AL,0Dh ; EOL? JNZ GETENV15 ; no, see if it the term char ; ; The user entered a string with a % but no trailing %. We copy the string. ; mov byte ptr es:[di-1],0 ; nul terminate the string mov si,offset TranGroup:ID ; point to buffer pop di ; point to line buffer push cs pop ds call StrCpy jc LineTooL pop ds jmp SavBatByt GETENV15: CMP AL,'%' ; terminating %? JNZ GETENV1 ; no, go take out more characters ; M017 - following DEC is wrong, because we replace the % with a = here. ; This was the source of bug #1. ; dec cx ;AN070; Don't count "%" mov al,'=' ; terminate with = MOV ES:[DI-1],al ; ; ID now either has a =-terminated string which we are to find in the ; environment or a non =-terminated string which will not be found in the ; environment. ; GETENV2: MOV SI,OFFSET TRANGROUP:ID PUSH CS POP DS ; DS:SI POINTS TO NAME ASSUME DS:TRANGROUP PUSH CX INVOKE FIND_NAME_IN_environment ASSUME ES:RESGROUP POP CX PUSH ES POP DS assume ds:resgroup PUSH CS POP ES ASSUME ES:TRANGROUP MOV SI,DI POP DI ; get back pointer to command line ; ; If the parameter was not found, there is no need to perform any replacement. ; We merely pretend that we've copied the parameter. ; JC GETENV6 ; ; ES:DI points to command line being built ; DS:SI points either to nul-terminated environment object AFTER = ; ASSUME ES:NOTHING call StrCpy ; (let RdBat handle overflow) GETENV6: pop ds JMP RDBAT ; no, go back to batch file EndProc ReadBat ; ; SkipToEOL - read from batch file until end of line ; Procedure SkipToEOL,NEAR ASSUME DS:ResGroup,ES:NOTHING TEST Batch,-1 retz ; no batch file in effect invoke GetBatByt CMP AL,0Dh ; eol character? JNZ SkipToEOL ; no, go eat another return EndProc SkipToEOL Break ; ; Free Transient. Modify ES,AX,flags ; Procedure Free_TPA,NEAR ASSUME DS:TRANGROUP,ES:RESGROUP PUSH ES MOV ES,[RESSEG] MOV ES,[RES_TPA] MOV AH,DEALLOC INT 21h ; Make lots of free memory POP ES return EndProc Free_TPA ; ; Allocate transient. Modify AX,BX,DX,flags ; Procedure Alloc_TPA,NEAR ASSUME DS:TRANGROUP,ES:RESGROUP PUSH ES MOV ES,[RESSEG] MOV BX,0FFFFH ; Re-allocate the transient MOV AH,ALLOC INT 21h PUSH BX ; Save size of block MOV AH,ALLOC INT 21h ; ; Attempt to align TPA on 64K boundary ; POP BX ; Restore size of block MOV [RES_TPA], AX ; Save segment to beginning of block MOV [TRAN_TPA], AX ; ; Is the segment already aligned on a 64K boundary ; MOV DX, AX ; Save segment AND AX, 0FFFH ; Test if above boundary JNZ Calc_TPA MOV AX, DX AND AX, 0F000H ; Test if multiple of 64K JNZ NOROUND Calc_TPA: MOV AX, DX AND AX, 0F000H ADD AX, 01000H ; Round up to next 64K boundary JC NOROUND ; Memory wrap if carry set ; ; Make sure that new boundary is within allocated range ; MOV DX, [RES_TPA] ADD DX, BX ; Compute maximum address CMP DX, AX ; Is 64K address out of range? JB NOROUND ; ; Make sure that we won't overwrite the transient ; MOV BX, CS ; CS is beginning of transient CMP BX, AX JB NOROUND ; ; The area from the 64K boundary to the beginning of the transient must ; be at least 64K. ; SUB BX, AX CMP BX, 4096 ; Size greater than 64K? JAE ROUNDDONE NOROUND: MOV AX, [RES_TPA] ROUNDDONE: MOV [LTPA],AX ; Re-compute everything MOV [TPA],AX MOV BX,AX MOV AX,CS SUB AX,BX PUSH BX MOV BX,16 MUL BX POP BX OR DX,DX JZ SAVSIZ2 MOV AX,-1 SAVSIZ2: ; ; AX is the number of bytes free in the buffer between the resident and the ; transient with a maximum of 64K-1. We round this down to a multiple of 512. ; CMP AX,512 JBE GotSize AND AX,0FE00h ; NOT 511 = NOT 1FF GotSize: MOV [BYTCNT],AX POP ES return EndProc Alloc_TPA Break ; ; The exec search has determined that the user has requested a batch file for ; execution. We parse the arguments, create the batch segment, and signal ; batch processing. ; Procedure BatCom,NEAR ASSUME DS:TRANGROUP, ES:NOTHING ; ; Batch parameters are read with ES set to segment of resident part ; MOV ES,[RESSEG] ASSUME ES:RESGROUP cmp es:[call_batch_flag],call_in_progress ;AN043; If in CALL, jz skip_ioset ;AN043; redirection was already set up invoke IOSET ; Set up any redirection skip_ioset: ;AN043; CALL FREE_TPA ; G cmp es:[call_batch_flag],call_in_progress ;G jz getecho ; G if we're in a call, don't execute ; ; Since BATCH has lower precedence than PIPE or FOR. If a new BATCH file is ; being started it MUST be true that no FOR or PIPE is currently in progress. ; Don't execute if in call ; invoke ForOff getecho: invoke PipeOff mov al,EchoFlag ; preserve echo state for chaining and al, 1 ; Save current echo state push ax xor ax,ax ;G test es:[batch],-1 ;G Are we in a batch file? jz leavebat ;G No, nothing to save mov ax,es:[batch] ;G get current batch segment cmp es:[call_batch_flag],call_in_progress ;G jz leavebat ;G ; ; We are in a chained batch file, save batlast from previous batch segment ; so that if we're in a CALL, we will return to the correct batch file. ; push es ;G mov es,ax ;G get current batch segment mov ax,es:[batlast] ;G get previous batch segment pop es ;G leavebat: ;G push ax ;G keep segment until new one created cmp es:[call_batch_flag],call_in_progress ;G are we in a CALL? jz startbat ;G Yes, keep current batch segment call BatchOff ;G No, deallocate old batch segment ; ; Find length of batch file ; startbat: ;G ASSUME ES:RESGROUP MOV es:[CALL_BATCH_FLAG], 0 ;G reset call flag mov SI, OFFSET TRANGROUP:EXECPATH mov ax,AppendTruename ;AN042; Get the real path where the batch file int 2fh ;AN042; was found with APPEND mov ah,Find_First ;AN042; The find_first will return it mov dx,si ;AN042; Get the string mov cx,search_attr ;AN042; filetypes to search for int 21h ;AN042; invoke DStrLen ; ; Allocate batch area: ; BYTE type of segment ; WORD segment of last batch file ; WORD segment for FOR command ; BYTE FOR flag state on entry to batch file ; DWORD offset for next line ; 10 WORD pointers to parameters. -1 is empty parameter ; ASCIZ file name (with . and ..) ; BYTES CR-terminated parameters ; BYTE 0 flag to indicate end of parameters ; ; We allocate the maximum size for the command line and use setblock to shrink ; later when we've squeezed out the extra ; MOV BX,CX ; length of file name. ADD BX,0Fh + (SIZE BatchSegment) + COMBUFLEN + 0Fh ; structure + max len + round up SaveReg MOV CL,4 SHR BX,CL ; convert to paragraphs PUSH BX ;G save size of batch segment MOV AH,ALLOC INT 21h ; Allocate batch segment POP BX ;G get size of batch segment ; ; This should *NEVER* return an error. The transient is MUCH bigger than ; the batch segment. This may not be true, however, in a multitasking system. ; G This error will occur with nesting of batch files. We also need to ; G make sure that we don't overlay the transient. ; jc mem_error ;G not enough memory - exit push ax ;G save batch segment add ax,bx ;G get end of batch segment add ax,20h ;G add some tpa work area mov bx,cs ;G get the transient segment ; ; M006; We cant check just for above. If the batchseg goes into a UMB, the ; M006; batchseg is always above the transient. We need to change this code ; M006; to only check for an overlap ; mov dx,offset TRANGROUP:TranSpaceEnd ; M006 add dx,15 ;round up para; M006 shr dx,cl ;para size of transient; M006 add dx,bx ;dx = top of transient; M006 cmp ax,bx ; M006 jb enough_mem ; Batchseg below transient ; enough memory ; M006 cmp ax,dx ; M006 ja enough_mem ; Batchseg above transient ; enough memory ; M006 ; ; M006; Batchseg overlaps transient -- insufficient memory ; pop ax ; restore ax; M006 ;M006; cmp ax,bx ;G do we end before the transient ;M006; pop ax ;G get batch segment back ;M006; jb enough_mem ;G we have enough memory - continue push es ;G no we're hitting the transient mov es,ax mov ax,DEALLOC SHL 8 ;G deallocate the batch segment int 21h pop es mem_error: jmp no_memory ;G Set up for message and exit enough_mem: pop ax ; restore ax; M006 MOV [BATCH],AX CALL ALLOC_TPA ; ; Initialize batch segment ; RestoreReg ; length of name POP AX ;G get saved batch segment back inc es:nest ;G increment # batch files in progress PUSH ES MOV ES,[BATCH] ASSUME ES:NOTHING MOV ES:[BatType],BatchType ; signal batch file type MOV ES:[batlast],ax ;G save segment of last batch file push DS ;G mov DS,[resseg] ;G set to resident data ASSUME DS:RESGROUP xor ax,ax ;G mov bl,forflag ;G get the current FOR state mov ES:[batforflag],bl ;G save it in the batch segment test bl,-1 ;G are we in a FOR? jz for_not_on ;G no, for segment set to 0 mov ax,forptr ;G yes, get current FOR segment mov forflag,0 ;G reset forflag for_not_on: mov ES:[batforptr],ax ;G save FOR segment in batch segment XOR AX,AX mov forptr,ax ;G make sure for segment is not active mov bl,echoflag ;G pop DS ;G mov byte ptr es:[Batechoflag],bl ;G save echo state of parent ;SR; ; Initialize the new BatchEOF flag we have added to 0 ; mov es:BatchEOF,0 MOV WORD PTR ES:[BatSeek],AX ; point to beginning of file MOV WORD PTR ES:[BatSeek+2],AX ; ; Initialize pointers ; DEC AX ; put -1 into AX MOV DI,BatParm ; point to parm area MOV BX,DI MOV CX,10 REP STOSW ; Init to no parms ; ; Move in batch file name ; MOV CX,DX rep movsb ; including NUL. ; ; Now copy the command line into batch segment, parsing the arguments along ; the way. Segment will look like this: ; ; CRCR...CRCR...CR 0 ; ; or, in the case of fewer arguments: ; ; CRCR...CR CR CR ... CR 0 ; MOV SI,OFFSET TRANGROUP:COMBUF+2 MOV CX,10 ; at most 10 arguments ; ; Look for beginning of next argument ; EACHPARM: invoke SCANOFF ; skip to argument ; ; AL is first non-delimiter. DS:SI points to char = AL ; CMP AL,0DH ; end of road? JZ HAVPARM ; yes, no more arguments ; ; If CX = 0 then we have stored the most parm we can. Skip store ; JCXZ MOVPARM ; Only first 10 parms get pointers ; ; Go into allocated piece and stick in new argument pointer. ; MOV ES:[BX],DI ; store batch pointer ADD BX,2 ; advance arg counter ; ; Move the parameter into batch segment ; MOVPARM: LODSB ; get byte INVOKE DELIM ; if delimiter JZ ENDPARM ; then done with parm STOSB ; store byte CMP AL,0DH ; if CR then not delimiter JZ HAVPARM ; but end of parm list, finish JMP SHORT MOVPARM ; ; We have copied a parameter up until the first separator. Terminate it with ; CR ; ENDPARM: MOV AL,0DH STOSB JCXZ EACHPARM ; if no parameters, don't dec DEC CX ; remember that we've seen one. JMP SHORT EACHPARM ; ; We have parsed the entire line. Terminate the arg list ; HAVPARM: XOR AL,AL STOSB ; Nul terminate the parms ; ; Now we know EXACTLY how big the BATCH segment is. Round up size (from DI) ; into paragraphs and setblock to the appropriate size ; LEA BX,[DI+15] MOV CL,4 SHR BX,CL MOV AH,SetBlock INT 21h POP ES ASSUME ES:RESGROUP PUSH ES POP DS ; Simply batch FCB setup ASSUME DS:RESGROUP CMP [SINGLECOM],-1 JNZ NOBATSING MOV [SINGLECOM],0FFF0H ; Flag single command BATCH job NOBATSING: ; ; Enter the batch file with the current echo state ; pop ax ; Get original echo state mov echoflag,al ;g restore it JMP TCOMMAND ; ; The following is executed if there isn't enough memory for batch segment ; NO_MEMORY: assume ds:trangroup,es:resgroup pop dx ;g even up our stack pop ax ;g pop ax ;g call Alloc_tpa ;g reallocate memory mov msg_disp_class,ext_msg_class ;AN000; set up extended error msg class mov dx,offset TranGroup:Extend_Buf_ptr ;AC000; get extended message pointer mov Extend_Buf_ptr,error_not_enough_memory ;AN000; get message number in control block jmp cerror ;g print error message and go... EndProc BatCom Procedure BatchOff ASSUME DS:NOTHING,ES:NOTHING SaveReg PUSH DS ;G PUSH BX ;G MOV ES,ResSeg MOV DS,ResSeg ;G ASSUME ES:ResGroup,DS:Resgroup ;G MOV AX,Batch ; Free the batch segment OR AX,AX JZ nofree PUSH ES MOV ES,AX test [echoflag],1 ;G Is echo on? jnz echo_last_line ;G Yes - echo last line in file mov suppress,no_echo ;G no - don't echo last line in file echo_last_line: MOV BL,ES:[BATECHOFLAG] ;G Get echo state mov [echoflag],bl ;G and restore it MOV BX,ES:[BATFORPTR] ;G Get FOR segment MOV FORPTR,BX ;G and restore it MOV BL,ES:[BATFORFLAG] ;G Get FOR flag MOV FORFLAG,BL ;G and restore it MOV BX,es:[batlast] ;G get old batch segment MOV AH,DEALLOC INT 21h POP ES MOV Next_BATCH,BX ;G reset batch segment DEC es:NEST ;G XOR AX,AX MOV Batch,AX ; No batch in progress NoFree: POP BX ;G pop ds ;G RestoreReg return EndProc BatchOff ; StrCpy - copy string, checking count in CX against COMBUFLEN ; Entry : DS:SI ==> source string ; ES:DI ==> destination string ; CX = current length of destination string ; Exit : string copied, CX updated, Carry set if length limit exceeded Procedure StrCpy,NEAR push ax ccycle: lodsb inc cx cmp cx,COMBUFLEN jb ccopy stc ; set carry to signal error jmp short ccend ccopy: stosb or al,al jnz ccycle ccend: dec cx ; discount extra byte dec di ; back up pointer pop ax return ; return carry clear EndProc StrCpy TRANCODE ENDS END