	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;
.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 suck 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
