2 ; SCCSID = @(#)tbatch.asm 4.5 85/10/01
3 ; SCCSID = @(#)tbatch.asm 4.5 85/10/01
4 TITLE Batch processing routines
13 include doscntry
.inc ;AN000;
19 DATARES
SEGMENT PUBLIC BYTE ;AC000;
21 EXTRN Batch_Abort
:byte
22 EXTRN call_batch_flag
:byte
35 EXTRN SUPPRESS
:BYTE ;AC000;
38 TRANDATA
SEGMENT PUBLIC BYTE ;AC000;
40 EXTRN Extend_buf_ptr
:word ;AC000;
41 EXTRN Extend_buf_sub
:byte ;AN022;
42 EXTRN msg_disp_class
:byte ;AC000;
43 EXTRN NEEDBAT_PTR
:WORD
44 EXTRN pausemes_ptr
:word ;AC000;
47 TRANSPACE
SEGMENT PUBLIC BYTE ;AC000;
50 EXTRN bwdbuf
:byte ;AN022;
57 EXTRN string_ptr_2
:word ;AC000;
62 TRANCODE
SEGMENT PUBLIC BYTE
64 ASSUME
CS:TRANGROUP
,DS:NOTHING
,ES:NOTHING
,SS:NOTHING
71 TRANSPACE
SEGMENT PUBLIC BYTE ;AC000;
72 extrn arg
:byte ; the arg structure!
76 Break <PromptBat
- Open
or wait for batch
file>
79 ; Open the batch file. If we cannot find the batch file. If the media is
80 ; changeable, we prompt for the change. Otherwise, we terminate the batch
81 ; file. Leave segment registers alone.
84 Procedure PromptBat
,NEAR
85 ASSUME
DS:ResGroup
,ES:NOTHING
86 invoke BATOPEN
; attempt to open batch file
88 cmp dx,error_file_not_found
;AN022; Ask for diskette if file not found
89 jz Bat_Remcheck
;AN022;
90 cmp dx,error_path_not_found
;AN022; Ask for diskette if path not found
91 jz Bat_Remcheck
;AN022; Otherwise, issue message and exit
92 invoke output_batch_name
;AN022; set up batch name in bwdbuf
93 jmp short BatDie
;AN022;
95 Bat_Remcheck: ;AN022; Go see if media is removable
96 CALL [RCH_ADDR
] ; DX has error number
97 JZ AskForBat
; Media is removable
99 ; The media is not changeable. Turn everything off.
103 MOV IfFlag
,AL ; No If in progress.
104 MOV DX,OFFSET TRANGROUP
:BADBAT_ptr
111 invoke std_eprintf
;AC022; display message
114 ; TCOMMAND resets the stack. This is the equivalent of a non-local goto.
116 JMP TCOMMAND
; he cleans off stack
119 ; Ask the user to reinsert the batch file
127 MOV DX,OFFSET TRANGROUP
:NEEDBAT_ptr
;AN022;
128 invoke std_eprintf
;Prompt for batch file on stderr
129 mov dx,offset trangroup
:pausemes_ptr
;AN000; get second part of message
130 invoke std_eprintf
;AN000; print it to stderr
137 ;****************************************************************
139 ;* ROUTINE: Output_batch_name
141 ;* FUNCTION: Sets up batch name to be printed on extended error
143 ;* INPUT: DX - extended error number
145 ;* OUTPUT: Ready to call print routine
147 ;****************************************************************
149 public output_batch_name
;AN022;
151 Output_batch_name proc
near ;AN022;
153 push ds ;AN022; save resident segment
154 mov ds,[batch
] ;AN022; get batch file segment
155 assume
DS:nothing
;AN022;
156 mov SI,BatFile
;AN022; get offset of batch file
157 invoke dstrlen
;AN022; get length of string
158 mov di,offset Trangroup
:bwdbuf
;AN022; target for batch name
159 rep movsb ;AN022; move the name
161 push cs ;AN022; get local segment
163 assume
DS:trangroup
;AN022;
164 mov extend_buf_ptr
,dx ;AN022; put message number in block
165 mov msg_disp_class
,ext_msg_class
;AN022; set up extended error msg class
166 mov dx,offset TranGroup
:Extend_Buf_ptr
;AN022; get extended message pointer
167 mov string_ptr_2
,offset trangroup
:bwdbuf
;AN022; point to substitution
168 mov extend_buf_sub
,one_subst
;AN022; set up for one subst
169 pop ds ;AN022; restore data segment
173 Output_batch_name endp
;AN022;
175 Break <GetKeystroke
- get a keystroke
and flush queue
>
178 ; read the next keystroke. Since there may be several characters in the queue
179 ; after the one we ask for (function keys/Kanji), we need to flush the queue
182 Procedure GetKeyStroke
,NEAR
184 ; read any character at any mode, interim mode or not.
187 PUSH DX ;AN000; 3/3/KK
188 MOV AX,(ECS_call
SHL 8) OR GetInterimMode
;AN000; 3/3/KK
189 INT int_command
;AN000; 3/3/KK
190 PUSH DX ;AN000; save interim state 3/3/KK
191 MOV AX,(ECS_call
SHL 8) OR SetInterimMode
;AN000; 3/3/KK
192 MOV DL,InterimMode
;AN000; 3/3/KK
193 INT int_command
;AN000; 3/3/KK
195 MOV AX,(STD_CON_INPUT_FLUSH
SHL 8) OR STD_CON_INPUT_no_echo
196 INT int_command
; Get character with KB buffer flush
197 MOV AX,(STD_CON_INPUT_FLUSH
SHL 8) + 0
200 MOV AX,(ECS_call
SHL 8) OR SetInterimMode
;AN000; 3/3/KK
201 POP DX ;AN000; restore interim state 3/3/KK
202 INT int_command
;AN000; 3/3/KK
203 POP DX ;AN000; 3/3/KK
208 Break <ReadBat
- read
1 line
from batch
file>
211 ; ReadBat - read a single line from the batch file. Perform all substitutions
215 Procedure ReadBat
,NEAR
216 ASSUME
DS:ResGroup
,ES:TranGroup
217 mov suppress
,yes_echo
;g initialize line suppress status
218 test byte ptr [Batch_Abort
],-1
220 mov byte ptr [In_Batch
],1 ; set flag to indicate batch job
224 MOV DI,OFFSET TRANGROUP
:COMBUF
+2
227 ; Save position and try to scan for first non delimiter.
234 PUSH WORD PTR DS:[BatSeek
]
235 PUSH WORD PTR DS:[BatSeek
+2] ; save current location.
238 invoke SkipDelim
; skip to first non-delim
240 ; If the first non-delimiter is not a : (label), we reseek back to the
241 ; beginning and read the line.
243 CMP AL,':' ; is it a label?
245 POP DX ; restore position in bat file
246 JZ NopLine
; yes, resync everything.
247 TEST [BATCH
],-1 ; are we done with the batch file?
250 CMP AL, NO_ECHO_CHAR
;g see if user wants to suppress line
251 JNZ SET_BAT_POS
;g no - go and set batch file position
252 MOV SUPPRESS
, NO_ECHO
;g yes set flag to indicate
253 jmp Rdbat
;g go read batch file
259 MOV WORD PTR DS:[BatSeek
],DX ; reseek back to beginning
260 MOV WORD PTR DS:[BatSeek
+2],CX
263 MOV AX,(LSEEK
SHL 8) + 0 ; seek back
265 MOV BatBufPos
,-1 ; nuke batch buffer position
266 xor cx,cx ; Initialize line length to zero
269 ; The first non-delimiter is a :. This line is not echoed and is ignored.
270 ; We eat characters until a CR is seen.
275 invoke GetBatByt
; eat trailing LF
276 TEST [BATCH
],-1 ; are we done with the batch file?
277 JNZ TESTNOP
; no, go get another line
281 ; Read a line into the buffer pointed to by ES:DI. If any %s are seen in the
282 ; input, we are to consider two special cases:
284 ; %0 to %9 These represent replaceable parameters from the batch segment
285 ; %sym% This is a symbol from the environment
290 inc cx ; Inc the line length
291 cmp cx,COMBUFLEN
; Is it too long?
292 jae TooLong
; Yes - handle it, handle it
294 ; See if we have a parameter character.
296 CMP AL,'%' ; Check for parameter
299 ; no parameter character. Store it as usual and see if we are done.
304 CMP AL,0DH ; End of line found?
305 JNZ RDBAT
; no, go for more
307 ; We have read in an entire line. Decide whether we should echo the command
312 SUB DI,OFFSET TRANGROUP
:COMBUF
+3
313 MOV AX,DI ; remember that we've not counted the CR
314 MOV ES:[COMBUF
+1],AL ; Set length of line
315 invoke GetBatByt
; Eat linefeed
317 CMP SUPPRESS
, NO_ECHO
;G
319 test [echoflag
],1 ; To echo or not to echo, that is the
323 PUSH CS ; question. (Profound, huh?)
324 POP DS ; Go back to local segment
325 retz
; no echoing here...
327 ; Echo the command line with appropriate CRLF...
332 cmp nullflag
,nullcommand
;G was there a command last time?
333 jz No_crlf_print
;G no - don't print crlf
334 invoke CRLF2
;G Print out prompt
337 invoke PRINT_PROMPT
;G
338 PUSH CS ;G change data segment
342 mov dx,OFFSET TRANGROUP
:COMBUF
+2 ; get command line for echoing
347 ; The line was too long. Eat remainder of input text up until the CR
351 cmp al,0dh ; Has the end of the line been reached?
352 jz Ltlcont
; Yes, continue
353 CALL SkipToEOL
; Eat remainder of line
356 stosb ; Terminate the command
357 jmp Found_EOL
; Go process the valid part of the line
359 ; We have found a parameter lead-in character. Check for the 0-9 case first
363 invoke GetBatByt
; get next character
364 CMP AL,'%' ; Check for two consecutive %
365 JZ SAVBATBYT
; if so, replace with a single %
366 CMP AL,0Dh ; Check for end-of-line
367 JZ SAVBATBYT
; yes, treat it normally
369 ; We have found %<something>. If the <something> is in the range 0-9, we
370 ; retrieve the appropriate parameter from the batch segment. Otherwise we
371 ; see if the <something> has a terminating % and then look up the contents
376 JB NEEDENV
; look for parameter in the environment
380 ; We have found %<number>. This is taken from the parameters in the
381 ; allocated batch area.
384 MOV BX,AX ; move index into AX
385 SHL BX,1 ; convert word index into byte ptr
389 ; The structure of the batch area is:
391 ; BYTE type of segment
392 ; DWORD offset for next line
393 ; 10 WORD pointers to parameters. -1 is empty parameter
394 ; ASCIZ file name (with . and ..)
395 ; BYTES CR-terminated parameters
396 ; BYTE 0 flag to indicate end of parameters
398 ; Get pointer to BX'th argument
400 MOV SI,ES:BatParm
[BX]
403 ; Is there a parameter here?
405 CMP SI,-1 ; Check if parameter exists
406 JNZ Yes_there_is
;G Yes go get it
407 JMP RDBAT
; Ignore if it doesn't
409 ; Copy in the found parameter from batch segment
416 dec cx ; Don't count '%' in line length
419 LODSB ; From resident segment
420 CMP AL,0DH ; Check for end of parameter
422 inc cx ; Inc the line length
423 cmp cx,COMBUFLEN
; Is it too long?
424 jae LineTooL
; Yes - handle it, handle it
428 ; We have copied up to the limit. Stop copying and eat remainder of batch
429 ; line. We need to make sure that the tooLong code isn't fooled into
430 ; believing that we are at EOL. Clobber AL too.
439 ; We have copied in an entire parameter. Go back for more
446 ; We have found % followed by something other than 0-9. We presume that there
447 ; will be a following % character. In between is an environment variable that
448 ; we will fetch and replace in the batch line with its value.
453 MOV DI,OFFSET TRANGROUP
:ID
; temp spot for name
454 ADD AL,'0' ; reconvert character
455 STOSB ; store it in appropriate place
457 ; loop getting characters until the next % is found or until EOL
461 invoke GetBatByt
; get the byte
464 JNZ GETENV15
; no, see if it the term char
466 ; The user entered a string with a % but no trailing %. We copy the string.
468 mov byte ptr es:[di-1],0 ; nul terminate the string
469 mov si,offset TranGroup
:ID
; point to buffer
470 pop di ; point to line buffer
484 CMP AL,'%' ; terminating %?
485 JNZ GETENV1
; no, go suck out more characters
486 mov al,'=' ; terminate with =
489 ; ID now either has a =-terminated string which we are to find in the
490 ; environment or a non =-terminated string which will not be found in the
494 MOV SI,OFFSET TRANGROUP
:ID
496 POP DS ; DS:SI POINTS TO NAME
499 INVOKE FIND_NAME_IN_environment
509 POP DI ; get back pointer to command line
511 ; If the parameter was not found, there is no need to perform any replacement.
512 ; We merely pretend that we've copied the parameter.
522 ; ES:DI points to command line being built
523 ; DS:SI points either to nul-terminated environment object AFTER =
534 POP DS ; restore pointer to resgroup
539 JMP RDBAT
; no, go back to batch file
544 ; SkipToEOL - read from batch file until end of line
547 Procedure SkipToEOL
,NEAR
549 ASSUME
DS:ResGroup
,ES:NOTHING
552 retz
; no batch file in effect
554 CMP AL,0Dh ; eol character?
555 JNZ SkipToEOL
; no, go eat another
560 Break <Allocate
and deallocate the transient portion
>
563 ; Free Transient. Modify ES,AX,flags
566 Procedure Free_TPA
,NEAR
568 ASSUME
DS:TRANGROUP
,ES:RESGROUP
574 INT int_command
; Make lots of free memory
582 ; Allocate transient. Modify AX,BX,DX,flags
585 Procedure Alloc_TPA
,NEAR
587 ASSUME
DS:TRANGROUP
,ES:RESGROUP
591 MOV BX,0FFFFH ; Re-allocate the transient
594 PUSH BX ; Save size of block
598 ; Attempt to align TPA on 64K boundary
600 POP BX ; Restore size of block
601 MOV [RES_TPA
], AX ; Save segment to beginning of block
604 ; Is the segment already aligned on a 64K boundary
606 MOV DX, AX ; Save segment
607 AND AX, 0FFFH ; Test if above boundary
610 AND AX, 0F000H ; Test if multiple of 64K
616 ADD AX, 01000H ; Round up to next 64K boundary
617 JC NOROUND
; Memory wrap if carry set
619 ; Make sure that new boundary is within allocated range
622 ADD DX, BX ; Compute maximum address
623 CMP DX, AX ; Is 64K address out of range?
626 ; Make sure that we won't overwrite the transient
628 MOV BX, CS ; CS is beginning of transient
632 ; The area from the 64K boundary to the beginning of the transient must
636 CMP BX, 4096 ; Size greater than 64K?
643 MOV [LTPA
],AX ; Re-compute everything
658 ; AX is the number of bytes free in the buffer between the resident and the
659 ; transient with a maximum of 64K-1. We round this down to a multiple of 512.
663 AND AX,0FE00h ; NOT 511 = NOT 1FF
673 Break <BatCom
- enter a batch
file>
676 ; The exec search has determined that the user has requested a batch file for
677 ; execution. We parse the arguments, create the batch segment, and signal
680 Procedure BatCom
,NEAR
682 ASSUME
DS:TRANGROUP
, ES:NOTHING
685 ; Batch parameters are read with ES set to segment of resident part
690 cmp es:[call_batch_flag
],call_in_progress
;AN043; If in CALL,
691 jz skip_ioset
;AN043; redirection was already set up
692 invoke IOSET
; Set up any redirection
696 cmp es:[call_batch_flag
],call_in_progress
;G
697 jz getecho
; G if we're in a call, don't execute
699 ; Since BATCH has lower precedence than PIPE or FOR. If a new BATCH file is
700 ; being started it MUST be true that no FOR or PIPE is currently in progress.
701 ; Don't execute if in call
707 mov al,EchoFlag
; preserve echo state for chaining
709 and al, 1 ; Save current echo state
713 test es:[batch
],-1 ;G Are we in a batch file?
714 jz leavebat
;G No, nothing to save
715 mov ax,es:[batch
] ;G get current batch segment
716 cmp es:[call_batch_flag
],call_in_progress
;G
719 ; We are in a chained batch file, save batlast from previous batch segment
720 ; so that if we're in a CALL, we will return to the correct batch file.
723 mov es,ax ;G get current batch segment
724 mov ax,es:[batlast
] ;G get previous batch segment
728 push ax ;G keep segment until new one created
729 cmp es:[call_batch_flag
],call_in_progress
;G are we in a CALL?
730 jz startbat
;G Yes, keep current batch segment
731 call BatchOff
;G No, deallocate old batch segment
734 ; Find length of batch file
739 MOV es:[CALL_BATCH_FLAG
], 0 ;G reset call flag
740 mov SI, OFFSET TRANGROUP
:EXECPATH
742 mov ax,AppendTruename
;AN042; Get the real path where the batch file
743 int 2fh
;AN042; was found with APPEND
744 mov ah,Find_First
;AN042; The find_first will return it
745 mov dx,si ;AN042; Get the string
746 mov cx,search_attr
;AN042; filetypes to search for
747 int int_command
;AN042;
751 ; Allocate batch area:
752 ; BYTE type of segment
753 ; WORD segment of last batch file
754 ; WORD segment for FOR command
755 ; BYTE FOR flag state on entry to batch file
756 ; DWORD offset for next line
757 ; 10 WORD pointers to parameters. -1 is empty parameter
758 ; ASCIZ file name (with . and ..)
759 ; BYTES CR-terminated parameters
760 ; BYTE 0 flag to indicate end of parameters
762 ; We allocate the maximum size for the command line and use setblock to shrink
763 ; later when we've squeezed out the extra
766 MOV BX,CX ; length of file name.
767 ADD BX,0Fh + (SIZE BatchSegment
) + COMBUFLEN
+ 0Fh
768 ; structure + max len + round up
771 SHR BX,CL ; convert to paragraphs
772 PUSH BX ;G save size of batch segment
774 INT int_command
; Allocate batch segment
775 POP BX ;G get size of batch segment
777 ; This should *NEVER* return an error. The transient is MUCH bigger than
778 ; the batch segment. This may not be true, however, in a multitasking system.
779 ; G This error will occur with nesting of batch files. We also need to
780 ; G make sure that we don't overlay the transient.
782 jc mem_error
;G not enough memory - exit
783 push ax ;G save batch segment
784 add ax,bx ;G get end of batch segment
785 add ax,20h
;G add some tpa work area
786 mov bx,cs ;G get the transient segment
787 cmp ax,bx ;G do we end before the transient
788 pop ax ;G get batch segment back
789 jb enough_mem
;G we have enough memory - continue
790 push es ;G no we're hitting the transient
792 mov ax,DEALLOC
SHL 8 ;G deallocate the batch segment
797 jmp no_memory
;G Set up for message and exit
803 ; Initialize batch segment
805 RestoreReg
<DX> ; length of name
806 POP AX ;G get saved batch segment back
807 inc es:nest
;G increment # batch files in progress
811 MOV ES:[BatType
],BatchType
; signal batch file type
812 MOV ES:[batlast
],ax ;G save segment of last batch file
814 mov DS,[resseg
] ;G set to resident data
817 mov bl,forflag
;G get the current FOR state
818 mov ES:[batforflag
],bl ;G save it in the batch segment
819 test bl,-1 ;G are we in a FOR?
820 jz for_not_on
;G no, for segment set to 0
821 mov ax,forptr
;G yes, get current FOR segment
822 mov forflag
,0 ;G reset forflag
825 mov ES:[batforptr
],ax ;G save FOR segment in batch segment
827 mov forptr
,ax ;G make sure for segment is not active
831 mov byte ptr es:[Batechoflag
],bl ;G save echo state of parent
832 MOV WORD PTR ES:[BatSeek
],AX ; point to beginning of file
833 MOV WORD PTR ES:[BatSeek
+2],AX
835 ; Initialize pointers
837 DEC AX ; put -1 into AX
838 MOV DI,BatParm
; point to parm area
841 REP STOSW ; Init to no parms
843 ; Move in batch file name
846 rep movsb ; including NUL.
848 ; Now copy the command line into batch segment, parsing the arguments along
849 ; the way. Segment will look like this:
851 ; <arg0>CR<arg1>CR...<arg9>CR<arg10>CR...<ARGn>CR 0
853 ; or, in the case of fewer arguments:
855 ; <arg0>CR<arg1>CR...<arg6>CR CR CR ... CR 0
857 MOV SI,OFFSET TRANGROUP
:COMBUF
+2
858 MOV CX,10 ; at most 10 arguments
860 ; Look for beginning of next argument
863 invoke SCANOFF
; skip to argument
865 ; AL is first non-delimiter. DS:SI points to char = AL
867 CMP AL,0DH ; end of road?
868 JZ HAVPARM
; yes, no more arguments
870 ; If CX = 0 then we have stored the most parm we can. Skip store
872 JCXZ MOVPARM
; Only first 10 parms get pointers
874 ; Go into allocated piece and stick in new argument pointer.
876 MOV ES:[BX],DI ; store batch pointer
877 ADD BX,2 ; advance arg counter
879 ; Move the parameter into batch segment
883 INVOKE DELIM
; if delimiter
884 JZ ENDPARM
; then done with parm
886 CMP AL,0DH ; if CR then not delimiter
887 JZ HAVPARM
; but end of parm list, finish
890 ; We have copied a parameter up until the first separator. Terminate it with
897 JCXZ EACHPARM
; if no parameters, don't dec
898 DEC CX ; remember that we've seen one.
901 ; We have parsed the entire line. Terminate the arg list
906 STOSB ; Nul terminate the parms
908 ; Now we know EXACTLY how big the BATCH segment is. Round up size (from DI)
909 ; into paragraphs and setblock to the appropriate size
920 POP DS ; Simply batch FCB setup
924 MOV [SINGLECOM
],0FFF0H ; Flag single command BATCH job
928 ; Enter the batch file with the current echo state
930 pop ax ; Get original echo state
931 mov echoflag
,al ;g restore it
935 ; The following is executed if there isn't enough memory for batch segment
939 assume
ds:trangroup
,es:resgroup
940 pop dx ;g even up our stack
943 call Alloc_tpa
;g reallocate memory
944 mov msg_disp_class
,ext_msg_class
;AN000; set up extended error msg class
945 mov dx,offset TranGroup
:Extend_Buf_ptr
;AC000; get extended message pointer
946 mov Extend_Buf_ptr
,error_not_enough_memory
;AN000; get message number in control block
947 jmp cerror
;g print error message and go...
953 ASSUME
DS:NOTHING
,ES:NOTHING
960 ASSUME
ES:ResGroup
,DS:Resgroup
;G
961 MOV AX,Batch
; Free the batch segment
967 test [echoflag
],1 ;G Is echo on?
968 jnz echo_last_line
;G Yes - echo last line in file
969 mov suppress
,no_echo
;G no - don't echo last line in file
972 MOV BL,ES:[BATECHOFLAG
] ;G Get echo state
973 mov [echoflag
],bl ;G and restore it
974 MOV BX,ES:[BATFORPTR
] ;G Get FOR segment
975 MOV FORPTR
,BX ;G and restore it
976 MOV BL,ES:[BATFORFLAG
] ;G Get FOR flag
977 MOV FORFLAG
,BL ;G and restore it
978 MOV BX,es:[batlast
] ;G get old batch segment
982 MOV Next_BATCH
,BX ;G reset batch segment
986 MOV Batch
,AX ; No batch in progress
1000 Procedure StrCpy
,near
1015 ; StrCpy - copy string, checking count in CX against COMBUFLEN
1016 ; Entry : DS:SI ==> source string
1017 ; ES:DI ==> destination string
1018 ; CX = current length of destination string
1019 ; Exit : string copied, CX updated, Carry set if length limit exceeded
1020 Procedure StrCpy
,NEAR
1027 stc ; set carry to signal error
1035 dec cx ; discount extra byte
1036 dec di ; back up pointer
1038 return
; return carry clear