962 lines
21 KiB
NASM
962 lines
21 KiB
NASM
page ,132
|
||
; SCCSID = @(#)tbatch2.asm 4.2 85/07/22
|
||
; SCCSID = @(#)tbatch2.asm 4.2 85/07/22
|
||
TITLE Batch processing routines part II
|
||
;/*
|
||
; * Microsoft Confidential
|
||
; * Copyright (C) Microsoft Corporation 1991
|
||
; * All Rights Reserved.
|
||
; */
|
||
|
||
;
|
||
; Revision History
|
||
; ================
|
||
;
|
||
; M020 SR 08/20/89 Changed GetBatByt to check if we
|
||
; already reached EOF before trying
|
||
; to read from batchfile. Also fixed
|
||
; old bug of ds not being setup on an
|
||
; error while reading the batchfile.
|
||
;
|
||
; M037 SR 11/1/90 Bug #1745 & #3438 fixed. On a GOTO, we
|
||
; reseek to the beginning of the
|
||
; batchfile. Clear the BatchEOF flag
|
||
; to indicate that we are no longer at
|
||
; EOF.
|
||
;
|
||
|
||
.xlist
|
||
.xcref
|
||
include comsw.asm
|
||
include dossym.inc
|
||
include syscall.inc
|
||
include comseg.asm
|
||
include comequ.asm
|
||
.list
|
||
.cref
|
||
|
||
|
||
DATARES SEGMENT PUBLIC BYTE ;AC000;
|
||
EXTRN BATCH:WORD
|
||
EXTRN Batch_Abort:byte
|
||
EXTRN call_batch_flag:byte
|
||
EXTRN call_flag:byte
|
||
EXTRN IFFlag:BYTE
|
||
EXTRN In_Batch:byte
|
||
EXTRN Nest:word
|
||
EXTRN PIPEFILES:BYTE
|
||
EXTRN RETCODE:WORD
|
||
EXTRN SINGLECOM:WORD
|
||
|
||
;;; extrn BatchEOF:byte
|
||
extrn EchoFlag:byte
|
||
extrn Next_Batch:word
|
||
DATARES ENDS
|
||
|
||
TRANDATA SEGMENT PUBLIC BYTE ;AC000;
|
||
EXTRN BADLAB_PTR:WORD
|
||
EXTRN BatBufLen:WORD
|
||
EXTRN IFTAB:BYTE
|
||
EXTRN SYNTMES_PTR:WORD
|
||
TRANDATA ENDS
|
||
|
||
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
|
||
EXTRN arg:byte ; the arg structure!
|
||
EXTRN BatBuf:BYTE
|
||
EXTRN BatBufEnd:WORD
|
||
EXTRN BatBufPos:WORD
|
||
EXTRN BATHAND:WORD
|
||
EXTRN COMBUF:BYTE
|
||
EXTRN DIRBUF:BYTE
|
||
EXTRN GOTOLEN:WORD
|
||
EXTRN if_not_count:word
|
||
EXTRN IFNOTFLAG:BYTE
|
||
EXTRN RESSEG:WORD
|
||
TRANSPACE ENDS
|
||
|
||
TRANCODE SEGMENT PUBLIC BYTE
|
||
|
||
ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
|
||
|
||
EXTRN cerror:near
|
||
EXTRN docom1:near
|
||
EXTRN tcommand:near
|
||
|
||
public $if,iferlev,goto,shift,ifexists,ifnot,forerror,$call
|
||
|
||
|
||
Break <GetBatByt - retrieve a byte from the batch file>
|
||
|
||
; Get one byte from the batch file and return it in AL. End-of-file returns
|
||
; <CR> and ends batch mode. DS must be set to resident segment.
|
||
; AH, DX destroyed.
|
||
|
||
Procedure GETBATBYT,NEAR
|
||
|
||
ASSUME DS:RESGROUP
|
||
|
||
SaveReg <BX,CX,DS>
|
||
test byte ptr [Batch_Abort],-1
|
||
jz @f
|
||
jmp BatEOF
|
||
@@:
|
||
TEST Batch,-1
|
||
JnZ @f
|
||
jmp BatEOF
|
||
@@:
|
||
PUSH ES
|
||
MOV ES,Batch
|
||
ASSUME ES:NOTHING
|
||
|
||
;M020;
|
||
;Check if we have already reached EOF (BatchEOF flag set. Then, we do not
|
||
;try to read from the batchfile again.
|
||
;
|
||
cmp es:BatchEOF,0 ;already reached EOF? ;M020
|
||
jz not_eof ;no, read batch file ;M020
|
||
jmp At_EOF ;yes, no more reads ;M020
|
||
not_eof: ;M020
|
||
|
||
ADD WORD PTR ES:[BatSeek],1
|
||
ADC WORD PTR ES:[BatSeek+2],0
|
||
POP ES
|
||
;
|
||
; See if we have bytes buffered...
|
||
;
|
||
MOV AX,CS
|
||
MOV DS,AX
|
||
ASSUME DS:TranGroup
|
||
MOV BX,BatBufPos
|
||
CMP BX,-1
|
||
JNZ UnBuf
|
||
;
|
||
; There are no bytes in the buffer. Let's try to fill it up.
|
||
;
|
||
MOV DX,OFFSET TranGROUP:BatBuf
|
||
MOV CX,BatBufLen ; max to read.
|
||
MOV BX,BatHand
|
||
MOV AH,READ
|
||
INT 21h ; Get one more byte from batch file
|
||
jnc bat_read_ok ;AN022; if no error - continue
|
||
invoke get_ext_error_number ;AN022; get the error
|
||
push ds ;AN022; save local segment
|
||
mov ds,[resseg] ;AN022; get resident segment
|
||
assume ds:resgroup ;AN022;
|
||
mov dx,ax ;AN022; put error in DX
|
||
invoke output_batch_name ;AN022; set up to print the error
|
||
pop ds ;AN022;
|
||
assume ds:trangroup ;AN022;
|
||
invoke std_eprintf ;AN022; print out the error
|
||
mov byte ptr combuf+2,end_of_line_in;AN022; terminate the batch line for parsing
|
||
mov byte ptr combuf+3,end_of_line_out ;AN022; terminate the batch line for output
|
||
;M020;
|
||
;Old bug! We jump to BatEof from here without ds=RESGROUP. Probably, this
|
||
;error is never hit (and it shouldn't be)
|
||
;
|
||
mov ds,ResSeg ; ds = RESGROUP ; M020
|
||
|
||
jmp short bateof ;AN022; terminate the batch file
|
||
|
||
bat_read_ok: ;AN022;
|
||
MOV CX,AX
|
||
JCXZ BATEOFDS
|
||
MOV BatBufEnd,CX
|
||
XOR BX,BX
|
||
MOV BatBufPos,BX
|
||
;
|
||
; Buffered bytes!
|
||
;
|
||
UnBuf:
|
||
MOV AL,BatBuf[BX] ; get next byte
|
||
INC BX
|
||
CMP BX,BatBufEnd ; beyond end of buffer?
|
||
JB SetBufPos
|
||
MOV BX,-1
|
||
|
||
SetBufPos:
|
||
MOV BatBufPos,BX
|
||
CMP AL,1AH ; ^Z for termination?
|
||
jnz GetByteDone
|
||
;
|
||
;We get here only when we hit an EOF
|
||
;
|
||
BatEOFDS:
|
||
;SR;
|
||
; HACK!!! A massive hack being put in here to get batch processing to work
|
||
;properly on EOF. Previously, a CR was returned and batch processing turned
|
||
;off the moment we hit an EOF. Unfortunately, if the last line had no CR-LF,
|
||
;batch processing is turned off before the last line is processed and so
|
||
;this line would never be executed.
|
||
; To fix this, a new flag BatchEOF has been introduced. This flag is
|
||
;set to 4 if there is no CR-LF before the EOF -- this is determined by looking
|
||
;at the buffer contents. If there is no LF ( we assume that presence of LF
|
||
;indicated a CR-LF combination), then we set BatchEOF to 4 and return a
|
||
;fake CR to the caller. This decrements BatchEOF. On the next call to this
|
||
;routine, BatchEOF is decremented to 2 and a fake lF is returned. On the
|
||
;third call, BatchEOF becomes zero and batch processing is turned off,
|
||
;now that the last line has been processed. If the EOF is the first char read into the buffer
|
||
;during this call, and there was a CR-LF previously, we are going to fake
|
||
;another redundant CR-LF. There is no work-around I can think of.
|
||
; I would love to restructure this entire routine and its caller to
|
||
;make the flow really easy to understand but I guess this will have to wait.
|
||
;
|
||
push es
|
||
mov es,ResSeg
|
||
;SR;
|
||
; If we had already set the BatchEOF flag on a previous call (BatchEOF == 2
|
||
;or BatchEOF == 1 now), then do not do the LF check.
|
||
;
|
||
mov es,es:Batch
|
||
cmp es:BatchEOF,0
|
||
jnz crpresent
|
||
|
||
inc es:BatchEOF ;match the dec following
|
||
mov bx,BatBufEnd
|
||
cmp BatBuf[bx-1],0ah ;was a LF present?
|
||
je crpresent ;yes, no need to fake it
|
||
|
||
add es:BatchEOF,3 ;BatchEOF == 4 to fake CR-LF
|
||
|
||
crpresent:
|
||
;;; pop es
|
||
|
||
ASSUME DS:TranGroup
|
||
MOV DS,ResSeg
|
||
ASSUME DS:ResGroup
|
||
|
||
;SR;
|
||
; The shift operation is done here to replace the decrement. This is because
|
||
;we can jump to this label directly from above when bogus calls are made to
|
||
;this routine even after batch processing is turned off. The shift ensures
|
||
;maintains the following invariance : 4 -> 2; 2 -> 1 ; 1 -> 0; 0 -> 0. Thus,
|
||
;it is used as a decrement and also as a NOP to just fall through on bogus
|
||
;calls.
|
||
; We turn batch processing off if BatchEOF == 1 or BatchEOF == 0.
|
||
;BatchEOF == 1 when we fall through from BatEOFDS and BatchEOF == 0 on a
|
||
;direct jump to BATEOF. If BatchEOF == 4, we return a fake CR-LF without
|
||
;turning batch processing off.
|
||
;
|
||
At_EOF: ;new label added ;M020
|
||
shr es:BatchEOF,1 ;decrement the flag
|
||
jz turn_off ;zero,turn batch off
|
||
cmp es:BatchEOF,1
|
||
jz ret_lf ;BatchEOF was 2, return LF
|
||
;
|
||
;BatchEOF == 4, indicates return fake CR now and fake LF next.
|
||
;
|
||
mov al,0dh ;return fake CR.
|
||
pop es
|
||
jmp short GetByteDone
|
||
ret_lf:
|
||
mov al,0ah ;return fake LF
|
||
pop es
|
||
jmp short GetByteDone
|
||
turn_off:
|
||
pop es
|
||
|
||
BATEOF:
|
||
invoke BatchOff ;turn batch processing off
|
||
CALL BATCLOSE
|
||
;;; mov BatchEOF,0 ;make sure BatchEOF = 0
|
||
|
||
;SR; BugBug
|
||
; There is a good reason why this carriage return is being returned here.
|
||
;This was part of the old code. Because,
|
||
;of the way the caller is structured, a fake CR has to be returned again on
|
||
;EOF to ensure the termination of the caller's loop. If echo is on, this
|
||
;results in an extra linefeed after the batchfile is run if the last line of
|
||
;the batchfile already had a CR-LF.
|
||
;NB: Do not confuse this with the faked CR. The fake CR-LF was to mark
|
||
;the end-of-line. This CR is to mark the end-of-file.
|
||
;
|
||
MOV AL,0dH ; If end-of-file, then end of line
|
||
|
||
test byte ptr [Batch_Abort],-1
|
||
mov byte ptr [Batch_Abort],0
|
||
jz Cont_Get_Byt
|
||
mov di,offset TRANGROUP:COMBUF+2 ; reset pointer to beginning of buffer
|
||
xor cx,cx ; zero line length
|
||
jmp short GetByteDone
|
||
|
||
Cont_Get_Byt:
|
||
CMP [SINGLECOM],0FFF0H ; See if we need to set SINGLECOM
|
||
JNZ GetByteDone
|
||
CMP NEST,0 ;G See if we have nested batch files
|
||
JNZ GETBYTEDONE ;G Yes - don't exit just yet
|
||
MOV [SINGLECOM],-1 ; Cause termination
|
||
|
||
GetByteDone:
|
||
RestoreReg <DS,CX,BX>
|
||
|
||
return
|
||
|
||
EndProc GetBatByt
|
||
|
||
break <$If - conditional execution>
|
||
assume ds:trangroup,es:trangroup
|
||
|
||
IFERRORP:
|
||
POP AX
|
||
IFERROR:
|
||
FORERROR:
|
||
MOV DX,OFFSET TRANGROUP:SYNTMES_ptr
|
||
JMP CERROR
|
||
|
||
$IF:
|
||
;
|
||
; Turn off any pipes in progress.
|
||
;
|
||
push ds ;AN004; save local DS
|
||
mov ds,[resseg] ;AN004; get resident segment
|
||
assume ds:resgroup ;AN004;
|
||
cmp [PIPEFILES],0 ;AN004; Only turn off if present.
|
||
jz IFNoPipe ;AN004; no pipe - continue
|
||
invoke PipeDel ;AN004; turn off piping
|
||
|
||
IFNoPipe: ;AN004;
|
||
pop ds ;AN004; get local DS back
|
||
assume ds:trangroup ;AN004;
|
||
MOV [IFNOTFLAG],0
|
||
mov [if_not_count], 0
|
||
MOV SI,81H
|
||
|
||
IFREENT:
|
||
invoke SCANOFF
|
||
CMP AL,0DH
|
||
JZ IFERROR
|
||
MOV BP,SI
|
||
MOV DI,OFFSET TRANGROUP:IFTAB ; Prepare to search if table
|
||
MOV CH,0
|
||
|
||
IFINDCOM:
|
||
MOV SI,BP
|
||
MOV CL,[DI]
|
||
INC DI
|
||
JCXZ IFSTRING
|
||
JMP SHORT FIRSTCOMP
|
||
|
||
IFCOMP:
|
||
JNZ IF_DIF ;AC000;
|
||
|
||
FIRSTCOMP:
|
||
LODSB
|
||
MOV AH,ES:[DI]
|
||
INC DI
|
||
CMP AL,AH
|
||
JZ IFLP
|
||
OR AH,20H ; Try lower case
|
||
CMP AL,AH
|
||
|
||
IFLP:
|
||
LOOP IFCOMP
|
||
|
||
IF_DIF: ;AC000;
|
||
LAHF
|
||
ADD DI,CX ; Bump to next position without affecting flags
|
||
MOV BX,[DI] ; Get handler address
|
||
INC DI
|
||
INC DI
|
||
SAHF
|
||
JNZ IFINDCOM
|
||
LODSB
|
||
CMP AL,0DH
|
||
|
||
IFERRORJ:
|
||
JZ IFERROR
|
||
invoke DELIM
|
||
JNZ IFINDCOM
|
||
invoke SCANOFF
|
||
JMP BX
|
||
|
||
IFNOT:
|
||
NOT [IFNOTFLAG]
|
||
inc [if_not_count]
|
||
JMP IFREENT
|
||
|
||
;
|
||
; We are comparing two strings for equality. First, find the end of the
|
||
; first string.
|
||
;
|
||
|
||
IFSTRING:
|
||
PUSH SI ; save away pointer for later compare
|
||
XOR CX,CX ; count of chars in first string
|
||
|
||
FIRST_STRING:
|
||
LODSB ; get character
|
||
CMP AL,0DH ; end of line?
|
||
JZ IFERRORP ; yes => error
|
||
invoke DELIM ; is it a delimiter?
|
||
JZ EQUAL_CHECK ; yes, go find equal sign
|
||
INC CX ; remember 1 byte for the length
|
||
JMP FIRST_STRING ; go back for more
|
||
;
|
||
; We have found the end of the first string. Unfortunately, we CANNOT use
|
||
; scanoff to find the next token; = is a valid separator and will be skipped
|
||
; over.
|
||
;
|
||
|
||
EQUAL_CHECK:
|
||
CMP AL,'=' ; is char we have an = sign?
|
||
JZ EQUAL_CHECK2 ; yes, go find second one.
|
||
CMP AL,0DH ; end of line?
|
||
JZ IFERRORPj ;AC004; yes, syntax error
|
||
LODSB ; get next char
|
||
JMP EQUAL_CHECK
|
||
;
|
||
; The first = has been found. The next char had better be an = too.
|
||
;
|
||
|
||
EQUAL_CHECK2:
|
||
LODSB ; get potential = char
|
||
CMP AL,'=' ; is it good?
|
||
jnz iferrorpj ; no, error
|
||
;
|
||
; Find beginning of second string.
|
||
;
|
||
invoke SCANOFF
|
||
CMP AL,0DH
|
||
jz iferrorpj
|
||
POP DI
|
||
;
|
||
; DS:SI points to second string
|
||
; CX has number of chars in first string
|
||
; ES:DI points to first string
|
||
;
|
||
; Perform compare to elicit match
|
||
;
|
||
REPE CMPSB
|
||
JZ MATCH ; match found!
|
||
;
|
||
; No match. Let's find out what was wrong. The character that did not match
|
||
; has been advanced over. Let's back up to it.
|
||
;
|
||
DEC SI
|
||
;
|
||
; If it is EOL, then syntax error
|
||
;
|
||
CMP BYTE PTR [SI],0DH
|
||
JZ IFERRORJ
|
||
;
|
||
; Advance pointer over remainder of unmatched text to next delimiter
|
||
;
|
||
|
||
SKIPSTRINGEND:
|
||
LODSB
|
||
|
||
NOTMATCH:
|
||
CMP AL,0DH
|
||
|
||
IFERRORJ2:
|
||
JZ IFERRORJ
|
||
invoke DELIM
|
||
JNZ SKIPSTRINGEND
|
||
;
|
||
; Signal that we did NOT have a match
|
||
;
|
||
MOV AL,-1
|
||
JMP SHORT IFRET
|
||
|
||
iferrorpj:
|
||
jmp iferrorp
|
||
;
|
||
; The compare succeeded. Was the second string longer than the first? We
|
||
; do this by seeing if the next char is a delimiter.
|
||
;
|
||
|
||
MATCH:
|
||
LODSB
|
||
invoke DELIM
|
||
JNZ NOTMATCH ; not same.
|
||
XOR AL,AL
|
||
JMP SHORT IFRET
|
||
|
||
IFEXISTS:
|
||
ifexist_attr EQU attr_hidden+attr_system
|
||
|
||
moredelim:
|
||
lodsb ; move command line pointer over
|
||
invoke delim ; pathname -- have to do it ourselves
|
||
jnz moredelim ; 'cause parse_file_descriptor is dumb
|
||
mov DX, OFFSET TRANGROUP:dirbuf
|
||
trap set_dma
|
||
mov BX, 2 ; if(0) [|not](|1) exist[1|2] file(2|3)
|
||
add BX, [if_not_count]
|
||
mov AX, OFFSET TRANGROUP:arg.argv
|
||
invoke argv_calc ; convert arg index to pointer
|
||
mov DX, [BX].argpointer ; get pointer to supposed filename
|
||
mov CX, ifexist_attr ; filetypes to search for
|
||
trap Find_First ; request first match, if any
|
||
jc if_ex_c ; carry is how to determine error
|
||
xor AL, AL
|
||
jmp short ifret
|
||
|
||
if_ex_c:
|
||
mov AL, -1 ; false 'n' fall through...
|
||
|
||
IFRET:
|
||
TEST [IFNOTFLAG],-1
|
||
JZ REALTEST
|
||
NOT AL
|
||
|
||
REALTEST:
|
||
OR AL,AL
|
||
JZ IFTRUE
|
||
JMP TCOMMAND
|
||
|
||
IFTRUE:
|
||
invoke SCANOFF
|
||
MOV CX,SI
|
||
SUB CX,81H
|
||
SUB DS:[80H],CL
|
||
MOV CL,DS:[80H]
|
||
MOV [COMBUF+1],CL
|
||
MOV DI,OFFSET TRANGROUP:COMBUF+2
|
||
CLD
|
||
REP MOVSB
|
||
MOV AL,0DH
|
||
STOSB
|
||
;
|
||
; Signal that an IF was done. This prevents the redirections from getting
|
||
; lost.
|
||
;
|
||
PUSH DS
|
||
MOV DS,ResSeg
|
||
ASSUME DS:RESGROUP
|
||
MOV IFFlag,-1
|
||
POP DS
|
||
ASSUME DS:TRANGROUP
|
||
;
|
||
; Go do the command
|
||
;
|
||
JMP DOCOM1
|
||
|
||
iferrorj3:
|
||
jmp iferrorj2
|
||
|
||
IFERLEV:
|
||
MOV BH,10
|
||
XOR BL,BL
|
||
|
||
GETNUMLP:
|
||
LODSB
|
||
CMP AL,0DH
|
||
jz iferrorj3
|
||
invoke DELIM
|
||
JZ GOTNUM
|
||
SUB AL,'0'
|
||
XCHG AL,BL
|
||
MUL BH
|
||
ADD AL,BL
|
||
XCHG AL,BL
|
||
JMP SHORT GETNUMLP
|
||
|
||
GOTNUM:
|
||
PUSH DS
|
||
MOV DS,[RESSEG]
|
||
ASSUME DS:RESGROUP
|
||
MOV AH,BYTE PTR [RETCODE]
|
||
POP DS
|
||
ASSUME DS:TRANGROUP
|
||
XOR AL,AL
|
||
CMP AH,BL
|
||
JAE IFRET
|
||
DEC AL
|
||
JMP SHORT IFRET
|
||
|
||
|
||
break <Shift - advance arguments>
|
||
|
||
;
|
||
; Shift the parameters in the batch structure by 1 and set up the new argument.
|
||
; This is a NOP if no batch in progress.
|
||
;
|
||
|
||
Procedure Shift,NEAR
|
||
|
||
assume ds:trangroup,es:trangroup
|
||
|
||
MOV DS,[RESSEG]
|
||
ASSUME DS:RESGROUP
|
||
MOV AX,[BATCH] ; get batch pointer
|
||
OR AX,AX ; in batch mode?
|
||
retz ; no, done.
|
||
MOV ES,AX ; operate in batch segment
|
||
MOV DS,AX
|
||
|
||
ASSUME DS:NOTHING,ES:NOTHING
|
||
|
||
;
|
||
; Now move the batch args down by 1 word
|
||
;
|
||
MOV DI,BatParm ; point to parm table
|
||
LEA SI,[DI+2] ; make source = dest + 2
|
||
MOV CX,9 ; move 9 parameters
|
||
REP MOVSW ; SHIFT down
|
||
;
|
||
; If the last parameter (the one not moved) is empty (= -1) then we are done.
|
||
; We have copied it into the previous position
|
||
;
|
||
CMP WORD PTR [DI],-1 ; if last one was not in use then
|
||
retz ; No new parm
|
||
;
|
||
; This last pointer is NOT nul. Get it and scan to find the next argument.
|
||
; Assume, first, that there is no next argument
|
||
;
|
||
MOV SI,[DI]
|
||
MOV WORD PTR [DI],-1 ; Assume no parm
|
||
;
|
||
; The parameters are CR separated. Scan for end of this parm
|
||
;
|
||
SKIPCRLP:
|
||
LODSB
|
||
CMP AL,0DH
|
||
JNZ SKIPCRLP
|
||
;
|
||
; We are now pointing at next arg. If it is 0 (end of original line) then we
|
||
; are finished. There ARE no more parms and the pointer has been previously
|
||
; initialized to indicate it.
|
||
;
|
||
CMP BYTE PTR [SI],0
|
||
retz ; End of parms
|
||
MOV [DI],SI ; Pointer to next parm as %9
|
||
|
||
return
|
||
|
||
EndProc Shift
|
||
|
||
;
|
||
; Skip delim reads bytes from the batch file until a non-delimiter is seen.
|
||
; returns char in AL, carry set -> eof
|
||
;
|
||
|
||
Procedure SkipDelim,NEAR
|
||
|
||
ASSUME DS:ResGroup,ES:NOTHING
|
||
TEST Batch,-1
|
||
JZ SkipErr ; batch file empty. OOPS!
|
||
CALL GetBatByt ; get a char
|
||
invoke Delim ; check for ignoreable chars
|
||
JZ SkipDelim ; ignore this char.
|
||
clc
|
||
return
|
||
|
||
SkipErr:
|
||
stc
|
||
return
|
||
|
||
EndProc SkipDelim
|
||
|
||
break $Call
|
||
|
||
; CALL is an internal command that transfers control to a .bat, .exe, or
|
||
; .com file. This routine strips the CALL off the command line, sets
|
||
; the CALL_FLAG to indicate a call in progress, and returns control to
|
||
; DOCOM1 in TCODE to reprocess the command line and execute the file
|
||
; being CALLed.
|
||
|
||
$CALL:
|
||
|
||
; strip off CALL from command line
|
||
|
||
ASSUME DS:trangroup,ES:trangroup
|
||
push si
|
||
push di
|
||
push ax
|
||
push cx
|
||
mov si,offset trangroup:combuf+2
|
||
invoke scanoff ;get to first non-delimeter
|
||
add si,length_call ;point to char past CALL
|
||
mov di,offset trangroup:combuf+2
|
||
mov cx,combuflen-length_call ;get length of buffer
|
||
rep movsb ;move it
|
||
pop cx
|
||
pop ax
|
||
pop di
|
||
pop si
|
||
|
||
|
||
; set call flag to indicate call in progress
|
||
|
||
push ds
|
||
mov ds,[resseg]
|
||
ASSUME DS:resgroup,ES:resgroup
|
||
mov call_flag, call_in_progress
|
||
mov call_batch_flag, call_in_progress
|
||
;
|
||
; Turn off any pipes in progress.
|
||
;
|
||
cmp [PIPEFILES],0 ; Only turn off if present.
|
||
jz NoPipe
|
||
invoke PipeDel
|
||
NoPipe:
|
||
pop ds
|
||
|
||
ret
|
||
|
||
break Goto
|
||
|
||
GOTO:
|
||
|
||
assume ds:trangroup,es:trangroup
|
||
MOV DS,[RESSEG]
|
||
assume ds:resgroup
|
||
TEST [BATCH],-1
|
||
retz ; If not in batch mode, a nop
|
||
XOR DX,DX
|
||
PUSH DS
|
||
MOV DS,Batch
|
||
MOV WORD PTR DS:[BatSeek],DX ; Back to start
|
||
MOV WORD PTR DS:[BatSeek+2],DX ; Back to start
|
||
;M037
|
||
; Clear EOF indicator because we have reseeked to the beginning of the file.
|
||
;
|
||
mov ds:BatchEOF,0 ; clear eof indicator ;M037
|
||
|
||
POP DS
|
||
|
||
GotoOpen:
|
||
invoke promptBat
|
||
MOV DI,FCB+1 ; Get the label
|
||
MOV CX,11
|
||
MOV AL,' '
|
||
REPNE SCASB
|
||
JNZ NOINC
|
||
INC CX
|
||
|
||
NOINC:
|
||
SUB CX,11
|
||
NEG CX
|
||
MOV [GOTOLEN],CX
|
||
;
|
||
; At beginning of file. Skip to first non-delimiter char
|
||
;
|
||
CALL SkipDelim
|
||
JC BadGoto
|
||
CMP AL,':'
|
||
JZ CHKLABEL
|
||
|
||
LABLKLP: ; Look for the label
|
||
CALL GETBATBYT
|
||
CMP AL,0AH
|
||
JNZ LABLKTST
|
||
;
|
||
; At beginning of line. Skip to first non-delimiter char
|
||
;
|
||
CALL SkipDelim
|
||
JC BadGoto
|
||
CMP AL,':'
|
||
JZ CHKLABEL
|
||
|
||
LABLKTST:
|
||
TEST [BATCH],-1
|
||
JNZ LABLKLP
|
||
|
||
BadGoto:
|
||
CALL BATCLOSE
|
||
;SR;
|
||
; At this point we are terminating without freeing up any nested batch
|
||
;segments i.e if the error occurred within a called batch file. This routine
|
||
;will traverse the linked list of batch segments and free all of them.
|
||
;
|
||
call free_batch ;free up nested batch segments
|
||
|
||
PUSH CS
|
||
POP DS
|
||
MOV DX,OFFSET TRANGROUP:BADLAB_ptr
|
||
JMP CERROR
|
||
|
||
;
|
||
; Found the :. Skip to first non-delimiter char
|
||
;
|
||
|
||
CHKLABEL:
|
||
CALL SkipDelim
|
||
JC BadGoto
|
||
MOV DI,FCB+1
|
||
MOV CX,[GOTOLEN]
|
||
JMP SHORT GotByte
|
||
|
||
NEXTCHRLP:
|
||
PUSH CX
|
||
CALL GETBATBYT
|
||
POP CX
|
||
|
||
GotByte:
|
||
INVOKE TESTKANJ ;AN000; 3/3/KK
|
||
JZ NOTKANJ1 ;AN000; 3/3/KK
|
||
CMP AL, ES:[DI] ;AN000; 3/3/KK
|
||
JNZ LABLKTST ;AN000; 3/3/KK
|
||
INC DI ;AN000; 3/3/KK
|
||
DEC CX ;AN000; 3/3/KK
|
||
JCXZ LABLKTST ;AN000; 3/3/KK
|
||
PUSH CX ;AN000; 3/3/KK
|
||
CALL GETBATBYT ;AN000; 3/3/KK
|
||
POP CX ;AN000; 3/3/KK
|
||
CMP AL, ES:[DI] ;AN000; 3/3/KK
|
||
JMP SHORT KNEXTLABCHR ;AN000; 3/3/KK
|
||
|
||
NOTKANJ1: ;AN000; 3/3/KK
|
||
OR AL,20H
|
||
CMP AL,ES:[DI]
|
||
JNZ TRYUPPER
|
||
JMP SHORT NEXTLABCHR
|
||
|
||
TRYUPPER:
|
||
SUB AL,20H
|
||
CMP AL,ES:[DI]
|
||
|
||
KNEXTLABCHR: ;AN000; 3/3/KK
|
||
JNZ LABLKTST
|
||
|
||
NEXTLABCHR:
|
||
INC DI
|
||
LOOP NEXTCHRLP
|
||
CALL GETBATBYT
|
||
cmp [GOTOLEN],8 ; Is the label atleast 8 chars long?
|
||
jge gotocont ; Yes, then the next char doesn't matter
|
||
CMP AL,' '
|
||
JA LABLKTST
|
||
|
||
gotocont:
|
||
CMP AL,0DH
|
||
JZ SKIPLFEED
|
||
|
||
TONEXTBATLIN:
|
||
CALL GETBATBYT
|
||
CMP AL,0DH
|
||
JNZ TONEXTBATLIN
|
||
|
||
SKIPLFEED:
|
||
CALL GETBATBYT
|
||
|
||
;SR;
|
||
; The BatchEOF flag is set in GetBatByt to indicate that we are faking a
|
||
;CR-LF for the last line. On a goto, this flag has to be cleared, because
|
||
;BatchEOF == 1 now, after returning a CR-LF. The next call to GetBatByt
|
||
;to get the EOF has not been made yet because we encountered the Goto. On
|
||
;all other cases, EOF will be hit while trying to read the next line and
|
||
;we are fine.
|
||
;
|
||
push es
|
||
mov es,Batch
|
||
mov es:BatchEOF,0 ;invalidate fake CR-LF flag
|
||
pop es
|
||
CALL BatClose
|
||
|
||
return
|
||
|
||
Procedure BatClose,NEAR
|
||
assume ds:resgroup
|
||
|
||
MOV BX,CS:[BATHAND]
|
||
CMP BX,5
|
||
JB CloseReturn
|
||
MOV AH,CLOSE
|
||
INT 21h
|
||
|
||
CloseReturn:
|
||
mov byte ptr [In_Batch],0 ; reset flag
|
||
return
|
||
|
||
EndProc BatClose
|
||
|
||
;
|
||
; Open the BATCH file, If open fails, AL is drive of batch file (A=1)
|
||
; Also, fills internal batch buffer. If access denied, then AX = -1
|
||
;
|
||
|
||
Procedure BatOpen,NEAR
|
||
|
||
ASSUME DS:RESGROUP,ES:TRANGROUP
|
||
|
||
PUSH DS
|
||
MOV DS,[BATCH]
|
||
ASSUME DS:NOTHING
|
||
|
||
MOV DX,BatFile
|
||
MOV AX,OPEN SHL 8
|
||
INT 21h ; Open the batch file
|
||
JC SETERRDL
|
||
MOV DX,WORD PTR DS:[BatSeek]
|
||
MOV CX,WORD PTR DS:[BatSeek+2]
|
||
POP DS
|
||
ASSUME DS:RESGROUP
|
||
|
||
MOV [BATHAND],AX
|
||
MOV BX,AX
|
||
MOV AX,LSEEK SHL 8 ; Go to the right spot
|
||
INT 21h
|
||
MOV BatBufPos,-1 ; nuke batch buffer position
|
||
|
||
return
|
||
|
||
SETERRDL:
|
||
MOV BX,DX
|
||
invoke get_ext_error_number ;AN022; get the extended error
|
||
mov dx,ax ;AN022; save extended error in DX
|
||
MOV AL,[BX] ; Get drive spec
|
||
SUB AL,'@' ; A = 1
|
||
POP DS
|
||
STC ; SUB mucked over carry
|
||
|
||
return
|
||
|
||
EndProc BatOpen
|
||
|
||
|
||
;
|
||
;Free_batch : This routine traverses the linked batch segments freeing all
|
||
;the batch and FOR segments until all of them are freed. It also restores
|
||
;the old state of the EchoFlag.
|
||
;
|
||
; ENTRY: ds = RESGROUP
|
||
;
|
||
; EXIT: All batch & FOR segments freed.
|
||
; EchoFlag restored to old state before batch process.
|
||
;
|
||
; REGISTERS AFFECTED: bx, cx
|
||
|
||
|
||
free_batch proc near
|
||
assume ds:RESGROUP,es:nothing
|
||
|
||
push es
|
||
mov bx,Next_Batch
|
||
or bx,bx
|
||
jz fb_ret
|
||
|
||
ClearBatch:
|
||
mov es,bx ; get batch segment
|
||
|
||
mov bx,es:BatForPtr ; get old FOR segment
|
||
cmp bx,0 ; is a FOR in progress
|
||
je no_bat_for ; no - don't deallocate
|
||
push es ;
|
||
mov es,bx ; yes - free it up...
|
||
mov ah,DEALLOC ;
|
||
int 21h ;
|
||
pop es ; restore to batch segment
|
||
|
||
No_Bat_For:
|
||
mov cl,es:BatEchoFlag ; get old echo flag
|
||
mov bx,es:BatLast ; get old batch segment
|
||
mov ah,DEALLOC ; free it up...
|
||
int 21h
|
||
mov Batch,bx ; get ready to deallocate next batch
|
||
dec nest ; is there another batch file?
|
||
jnz ClearBatch ; keep going until no batch file
|
||
|
||
mov EchoFlag,cl ;restore echo status
|
||
mov Batch,0 ;no batch process in progress
|
||
|
||
fb_ret:
|
||
pop es
|
||
ret
|
||
|
||
free_batch endp
|
||
|
||
|
||
TRANCODE ENDS
|
||
END
|
||
|