You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1097 lines
28 KiB
1097 lines
28 KiB
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 <PromptBat - Open or wait for batch file>
|
|
|
|
;
|
|
; 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 <GetKeystroke - get a keystroke and flush queue>
|
|
|
|
;
|
|
; 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 1 line from batch file>
|
|
|
|
;
|
|
; 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 %<something>. If the <something> is in the range 0-9, we
|
|
; retrieve the appropriate parameter from the batch segment. Otherwise we
|
|
; see if the <something> 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 %<number>. 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 <ES>
|
|
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 <ES>
|
|
;
|
|
; 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 <DS,DI>
|
|
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 <Allocate and deallocate the transient portion>
|
|
|
|
;
|
|
; 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 <BatCom - enter a batch file>
|
|
|
|
;
|
|
; 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 <CX>
|
|
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 <DX> ; 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:
|
|
;
|
|
; <arg0>CR<arg1>CR...<arg9>CR<arg10>CR...<ARGn>CR 0
|
|
;
|
|
; or, in the case of fewer arguments:
|
|
;
|
|
; <arg0>CR<arg1>CR...<arg6>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 <AX,ES>
|
|
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 <ES,AX>
|
|
|
|
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
|
|
|