		page	66,132
;============================================================================
; BAT2EXEC.COM - a batch file compiler
;
; Syntax:
;	BAT2EXEC filename
;
; Revision History:
;
;	Version 1.0	Initial Release 	PC Magazine Vol 9 Num 14
;	Version 1.1	Bug Fixes               July 19, 1990
;	Version 1.2	Bug Fixes               July 28, 1990
;                       Added ability to read large BAT files.
;       Version 1.3     PATH cmd fix.           Oct.  3, 1990
; 			Echo cmd fix
;                       Other small bug fixes.
;
;============================================================================

		code	segment
		assume	cs:code

		org	2ch
local_environment	dw	?			;Word containing the segment
						;  of the program's env. block.
		org	80h
command_tail	db	?			;Offset of the command tail.

		org	100h

main:		jmp	initialize
program 	db	13,10,"BAT2EXEC 1.3 "
copyright	db	"(c) 1990 Ziff Communications Co.",10,13
author		db	"PC Magazine ",254," Douglas Boling"
		db	10,13,"$",1Ah
;----------------------------------------------------------------------------
;Equates used to find data offsets in the compiled program.
;----------------------------------------------------------------------------
BUFF_SIZE    	equ	255			;Size of runtime buffers

std_data_size	equ	offset data_end - data_start
code_start_ptr	equ	[bp + offset code_start - offset data_start]
com_stack_ptr	equ	[bp + offset stack_ptr - offset data_start]
com_prog_size	equ	[bp + offset prog_segsize - offset data_start]
com_label_start equ	[bp + offset label_list_strt - offset data_start]
parse_buff	equ	[bp + offset prse_buff_ptr - offset data_start]
parse2_buff	equ	[bp + offset prs2_buff_ptr - offset data_start]
exec_buff	equ	[bp + offset exec_buff_ptr - offset data_start]
forloop_buff	equ	[bp + offset for_buff_ptr - offset data_start]
forloop_ptr	equ	[bp + offset floop_ptr - offset data_start]
stdout_hdl	equ	[bp + offset file_handle1 - offset data_start]
outfile_hdl	equ	[bp + offset file_handle2 - offset data_start]
stdin_hdl	equ	[bp + offset file_handle3 - offset data_start]
infile_hdl	equ	[bp + offset file_handle4 - offset data_start]
environment_seg equ	[bp + offset master_env - offset data_start]
dos_version	equ	[bp + offset version_num - offset data_start]
process_rc	equ	[bp + offset proc_rc - offset data_start]
shift_count	equ	[bp + offset shift_cnt - offset data_start]

code_call_size	equ	offset code_call_end - offset code_call
code_jmp_size	equ	offset code_jmp_end - offset code_jmp
code_jc_size	equ	offset code_jc_end - offset code_jc
code_jnc_size	equ	offset code_jnc_end - offset code_jnc
code_jmpdis_size   equ	   offset code_jmpdis_end - offset code_jmpdis
code_leasi_size equ	offset code_leasi_end - offset code_leasi
code_movsi_size equ	offset code_movsi_end - offset code_movsi
code_movsiim_size equ	offset code_movsiim_end - offset code_movsiim
code_leadi_size equ	offset code_leadi_end - offset code_leadi
code_movdi_size equ	offset code_movdi_end - offset code_movdi
code_movdiim_size equ	offset code_movdiim_end - offset code_movdiim
;============================================================================
;Compiler data
;============================================================================
command_table	db	"IF",0                  ;Commands processed by
		db	"REM",0                 ;  compiler
		db	"FOR",0
		db	"ECHO",0
		db	"GOTO",0
		db	"EXIT",0
		db	"PAUSE",0
		db	"SHIFT",0
		db	"SET",0
		db	"CALL",0
		db	"PATH",0
		db	"PROMPT",0
		db	"CD",0                  ;DOS commands internal to
		db	"MD",0                  ;  command.com.
		db	"RD",0
		db	"CLS",0
		db	"DIR",0
		db	"DEL",0
		db	"REN",0
		db	"VER",0
		db	"VOL",0
		db	"CTTY",0
		db	"CHCP",0
		db	"TYPE",0
		db	"COPY",0
		db	"DATE",0
		db	"TIME",0
		db	"ERASE",0
		db	"CHDIR",0
		db	"MKDIR",0
		db	"RMDIR",0
		db	"BREAK",0
		db	"RENAME",0
		db	"DELETE",0
		db	"VERIFY",0
		db	"COMMAND",0,0

batcmd_jmptbl	dw	if_cmd			;if command
		dw	rem_cmd 		;rem command
		dw	for_cmd 		;for command
		dw	echo_cmd		;echo command
		dw	goto_cmd		;goto command
		dw	rem_cmd 		;exit command
		dw	pause_cmd		;pause command
		dw	shift_cmd		;shift command
		dw	set_cmd 		;set command

		dw	internal_cmd		;call command
		dw	path_cmd 		;Path command
		dw	prompt_cmd 		;Prompt command
		dw	internal_cmd		;DOS internal command
		dw	external_cmd		;DOS program
		dw	label_cmd		;Process BAT label
batcmd_jmptbl1	=	$

ifstr1		db	"ERRORLEVEL"
ifstr2		db	"EXIST"

internal_cmdsw	db	"/C "                   ;Switch for transient commands

for_active_flag db	0			;Set if parsing a FOR loop
goto_active	db	0			;Set if goto parsed
goto_data_ptr	dw	0			;Data offset of last goto	

temp1		dw	0			;Temp data storage.

cmd_switches	db	?			;Used if command line/„ŒˆppŒ	€Œ€‰ò@rIòrr@OÏ€ˆ	ˆ	ÀðpðpŽ€€‚ˆr	€rˆ„Ï‚‚€	ˆˆò@ŽÏŒ€ˆ	Ï„€‚ˆO€…{@ `X‹š">	äääää€9	ŸŸ	ŸŸàá	äääää€ŸŸŸŸŸ8  Ÿàáàá	ää€äää€9	ŸŸŸ jmp_disp_error	;If none, report no filename
init_1:
		mov	al,[si]
		cmp	amÀ`&ÔÙYHYˆÛÛ[X[™ÝÚ]ÚB‚BZ™B\\œÙWÙ\œ›ÜƒB‚BXØ[[ØY˜]š[BBNÓØY[œ]Uš[CB‚BZ˜ÂZ›\Ù\ÜÙ\œ›ÜƒB‚BZ›\\ÚÜ\œÙWØÛY[™WÙ[™Bœ\œÙWÙ\œ›ÜŽƒB‚B[[Ý‚YÙ™œÙ]\œ›\ÙÌƒBš›\Ù\ÜÙ\œ›ÜŽƒB‚BZ›\Y\ÜÙ\œ›ÜƒBœ\œÙWØÛY[™WÙ[™ƒBŽÃBŽÐÛÛ\[HUš[H[™HžH[™KƒBŽÃB‚B[[Ý‚\ÚK[˜Y™—Ü‚BNÔÒHÚ[ÈÈ[œ]Uš[CB‚B[[Ý‚YK]XY™—ÜÝ\NÑHÚ[ÈÈÓÓHš[H]KƒB‚BXYYKÝÙ]WÜÚ^™BNÓXZÙH›ÛÛH›Üˆstd data set.
new_line:
		inc	file_linecount		;Inc line number
new_line1:
		xor	bl,bl			;Look for 1st character
		call	scan4char		;If nothing on line, check
		jc	comp_endcheck		;  for EOF
		call	redirect_check
		cmp	byte ptr [si],'@'	;See if non echo prefix
		jne	comp_1
		inc	si			;Move past @
comp_1:
		call	parse			;Parse 1st wp‰Ï‚ÏƒˆO‚ˆ@ðpòrŒOò€€	‚€„€òrppòr€ò€Ï€ˆ‚ˆppŒ€‰Œˆ@ðpòròÁÆDrrr@O‚ÏŒ„ŽO„ˆ€€ÁÏ‚Ï„ppòrˆrˆ‚ˆrr@OÏŒ‚€@ðpòrò‚ˆCŽ‰,13,"$",1Ah
;----------------------------------------------------------------------------
;Equates used to find data offsets in the compiled program.
;----------------------------------------------------------------------------
BUFF_SIZE    	equ	255			;Size of runtime buffers

std_data_size	equ	offset data_end - data_start
code_start_ptr	equ	[bp + offset code_start - offset data_start]
com_stack_ptr	equ	[bp + offset stack_ptr - offset data_start]
com_prog_size	equ	[bp + offset prog_segsize - offset data_start]
com_label_start equ	[bp + offset label_list_strt - offset data_start]
parse_buff	equ	[bp + offset prse_buff_ptr - offset data_start]
parse2_buff	equ	[bp + offset prs2_buff_ptr - offset data_start]
exec_buff	equ	[bp + offset exec_buff_ptr - offset data_start]
forloop_buff	equ	[bp + offset for_buff_ptr - offset data_start]
forloop_ptr	equ	[bp + offset floop_ptr - offset data_start]
stdout_hdl	equ	[bp + offset file_handle1 - offset data_start]
outfile_hdl	equ	[bp + offset file_handle2 - offset data_start]
stdin_hdl	equ	[bp + offset file_handle3 - offset data_start]
infile_hdl	equ	[bp + offset file_handle4 - offset data_start]
environment_seg equ	[bp + offset master_env - offset data_start]
dos_version	equ	[bp + offset version_num - offset data_start]
process_rc	equ	[bp + offset proc_rc - offset data_start]
shift_count	equ	[bp + offset shift_cnt - offset data_start]

code_call_size	equ	offset code_call_end - offset code_call
code_jmp_size	equ	offset code_jmp_end - offset code_jmp
code_jc_size	equ	offset code_jc_end - offset code_jc
code_jnc_size	equ	offset code_jnc_end - offset code_jnc
code_jmpdis_size   equ	   offset code_jmpdis_end - offset code_jmpdis
code_leasi_size equ	offset code_leasi_end - offset code_leasi
code_movsi_size equ	offset code_movsi_end - offset code_movsi
code_movsiim_size equ	offset code_movsiim_end - offset code_movsiim
code_leadi_size equ	offset code_leadi_end - offset code_leadi
code_movdi_size equ	offset code_movdi_end - offset code_movdi
code_movdiim_size equ	offset code_movdiim_end - offset code_movdiim
;============================================================================
;Compiler data
;============================================================================
command_table	db	"IF",0                  ;Commands processed by
		db	"REM",0                 ;  compiler
		db	"FOR",0
		db	"ECHO",0
		db	"GOTO",0
		db	"EXIT",0
		db	"PAUSE",0
		db	"SHIFT",0
		db	"SET",0
		db	"CALL",0
		db	"PATH",0
		db	"PROMPT",0
		db	"CD",0                  ;DOS commands internal to
		db	"MD",0                  ;  command.com.
		db	"RD",0
		db	"CLS",0
		db	"DIR",0
		db	"DEL",0
		db	"REN",0
		db	"VER",0
		db	"VOL",0
		db	"CTTY",0
		db	"CHCP",0
		db	"TYPE",0
		db	"COPY",0
		db	"DATE",0
		db	"TIME",0
		db	"ERASE",0
		db	"CHDIR",0
		db	"MKDIR",0
		db	"RMDIR",0
		db	"BREAK",0
		db	"RENAME",0
		db	"DELETE",0
		db	"VERIFY",0
		db	"COMMAND",0,0

batcmd_jmptbl	dw	if_cmd			;if command
		dw	rem_cmd 		;rem command
		dw	for_cmd 		;for command
		dw	echo_cmd		;echo command
		dw	goto_cmd		;goto command
		dw	rem_cmd 		;exit command
		dw	pause_cmd		;pause command
		dw	shift_cmd		;shift command
		dw	set_cmd 		;set command

		dw	internal_cmd		;call command
		dw	path_cmd 		;Path command
		dw	prompt_cmd 		;Prompt command
		dw	internal_cmd		;DOS internal command
		dw	external_cmd		;DOS program
		dw	label_cmd		;Process BAT label
batcmd_jmptbl1	=	$

ifstr1		db	"ERRORLEVEL"
ifstr2		db	"EXIST"

internal_cmdsw	db	"/C "                   ;Switch for transient commands

for_active_flag db	0			;Set if parsing a FOR loop
goto_active	db	0			;Set if goto parsed
goto_data_ptr	dw	0			;Data offset of last goto	

temp1		dw	0			;Temp data storage.

cmd_switches	db	?			;Used if command line/„ŒˆppŒ	€Œ€‰ò@rIòrr@OÏ€ˆ	ˆ	ÀðpðpŽ€€‚ˆr	€rˆ„Ï‚‚€	ˆˆò@ŽÏŒ€ˆ	Ï„€‚ˆO€…{@ `X‹š">	äääää€9	ŸŸ	ŸŸàá	äääää€ŸŸŸŸŸ8  Ÿàáàá	ää€äää€9	ŸŸŸtry:  SI - Pointer to ASCII string to find.
;	  DI - Pointer to list of ASCIIZ strings.
;	  CX - Size of string
; Exit:   CF - Clear if string found
;	  BX - If CF clear, index into list
;-----------------------------------------------------------------------------
findstr 	proc	near
		push	cx
		push	es
		push	cs			;Point ES:DI to table of
		pop	es			;  batch commands.
		xor	dx,dx
		or 	dx,cx			;Save length of string
		je	finds_3
		xor	bx,bx			;Zero index counter
finds_1:
		push	si
		mov	cx,dx			;Restore command size
		repe	cmpsb			;Compare command
		pop	si
		je	string_found
		inc	bx
		xor	al,al
		cmp	[di-1],al
		jne	finds_2
		dec	di
finds_2:
		mov	cx,10			;Scan to next zero
		repne	scasb
		cmp	byte ptr [di],0 	;See if second zero. If so
		jne	finds_1 		;  end of list.
finds_3:
		mov	bx,13
		stc				;Indicate string not found
findstr_exit:
		pop	es
		pop	cx
		ret
string_found:
		cmp	bx,12			;If past the BAT commands,
		jb	findstr_3		;  then it is an internal cmd.
		mov	bx,12
findstr_3:
		clc				;Set string found flag
		jmp	short findstr_exit
findstr 	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsalpha	proc near
		assume	ds:nothing,es:nothing
		push	ax
		xor	ax,ax			;Exit on non-alpha chars
		jmp	short capsword_entry2
capsalpha	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsword	proc near
		assume	ds:nothing,es:nothing
		push	ax
		mov	ah,1			;Ignore extra characters
capsword_entry2:
		push	di
		push	si
		push	es
		push	ds			;Set ES:DI = DS:SI
		pop	es
		mov	di,si
		xor	cx,cx			;Clear byte counter.
caps_1:
		lodsb				;Get character
		cmp	al," "                  ;Allow any non-space character
		jbe	caps_exit
		cmp	al,"a"                  ;If between a and z,
		jb	caps_3   		;  capitalize it.
		cmp	al,"z"
		ja	caps_exit
		and	al,0DFh
caps_2:
		stosb				;Save character
		inc	cx			;Inc byte counter
		jmp	short caps_1
caps_3:
		or	ah,ah			;See if we should check 
		jne	caps_2			;  non-alpha chars.
		cmp	al,'Z'
		ja	caps_exit
		cmp	al,'A'
		jae	caps_2
caps_exit:
		pop	es
		pop	si
		pop	di
		pop	ax
		ret
capsword	endp

;-----------------------------------------------------------------------------
; REDIRECT CLOSE post process redirection commands
; Entry:  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_close	proc	near
		assume	cs:code,ds:code
		cmp	redirect_in,0		;See if redirecton active
		je	redirclose_1
		mov	redirect_in,0
		mov	bx,offset redirci_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_1:
		cmp	redirect_out,0		;See if redirecton active
		je	redirclose_2
		mov	redirect_out,0
		mov	bx,offset redirco_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_2:
		cmp	pipein_flag,0		;See if in pipe curr active
		je 	redirclose_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@---------
; REDIRECT OPENI loads code to open a file for ynput redirection
; Entry:  DX - Offset from data buffer start oæ file name
<rOÏœƒÏÀOÇÏ€€O„Ž„Ž‚Ï€Ïˆ‚ˆŽ@ðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@pp„ˆ	‚ˆ€ˆ‚r„€rˆðpòrr@Œ	ˆA‰„@Œ	ˆppòr€òppòròÁÆrrr@‰‚ˆO‡ŽŽˆÏ‚ˆppòròƒÁŒƒòrr@O‚Ïˆ€Ï€ŽÏ€ÏŒ	Ï‚ˆppòrˆrˆ	‚ˆ€Frr@OÏ‡Ž€@ðpðpòròƒÁ€ˆ„Ï‡„€LyÀÜ€@; IÚ;+¡{33›+¡{1£“s›c£(hPHKc`KKsc«#*û{#(HIÙ,dx			;Restore command size
		repe	cmpsb			;Compare command
		pop	si
		je	string_found
		inc	bx
		xor	al,al
		cmp	[di-1],al
		jne	finds_2
		dec	di
finds_2:
		mov	cx,10			;Scan to next zero
		repne	scasb
		cmp	byte ptr [di],0 	;See if second zero. If so
		jne	finds_1 		;  end of list.
finds_3:
		mov	bx,13
		stc				;Indicate string not found
findstr_exit:
		pop	es
		pop	cx
		ret
string_found:
		cmp	bx,12			;If past the BAT commands,
		jb	findstr_3		;  then it is an internal cmd.
		mov	bx,12
findstr_3:
		clc				;Set string found flag
		jmp	short findstr_exit
findstr 	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsalpha	proc near
		assume	ds:nothing,es:nothing
		push	ax
		xor	ax,ax			;Exit on non-alpha chars
		jmp	short capsword_entry2
capsalpha	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsword	proc near
		assume	ds:nothing,es:nothing
		push	ax
		mov	ah,1			;Ignore extra characters
capsword_entry2:
		push	di
		push	si
		push	es
		push	ds			;Set ES:DI = DS:SI
		pop	es
		mov	di,si
		xor	cx,cx			;Clear byte counter.
caps_1:
		lodsb				;Get character
		cmp	al," "                  ;Allow any non-space character
		jbe	caps_exit
		cmp	al,"a"                  ;If between a and z,
		jb	caps_3   		;  capitalize it.
		cmp	al,"z"
		ja	caps_exit
		and	al,0DFh
caps_2:
		stosb				;Save character
		inc	cx			;Inc byte counter
		jmp	short caps_1
caps_3:
		or	ah,ah			;See if we should check 
		jne	caps_2			;  non-alpha chars.
		cmp	al,'Z'
		ja	caps_exit
		cmp	al,'A'
		jae	caps_2
caps_exit:
		pop	es
		pop	si
		pop	di
		pop	ax
		ret
capsword	endp

;-----------------------------------------------------------------------------
; REDIRECT CLOSE post process redirection commands
; Entry:  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_close	proc	near
		assume	cs:code,ds:code
		cmp	redirect_in,0		;See if redirecton active
		je	redirclose_1
		mov	redirect_in,0
		mov	bx,offset redirci_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_1:
		cmp	redirect_out,0		;See if redirecton active
		je	redirclose_2
		mov	redirect_out,0
		mov	bx,offset redirco_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_2:
		cmp	pipein_flag,0		;See if in pipe curr active
		je 	redirclose_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@com file data area.  As always, if environment
;variables or command line parameters are in the data, insert a call to
;the translate routine before calli~g the for loop routine.
;
for_cmd_1:
		lodsb				;Scan until '(' is found
		cmp	al,13			;  indicating the start of the
		jne	for_cmd_2		;  data set.
jmp_for_syntax:
		jmp	for_syntax
for_cmd_2:
		cmp	al,'('
		jne	for_cmd_1
		mov	ah,3			;Copy until ')' found.
		call	copy_string
		xpòÁŽòrr@ˆ€0º29º94·3;´º4=2¹7—…„¹º7¹±…„¶·»±¼˜´,dx			;Restore command size
		repe	cmpsb			;Compare command
		pop	si
		je	string_found
		inc	bx
		xor	al,al
		cmp	[di-1],al
		jne	finds_2
		dec	di
finds_2:
		mov	cx,10			;Scan to next zero
		repne	scasb
		cmp	byte ptr [di],0 	;See if second zero. If so
		jne	finds_1 		;  end of list.
finds_3:
		mov	bx,13
		stc				;Indicate string not found
findstr_exit:
		pop	es
		pop	cx
		ret
string_found:
		cmp	bx,12			;If past the BAT commands,
		jb	findstr_3		;  then it is an internal cmd.
		mov	bx,12
findstr_3:
		clc				;Set string found flag
		jmp	short findstr_exit
findstr 	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsalpha	proc near
		assume	ds:nothing,es:nothing
		push	ax
		xor	ax,ax			;Exit on non-alpha chars
		jmp	short capsword_entry2
capsalpha	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsword	proc near
		assume	ds:nothing,es:nothing
		push	ax
		mov	ah,1			;Ignore extra characters
capsword_entry2:
		push	di
		push	si
		push	es
		push	ds			;Set ES:DI = DS:SI
		pop	es
		mov	di,si
		xor	cx,cx			;Clear byte counter.
caps_1:
		lodsb				;Get character
		cmp	al," "                  ;Allow any non-space character
		jbe	caps_exit
		cmp	al,"a"                  ;If between a and z,
		jb	caps_3   		;  capitalize it.
		cmp	al,"z"
		ja	caps_exit
		and	al,0DFh
caps_2:
		stosb				;Save character
		inc	cx			;Inc byte counter
		jmp	short caps_1
caps_3:
		or	ah,ah			;See if we should check 
		jne	caps_2			;  non-alpha chars.
		cmp	al,'Z'
		ja	caps_exit
		cmp	al,'A'
		jae	caps_2
caps_exit:
		pop	es
		pop	si
		pop	di
		pop	ax
		ret
capsword	endp

;-----------------------------------------------------------------------------
; REDIRECT CLOSE post process redirection commands
; Entry:  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_close	proc	near
		assume	cs:code,ds:code
		cmp	redirect_in,0		;See if redirecton active
		je	redirclose_1
		mov	redirect_in,0
		mov	bx,offset redirci_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_1:
		cmp	redirect_out,0		;See if redirecton active
		je	redirclose_2
		mov	redirect_out,0
		mov	bx,offset redirco_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_2:
		cmp	pipein_flag,0		;See if in pipe curr active
		je 	redirclose_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@osw				;Clear end of list tag
		stosw				;Indicate unitialized label
		mov	al,cl
		stosb				;Save length of label
		rep	movsb			;Copy label into data buffer
		mov	ax,bp
		sub	ax,bx			;Compute offset to new label
		mov	[bx],ax 		;Load offset in prev. label
		mov	bx,bp			;Get start of label entry
getlabel_exit:
		clc
		ret
getlabel	endp

;-----------------------------------------------------------------------------
; EXTERNAL CMD compiles a routine to exexute a program.
; Entry:  SI - Pointer to character after the command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
external_cmd	proc	near
		assume	cs:code,ds:code
		mov	dx,di			;Save ptr to filename
		sub	dx,databuff_start	;Compute offset.
		mov	ah,1
		call	copy_string		;Copy filename to COM data.
		mov	temp1,bx		;Save translate flag
		dec	si			;Back up one char
		xor	al,al			;Terminate filename with zero
		stosb
		inc	di
		push	di			;Save ptr to command line tail
		xor	ah,ah
		call	cï‡‚„‚ˆrr@‚OŒ‰Ï‚ˆOŽÀðpòròÁÇÇÇ‰ƒòr@‡ˆ‰ÏœÏŽ‰Ï€ˆ€ppòr€ppòr€ò‡òrr@ˆOˆˆƒÏ€ÏŒ	Ï‚ˆOŽÀðpòrŒròrr@OÏž	€€Ïˆ€ÏƒˆOœÏ„ŒOðpòrò‡ÀFAŒòr@OÏ‡„€„Ï€ÏŽ	‰ÏœÀ:7:42†…„¹º±±820º0±:³3/¹º0¹:1·º·:"¼2±897³34¼2¹…„¸yàã#ù99 'çÀÁÁ'ÇçÂ@gÀÁ x8y9yAàÆAù99 'ÁgÀÂGBÇÁgÆÄ'ÀDÄø8y9D9ÀÄ@GÆÀ#88`88`'ÁgÀ'ÀDçÀÂGBÇÁg2
capsalpha	endp

;-----------------------------------------------------------------------------
; CAPSWORD capitalizes word pointed to by SI
; Entry:  SI - Pointer to ASCII word to capitalize
; Exit:   CX - Size of word
;-----------------------------------------------------------------------------
capsword	proc near
		assume	ds:nothing,es:nothing
		push	ax
		mov	ah,1			;Ignore extra characters
capsword_entry2:
		push	di
		push	si
		push	es
		push	ds			;Set ES:DI = DS:SI
		pop	es
		mov	di,si
		xor	cx,cx			;Clear byte counter.
caps_1:
		lodsb				;Get character
		cmp	al," "                  ;Allow any non-space character
		jbe	caps_exit
		cmp	al,"a"                  ;If between a and z,
		jb	caps_3   		;  capitalize it.
		cmp	al,"z"
		ja	caps_exit
		and	al,0DFh
caps_2:
		stosb				;Save character
		inc	cx			;Inc byte counter
		jmp	short caps_1
caps_3:
		or	ah,ah			;See if we should check 
		jne	caps_2			;  non-alpha chars.
		cmp	al,'Z'
		ja	caps_exit
		cmp	al,'A'
		jae	caps_2
caps_exit:
		pop	es
		pop	si
		pop	di
		pop	ax
		ret
capsword	endp

;-----------------------------------------------------------------------------
; REDIRECT CLOSE post process redirection commands
; Entry:  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_close	proc	near
		assume	cs:code,ds:code
		cmp	redirect_in,0		;See if redirecton active
		je	redirclose_1
		mov	redirect_in,0
		mov	bx,offset redirci_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_1:
		cmp	redirect_out,0		;See if redirecton active
		je	redirclose_2
		mov	redirect_out,0
		mov	bx,offset redirco_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_2:
		cmp	pipein_flag,0		;See if in pipe curr active
		je 	redirclose_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Append runtime routines.
		sub	dx,databuff_start
		or	bx,bx			;Check translate flag, if set
		je	echo_c21		;  append translate code 1st.
		mov	bx,offset procstr_next	;Append translate string
		call	include_code		;  routine to code.
		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,0021h		;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	dx,bx
		mov	cx,2			;Use proper parameter type
echo_c21:
		mov	bx,offset echo_msg_next ;Append echo string
		call	include_code		;  routine to code.
echo_c3:
		call	inline_code		;Add code to COM file.
echo_cstatus:
echo_exit:
		clc
echo_cmd_exit:
		ret
echo_cmd	endp

;-----------------------------------------------------------------------------
; PAUSE CMD compiles a PAUSE command.
; Entry:  SI - Pointer to character after the PAUSE command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
pause_cmd	proc	near
		assume	cs:code,ds:code
;Append routine if necessary
		mov	bx,offset pause_next	 ;Append PAUSE routine if
		call	include_code		 ;  necessary.

		mov	cx,0			;Add inline code with 0 params
		call	inline_code		;  to call pause routine.

		mov	bl,2			;Scan to end of line
		call	scan4char
		clc
pause_cmd_exit:
		ret
pause_cmd	endp

;-----------------------------------------------------------------------------
; SHIFT CMD compiles a SHIFT command.
; Entry:  SI - Pointer to character after the SHIFT command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
shift_cmd	proc	near
		assume	cs:code,ds:cp	ˆppòròƒÁ€ˆ„Ï„‚€ˆòOÀ‡ˆ‰Ï„‚Ï„€‚ˆO‚ðpòròŒ€	ˆ	ˆrrOÀOÏ€ˆ‚@ðpòròÁÇòrr@	‰Ï‚‚ˆOŒ	ˆO€ƒÏÇÏ‡ŽŽppòrò‚ˆ	ˆrr@OÏ€OŒÏ‡ŽO„€‚ˆ@ðpòr·»±6„„©±°·:72d of line
		call	scan4char
		clc
		ret
shift_cmd	endp

;-----------------------------------------------------------------------------
; REM CMD  Processes remark lines in batch file.
; Entry:  SI - pointer to line in BAT file
;--------------------------------------------------------------------------.@A†8œ"­€¶Öœ@<$¹•…È4($%…ÍÍÕµ”%Ìé½‘”±‘Ìé½‘”4($%‘•Œ%Í¤$$$í	…¬ÕÀÑ¼µ…­”ÍÕÉ”Ý”‘½¸Ð4)É•µ}ŒÄè€%±½‘Íˆ$$$$ì€µ¥ÍÌ„…ÉÉ…”É•ÑÕÉ¸¸4($%µÀ%…°°Å… $$$í1½½ÀÕ¹Ñ¥°•¹½˜±¥¹”½È•¹4($%©”%É•µ}•á¥Ð$$_2
caps_exit:
		pop	es
		pop	si
		pop	di
		pop	ax
		ret
capsword	endp

;-----------------------------------------------------------------------------
; REDIRECT CLOSE post process redirection commands
; Entry:  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_close	proc	near
		assume	cs:code,ds:code
		cmp	redirect_in,0		;See if redirecton active
		je	redirclose_1
		mov	redirect_in,0
		mov	bx,offset redirci_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_1:
		cmp	redirect_out,0		;See if redirecton active
		je	redirclose_2
		mov	redirect_out,0
		mov	bx,offset redirco_next	;Append close redirect file
		call	include_code		;  routine to code.
		xor	cx,cx
		call	inline_code
redirclose_2:
		cmp	pipein_flag,0		;See if in pipe curr active
		je 	redirclose_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ODE - Appends the routine to the append list if necessary and
;		 returns the offset of the routine in the compiled program.
; Entry:  BX - Pointer to header of canned routine to append.
; Exit:   AX - Offset of canned routine in COM file.
;-----------------------------------------------------------------------------
include_code	proc	near
		assume	ds:code,es:nothing
		push	cx
		push	dx
		mov	ax,[bx-4]		;Get COM file offset
		or	ax,ax			;If zero, routine has not
		jne	include_exit		;  been appended.
		mov	cx,bx			;Save pointer to new routine
;
;Address the prevous 'last' routine header.  Update that header, then use
;information in that header to compute the offset of the new routine.
;
		xchg	bx,last_routine
		mov	[bx],cx 		;Add code to chain.
		mov	ax,[bx-2]		;Compute offset of new routine
		add	ax,[bx-4]		;  by adding the offset of the
		sub	ax,[bx+2]		;  prevous routine to its size.
		mov	bx,cx

		mov	cx,[bx+2]		;Get number of called routines.
		add	ax,cx
		mov	[bx-4],ax		;Set offset of code.
		jcxz	include_exit
		shr	cx,1			;If this routine needs other
		add	bx,4			;  routines, include them in
		push	ax			;  the COM file.
include_1:
		push	cx
		push	bx
		mov	bx,[bx]
		call	include_code
		pop	bx
		mov	[bx],ax
		add	bx,2
		pop	cx
		loop	include_1
		pop	ax
include_exit:
		pop	dx
		pop	cx
		ret
include_code	endp
;-----------------------------------------------------------------------------
; PRINTMSG prints the message pointed to by DX to the screen.
; Entry:  DX - pointer to ASCII message terminated by $
;-----------------------------------------------------------------------------
printmsg	proc	near
		assume	ds:nothing,es:nothing
		push	ds
		push	cs
		pop	ds
		assume	ds:code
		mov	ah,9			;Print message
		int	21h
		pop	ds
		ret
printmsg	endp

;-----------------------------------------------------------------------------
; PRINTMSGCR calls PRINTMSG, then appends a carriage return to the message.
; Entry:  DX - pointer to ASCII message terminated by $
;-----------------------------------------------------------------------------
printmsgcr	proc	near
		assume	ds:nothing,es:nothing
		push	dx
		call	printmsg
		mov	dx,offset endmsg
		call	printmsg
		pop	dx
		ret
printmsgcr	endp

;-----------------------------------------------------------------------------
; HEX2ASC converts a binary number to ASCII and prints it to the screen.
; Entry:  AX - binary number
;-----------------------------------------------------------------------------
hex2asc 	proc near
		assume	ds:nothing,es:nothing
		push	bx
		mov	cx,5			;Allow max of five digits
hex_loop1:
		xor	dx,dx			;Clear high word
		mov	bx,10			;Load number base
		div	bx			;Divide by base (10)
		add	dl,30h			;Convert to ascii
		push	dx			;Save digit on stack
		loop	hex_loop1
		mov	cx,5			;Allow max of five digits
		mov	bl,"0"                  ;Set leading zero indicator
hex_loop2:
		pop	dx			;Get digit off stack
		or	bl,dl			;Don't print leading zeros.
		cmp	bl,"0"                  ;The first non zero will
		je	hex_1			?  change bl to non.ˆ€@ðpòròÁÄòrr@OŒŽŽˆÏ€‡€ðpòròDÆðpƒˆ€F@ðpòr€òˆ€€Äðpƒˆ€‚Àðpòr€òƒðpòrˆðpƒˆÄŽOò‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ppÀO‘œž˜’˜O€	„OƒˆO‚‡€ÏœžÏˆ‚ˆ@ðpÀO˜„‚@ÏÏ™„@„OÀO‡€ˆÏ€OƒˆO€ŽO€ÏƒˆOˆ‚ˆO€O€ˆðpÀO˜‚ÀÏÏÏÏÏÏœÏÀOŒˆÏ‚Ï„€ðpÀppÀOÏ„‡€Ïˆ€ÏœžÏˆ‚ˆOŽˆÏƒŽÏÆ@ÏO‚O€€ÏŒ„ˆ‚O‚‚ˆ	ÀðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@se_3
		mov	dx,pipein_file		;Get offset of input pipe file
		mov	cx,1			;Indicate parameter type
		mov	bx,offset redirdel_next	;Delete the piping file
		call	include_code
		call	inline_code		
		mov	pipein_flag,0		;Clear flag
redirclose_3:
		cmp	pipeout_flag,0		;See if out pipe curr active
		je 	redirclose_exit
		mov	dx,pipeout_file		;Get offset of input pipe file
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openi
		mov	pipein_flag,1		;Set pipe input flag
		mov	pipeout_flag,0		;Clear pipe output flag
		mov	pipein_file,dx		;Copy pointer to filename
redirclose_exit:
		ret
redirect_close	endp

;-----------------------------------------------------------------------------
; REDIRECT CHECK process redirection commands
; Entry:  SI - Pointer to current BAT file line
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  Batch file line updated to remove any redirection symbols
;-----------------------------------------------------------------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@c	near
		mov	si,[bp+1234h]		;Load data
code_movsi_end	=	$
code_movsi	endp

code_movsiim	proc	near
		mov	si,1234h		;Load immediate data
code_movsiim_end  =	$
code_movsiim	endp

code_leadi	proc	near
		lea	di,[bp+1234h]		;Load address of data
code_leadi_end	=	$
code_leadi	endp

code_movdi	proc	near
		mov	di,[bp+1234h]		;Load data
code_movdi_end	=	$
code_movdi	endp

code_movdiim	proc	near
		mov	di,1234h		;Load immediate data
code_movdiim_end  =	$
code_movdiim	endp

;----------------------------------------------------------------------------
;Predefined data needed for all compiled programs.
;----------------------------------------------------------------------------
data_start	=	$
code_start	dw	?			;Offset of main code routine
stack_ptr	dw	?			;Offset of end of code + stack
prog_segsize	dw	?			;Size of COM prog in paragraphs
prse_buff_ptr	dw	?			;Buffer for parsing strings
prs2_buff_ptr	dw	?			;Buffer for parsing strings
exec_buff_ptr	dw	?			;Buffer for exec function
for_buff_ptr	dw	?			;Buffer for For loop variables
floop_ptr	dw	?			;Pointer to for loop string
file_handle1	dw	?			;Saved handle of std output
file_handle2	dw	?			;Handle of output file
file_handle3	dw	?			;Saved handle of std input
file_handle4	dw	?			;Handle of input file
label_list_strt dw	?			;Offset into data of 1st label.
master_env	dw	?			;Segment of environment blk
version_num	dw	?			;DOS version number
proc_rc 	db	?			;Return code of last program.
shift_cnt	db	?			;Count of shift parameter
data_end	=	$

;----------------------------------------------------------------------------
;INIT CODE Routine at the start of all compiled programs.
;----------------------------------------------------------------------------
init_code_off	dw	100h			;Pointer to offset in COM file
init_code_size	dw	offset init_code_end-offset init_code_start
init_code_next	dw	0			;Ptr to next routine to append
init_code_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
init_code_start =	$

init_code	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		cld
		mov	bp,ds:[offset data_start_ptr - offset init_code + 100h]
		mov	bp,[bp]
		mov	sp,com_stack_ptr	;Move stack pointer
		mov	bx,com_prog_size	;Reduce memory allocation
		mov	ah,4ah			;Resize memory block
		int	21h
		mov	ax,ds:[2ch]		;Get program environment seg
		mov	environment_seg,ax	;  use unless SET cmd used.
		mov	bx,code_start_ptr	;Get starting code offset
		jmp	bx			;Jump to start of code.
data_start_ptr	dw	?			;Offset of data area.
init_code	endp
init_code_end	=	$

;----------------------------------------------------------------------------
;END CODE Routine appended at the end of all compiled programs.
;----------------------------------------------------------------------------
end_code_off	dw	0			;Pointer to offset in COM file
end_code_size	dw	offset end_code_end-offset end_code_start
end_code_next	dw	0			;Ptr to next routine to append
end_code_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
end_code_start	=	$

end_code	proc	near
		assume	cs:code,ds:code
		mov	ax,4c00h		;Terminate program
		int	21h
end_code	endp
end_code_end	=	$

;----------------------------------------------------------------------------
;ECHO MSG CODE Routine used print a string to the standard output device
; Entry:  DS:SI - offset of string to print.
;----------------------------------------------------------------------------
echo_msg_off	dw	0			;Pointer to offset in COM file
echo_msg_size	dw	offset echo_msg_ep‰Àˆ„Ïˆ€Žðpˆ€ˆò	€rGòrr@„Ï€O€ˆÏ„€‚ˆO€OŽ‡ˆ‰ðpˆ€€€r	€rGòrr@‚ˆO‚ÏƒˆO‰ˆˆ‰ŽŒOƒˆ	ˆÏÀ€ˆÏ€Ï„€‚ˆOŒˆ	ðpˆ€Žò@rIðpðpˆ€	ˆr„€rˆðpòrr@Œ	ˆA‰„@Œ	ˆppòrò	Á€Oòr@ÏŒ0¹0±º2¹…„´·1„¹´†…„·¹²626…„µ2„²±´7¯¶¹³¯˜†…„¶·»°´„„¨94·:1´0¹0±º2¹…„´·:™´…„µ6¸¹´7¹:2±´7¯¶¹³¯±·²2†…2±´7¯¶¹³¯˜…„¹2º…2±´7¯¶¹³¯±·²2„²·28…2±´7¯¶¹³¯²·2ž„’……–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-
;ECHO STATUS CODE Routine used to report the status of the echo flag.
; Entry:  AL - Echo flag.
;----------------------------------------------------------------------------
echo_stat_ptr	dw	0			;Pointer to offset in COM file
echo_stat_size	dw	offset echo_sta|_end-offset echo_stat_start
echo_stat_next	dw	0			;Ptr to next routine to append
echo_stat_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
echo_stat_start =	$

echo_stat_code	proc	near
		assume	cs:code,ds:code
		cal-----------------------
redirect_check	proc	near
		assume	cs:code,ds:code
		push	si
		call	redirect_close
		mov	quote_flag,0
redirect_1:
		mov	bp,si			;Save current position
		lodsb
		cmp	al,'"'                  ;See if quote
		jne	redirect_2
		not	quote_flag
redirect_2:
		cmp	quote_flag,0		;Ignore characters in quotes
		jne	redirect_1
;
; Check for piping
;
		cmp	al,'|'                  ;Check for piping symbol
		jne	redirect_5 
		mov	byte ptr [si-1],13	;Replace | with CR
 
		mov	bx,offset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ze	dw	offset getmember_end - offset getmember_start
getmember_next	dw	0			;Ptr to next routine to append
getmember_lnks	dw	2			;Bytes in the dependancy header
getmember_start =	$
		dw	offset scan4_next	;Offset of called routine.

getmember	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	getmember_0
getmember_0:
		pop	bx
getmember_1:
		xor	dl,dl
		call	getmember_scan4 	;Find next word
		jc	getmember_notfound
		dec	dh			;Dec parameter count
		jle	getmember_2
		inc	dl
		call	getmember_scan4 	;Find next space
		jc	getmember_exit
		jmp	short getmember_1	;If not done, loop back.
getmember_2:
		dec	si			;Backup to 1st char in word.
		clc
getmember_exit:
		pop	bx
		ret
getmember_notfound:
		stc
		jmp	short getmember_exit
getmember	endp
getmember_end	=	$

;-----------------------------------------------------------------------------
; PROCSTRING processes a string to convert any environment variables or
;	     command line variables.
; Entry:  DS:SI - ASCIIZ string to process
;	  ES:DI - pointer to output buffer.
; Exit:   DS:SI - pointer to beginning of new ASCIIZ string.
;	 [SI-1] - Length of new string.
;-----------------------------------------------------------------------------
procstr_cmdl	equ	[bx-8]
procstr_env	equ	[bx-6]
procstr_off	dw	0			;Pointer to offset in COM file
procstr_size	dw	offset procstr_end - offset procstr_start
procstr_next	dw	0			;Ptr to next routine to append
procstr_lnks	dw	4			;Bytes in the dependancy header

procstr_start	=	$
		dw	offset subparm_next	;Offset of called routines.
		dw	offset subenv_next

procstr_code	proc	near
		assume	cs:code,ds:code,es:code
		push	bx
		call	procstr_0
procstr_0:
		pop	bx
		push	di
		push	si
		mov	ah,BUFF_SIZE		;Set size of buffer
procstr_1:
		lodsb				;Get byte from string
		or	al,al			;Check for end of string
		je	procstr_exit
		cmp	al,"%"                  ;See if special character
procstr_jmp:
		je	procstr_3		;Yes, process special char.
procstr_2:
		stosb				;Store byte from string
		dec	ah
		jne	procstr_1
procstr_exit:
		xor	al,al			;Force zero byte end.
		stosb
		pop	si
		pop	di
		mov	bl,BUFF_SIZE
		sub	bl,ah
		mov	ds:[di-1],bl		;Store length of string
		pop	bx
		ret
;
;A percent sign has been found indicating a 'soft' parameter.  Three types of
;soft parameters are allowed; command line parameter, environment variable,
;and for loop parameter.
;
procstr_3:
		lodsb				;Get next character
		dec	cx
		cmp	al,"%"                  ;If double %, include one
		je	procstr_2		;  in string.
		cmp	al,7fh			;See if for loop var
		jne	procstr_32
		push	si
		mov	si,forloop_ptr		;Get ptr to for loop string
procstr_30:
		lodsb
		cmp	al,0			;If zero, end of string
		je	procstr_31
		stosb
		dec	ah			;Dec buffer size counter
		jne	procstr_30		;If buffer not full, continue
procstr_31:
		pop	si			;Get back source string pointer
		jmp	short procstr_4
procstr_32:
		mov	dh,al			;Copy and check to see if
		sub	dh,"0"                  ;  the next char is a number.
		jb	procstr_5		;  If so, assume a line
		cmp	dh,9			;  parameter.
		ja	procstr_5
		call	procstr_cmdl		;Call cmd line param routine
procstr_4:
		or	ah,ah
		jne	procstr_1
		jmp	short procstr_exit	;If at end of string, done
procstr_5:
		dec	si			;Backup to 1st character
		inc	cx
		call	procstr_env
		jmp	short procstr_4
procstr_code	endp
procstr_end	=	$

;-----------------------------------------------------------------------------
; SUBLINEPARAM substitutes a parameter from the command line.
; Entry:  ES:DI - pointer to buffer to copy the line parameter
;	     DH - binary number of the line parameter
;	     AH - size of buffer
; Exit:   ES:DI - pointer to byte after the parameter in the buffer
;	     CX - remaining length of the buffer
;-----------------------------------------------------------------------------
subparm_getmem	equ	[bx-6]
subparm_off	dw	0			;Pointer to offset in COM file
subparm_size	dw	offset subparm_end - offset subparm_start
subparm_next	dw	0			;Ptr to next routine to append
subparm_lnks	dw	2			;Bytes in the dependancy header
subparm_start	=	$
		dw	offset getmember_next	;Offset of called routine.

sublineparam	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	sublineparam_1
		db	"DOS2X",0		;Dummy %0 parameter for DOS 2x
sublineparam_1:
		pop	bx
		push	cx
		push	si
		push	ds
		mov	si,80h			;Get ptr to cmd line
		xor	cx,cx
		mov	cl,[si] 		;Get number of chars in buffer.
		inc	si			;Point to data
		add	dh,ds:shift_count	;Add in shift cou~t.
		or	dh,dh			;Check coupÏ€Ï‡ŽŽO€Oˆ‚‰Àðpòr€€ò‚ˆŽŽFDðpÀppÀ€Ï‡ŽŽˆÏÇÁÏŽˆÏ€O€O‚ÏƒˆOˆ€ÏŒ€Oˆ€ÏƒˆP70¶²7³:42897³—…†…„¸:¹´°¼„„©°»21:³32¹9´½24· ¤…„¶·»°´˜4„„£²º"'©;2¹9´··…„´·:™´„„¤³"'©<897³¹0¶70·277º…„±¶¸°¶„„4·:422·;9²³¶²·:*¹²†…„¸7¸°¼…„µ0„¹º±64·2¸0¹0¶¯˜˜„2:·7<70¶²4·9º2°²…„¶2°„¹´–-±<.„„„¨7´·::72:¶¶¼80¹0¶²º2¹…„µ6¸¹µÀ@çÂ@ÁDÇG"x8B@ÁDÇG##àx8y9Àù88y9ÀùÁ88y9y ÄÂ AÌ˜ÚBBNÑffset pipe1_file	;Get offset of pipe file to
		cmp	pipe_toggle,0		;  use.  Alternate files so
		jne	redirect_3 		;  that input and output files
		mov	bx,offset pipe2_file	;  don't get mixed up.
redirect_3:
		not	pipe_toggle
		xor	dx,dx			;If file not already used, 
		or 	dx,[bx]			;  load the filename into 
		jne	redirect_4 		;  the COM data buffer.
		mov	dx,di
		sub	dx,databuff_start	;Compute offset of filename
		mov	[bx],dx			;Save pointer to filename
		lea	si,[bx+2]		;Get address of filename
		mov	ah,1	
		call	copy_string		;Copy name to COM data buffer
		xor	al,al			;Terminate filename with zero
		stosb
redirect_4:
		mov	pipeout_file,dx
		mov	pipeout_flag,1
		xor	bx,bx			;Add output redirection code
		xor	ax,ax			;  to file.
		call	redirect_openo
		jmp	short redirect_exit	;Terminate line scan.
;
; Check for output redirection
;
redirect_5:
		cmp	al,'>'                  ;Check for redirect out
		jne	redirect_7
		xor	bx,bx
		cmp	byte ptr [si],'>'       ;Check for append redirect
		jne	redirect_6
		lodsb				;Remove 2nd >
		inc	bx
redirect_6:
		push	bx			;Save append flag
		xor	bl,bl
		call	scan4char		;Get filename
		mov	dx,di
		mov	ah,1			;Copy only one word
		call	copy_string
		xor	al,al			;Terminate name with zero
		stosb
		sub	dx,databuff_start	;Compute offset of filename
		pop	ax
		call	redirect_openo
		call	erase_redirect		;Erase redirect from line.
		jmp	short redirect_1
redirect_7:
		cmp	al,'<'                  ;Check for redirect in
		jne	redirect_10
		xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@n COM file
searchenv_size	dw	offset searchenv_end - offset searchenv_start
searchenv_next	dw	0		       ;Ptr to next routine to append
searchenv_lnks	dw	0
searchenv_start =	$

search_env	proc near
		assume	ds:nothing,es:nothing
		push	bx
		push	cx
		push	di
		push	es
		mov	es,environment_seg	;Get seg of environment blk
		xor	di,di			;Point ES:DI to environment.
		mov	bx,si			;Save pointer to var name
search_env_1:
		mov	si,bx			;Get back ptr to var name.
		mov	cx,dx			;Compare env var to var in
		repe	cmpsb			;  string.
		je	search_env_2		;Variable found, exit loop
		xor	al,al			;Find next environment var.
		mov	cx,-1			;Scan the entire segment.
		repne	scasb
		cmp	byte ptr es:[di],0	;If double zero, end of env
		jne	search_env_1		;  block. else, loop back.
search_env_not_found:
		mov	si,di			;Point SI to end of env data
		push	es
		pop	ds
		stc
		jmp	short search_env_exit
search_env_2:
;
;Environment variable found. Point DS:SI to the string.
;
		mov	si,di
		push	es			;DS:SI points to env string
		pop	ds
search_env_3:
		lodsb				;Move environment pointer past
		cmp	al,"="                  ;  the equals sign.
		jne	search_env_3
		cmp	byte ptr [si],0
		je	search_env_not_found
search_env_4:
		lodsb				;Move pointer to first
		or	al,al			;  non-space character.
		jb	search_env_5
		cmp	al," "
		jb	search_env_4
search_env_5:
		dec	si
		clc
search_env_exit:
		pop	es
		pop	di
		pop	cx
		pop	bx
		ret
search_env	endp
searchenv_end	=	$

;----------------------------------------------------------------------------
;EXTERNAL CMD  Routine used to launch programs from the COM file.
; Entry   DS:SI - Pointer to the ASCIIZ program name
;	  ES:DI - Pointer to the ASCIIZ command line tail
;----------------------------------------------------------------------------
extern_intcmd	equ	[bx-14]
extern_echomsg	equ	[bx-12]
extern_parspath equ	[bx-10]
extern_launch	equ	[bx-8]
extern_ifexits	equ	[bx-6]
extern_pathcnt	equ	[bx]
extern_path_var equ	[bx+1]
extern_file_ext equ	[bx+6]
extern_filename equ	[bx+15]
extern_filetail equ	[bx+17]
extern_lostmsg	equ	[bx+19]
extern_cmdparm	equ	[bx+46]
extern_pathflag	equ	[bx+49]
external_ptr	dw	0			;Pointer to offset in COM file
external_size	dw	offset external_end - offset external_start
external_next	dw	0			;Ptr to next routine to append
external_lnks	dw	10			;Bytes in the dependancy header ;Number of routines called
external_start	=	$
		dw	intcmd_next		;Call to launch COMMAND.COM
		dw	echo_msg_next		;Call to display string
		dw	parsepath_next		;Call to get part of path
		dw	launch_next		;Call execute file
		dw	ifexist_next		;Call to find file

external_code	proc	near
		assume	cs:code,ds:code
		push	bx
		call	external_0
		db	0			;Cnt to track path search.
		db	"PATH="
		db	"COMEXEBAT"
		dw	0			;Pointer to filename
		dw	0			;Pointer to command line tail
		db	"Bad command or file name",13,10,0
		db	"/C "
		db	0			;Flag for path search
external_0:
		pop	bx			;Get pointer to local vars.
		mov	extern_filename,si	;Save ptr to file name
		mov	extern_filetail,di	;Save ptr to command line tail
		mov	dx,exec_buff 		;Get pointer to free buffer.
		add	dx,4			;Make room for /C if needed.
		mov	byte ptr extern_pathflag,0
		mov	byte ptr extern_pathcnt,0
;Parse path to generate filename.
external_1:
		mov	di,dx			;Get ptr to start of buffer
		xor	cx,cx			;Check to see if we need to
		or	cl,extern_pathcnt	;  check the directorys in
		jne	external_19		;  the path.
;The first time through, parse the name without using the path.
		push	dx
		push	si
		xor	dx,dx			;Assume default drive
		cmp	byte ptr [si+1],':'	;See if drive specified	
		jne	external_11
		mov	dl,[si]
		and	dl,0dfh			;Set to upper case
		sub	dl,'@'			;Convert ASCII to number
		movsw				;Copy drive letter
		add	word ptr extern_filename,2  ;Don't use drive in path
external_11:
		cmp	byte ptr [si],'\'	;See if starting at root.
		je	external_110
		mov	al,'\'			;Start at root dir
		stosb
		push	si
		mov	si,di			;Get pointer to buffer.
		mov	ah,47h			;Get current directory
		int	21h
		pop	si
		xor	al,al
		mov	cx,64
		repne	scasb			;Find end of directory string
		dec	di
		cmp	byte ptr [di-1],'\'	;Unless at root dir, add
		je 	external_110
		mov	al,'\'
		stosb
external_110:
		xor	ax,ax
external_12:
		lodsb				;Get a byte
		cmp	ax,2e2eh		;See if we need to back up
		jne	external_13		;  one directory.
		std
		mov	al,'\'			;Scan backwards to erase	
		mov	cx,18			;  last directory.
		repne	scasb
		repne	scasb
		cld
		inc	di
		jmp	short external_12
external_13:
		stosb	
		mov	ah,al			;Copy last character.
		cmp	al,'\'			;See if name specifies a path
		jne	external_14
		inc	byte ptr extern_pathflag
external_14:
		cmp	al,0
		jne	external_12
external_15:
		dec	di			;Since DOS does not let the
		mov	dx,di			;  user specify the file ext.
		std				;  scan back to make sure one
		mov	cx,5			;  is not attached.
		mov	al,'.'
		repne   scasb	
		cld
		jne	external_16
		inc	di
		mov	dx,di
external_16:
		mov	di,dx
		pop	si
		pop	dx
		jmp	short external_4
;Parse the path string to search remaining directorys.
external_19:
		cmp	byte ptr extern_pathflag,0  ;If a complete path was
		jne	external_badcmd		    ;  specified, don't search
		mov	si,extern_filename	    ;  the path.
		call	extern_parspath
		jnc	external_2		;If we have checked all
external_badcmd:
		lea	si,extern_lostmsg	;  directories in the path,
		call	extern_echomsg		;  display file not found msg.
		jmp	short external_exit

;Append filename to the end of the path.
external_2:
		mov	cx,73			;Max length of filename
external_3:
		lodsb
		cmp	al,' '                  ;See if end of word
		jbe	external_4
		cmp	al,'.'                  ;See if end of filename
		je	external_4
		stosb
		loop	external_3
external_4:
		mov	al,'.'                  ;Append '.' to filename
		stosb
		lea	si,extern_file_ext	;Get pointer to extensions
		mov	cx,3			;3 extension types COM EXE BAT
external_5:
		movsw				;Append extension to filename
		movsb
		xor	al,aqòrr@ˆ€ŽˆO€ƒÏ€ˆ€ppòr€ðpòr€ò	ƒðpòr€òppòr€òðpòròA‰ƒòrr@Ï‡„Ï€O„ŽÏ€Ï€Žppòròˆ€€ˆ‚„rr@ŒÏˆ€Ïˆ‚ˆppòr€òðpòr€òppòr€ò	ƒðpòr€Œrˆ€Ž€@ðpòrò	‚AÁòrr@ŽÏ€Oˆ‚ˆOˆˆ„ðpòr€òˆ€Ž€@ppòrŒr‚ˆO‡„Ïˆˆ€€ŽƒŒÏÀ€O‚ÏƒˆO€ˆÏ‡ŽƒÏ„„ðpòr€òˆ€Ž€Fppˆˆ€Ž€@ÀðpòròÁÆrrr@P4„gÎOçÄÀÄBx8y9@D9ÀÄ@GÀ!ø8y9Ä9 ÄÀÄ@@ÃÇ@9 çÃÀ@ÄgÀÀ'à88y9yÁàâ88y9yÁ ÄÁù99 çÃÀÂgÀÀ'ÂÂAD'Æ@DDx8y9B9999 	xor	bl,bl
		call	scan4char		;Get filename
		mpò	ƒÁ‰‚ppòròÁÆrrr@‚O€‚O€ˆO€‰ðpòrò‚„‚ˆppòr€òÁŽòrr@ˆ€ŽˆO€ŽO€ƒÏ€ˆ€ppòr€ðpòrò	ƒÁ‰ŽŽ€ˆ€Žò@€ˆO€ˆ„Ï€Ïˆ‚ˆŽppòròˆ	‚ˆ€ˆ‚ppòròŽˆ	‚ˆòr@ŽO„ˆ	‚ˆÏˆ„€O‘‚ˆ@ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€FGÀðpòròÁÆDppòrˆrˆ	‚ˆ€‚ðpòr€òˆ	‚ˆ€Fpp„ˆ	‚ˆ€‚ÀðpòrŒppòr€òppòrˆðp„ˆ	‚ˆ€ˆr‰‡ðpðpÀ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@to next routine to append
parsepath_lnks	dw	2
parsepath_start =	$
		dw	offset searchenv_next	 ;Call to search env block

parsepath	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	parsepath_1
		db	"PATH"
parsepath_1:
		pop	bx		
		push	dx
		push	si
		push	ds
		mov	dx,4			;Length of PATH string
		mov	si,bx			;Point SI to PATH string
		call	getcom_srchenv		;PATH var ptr return in DS:SI
parsepath_2:
		dec	cx			;Dec path segment count
		jcxz	parsepath_4
parsepath_3:
		lodsb				;Get character
		or	al,al			;See if end of path string
		je	parsepath_notfound
		cmp	al,';'                  ;See if end of path segment
		jne	parsepath_3
		jmp	short parsepath_2
parsepath_4:
		lodsb
		cmp	al,';'                  ;See if end of path segment
		je	parsepath_5
		or	al,al			;See if end of path
		je	parsepath_5
		stosb
		jmp	short parsepath_4
parsepath_5:
		push	cs
		pop	ds
		cmp	byte ptr es:[di-1],'\'  ;Append \ if necessary.
		je	parsepath_6
		mov	al,'\'
		stosb
parsepath_6:
		clc
parsepath_exit:
		pop	ds
		pop	si
		pop	dx
		pop	bx
		ret
parsepath_notfound:
		stc
		jmp	short parsepath_exit
parsepath	endp
parsepath_end	  =	  $

;-----------------------------------------------------------------------------
; INTCMD  Launches the shell ,usually COMMAND.COM, to run an internal command.
; Entry   DS:SI - pointer to the ASCIIZ internal command to run.
;-----------------------------------------------------------------------------
intcmd_launch	equ    [bx-8]
intcmd_getcom	equ    [bx-6]
intcmd_off	dw     0			;Pointer to offset in COM file
intcmd_size	dw     offset intcmd_end - offset intcmd_start
intcmd_next	dw     0			;Ptr to next routine to append
intcmd_lnks	dw     4
intcmd_start	=      $
		dw     offset launch_next	;Call to load and run program.
		dw     offset getcom_next	;Call to find shell name.

intcommand	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	intcmd_1
intcmd_1:
		pop	bx			;Get pointer to sub calls.
		mov	di,si			;Copy ptr to command
		dec	di			;Back up to cmd line size.
		mov	al,process_rc		;Get and save return code
		push	ds
		push	ax
		call	intcmd_getcom		;Get comspec string.
		call	cs:intcmd_launch	;Run program.
		pop	ax
		pop	ds
		mov	process_rc,al		;Restore return code	
		pop	bx
		ret
intcommand	endp
intcmd_end	=	$

;-----------------------------------------------------------------------------
; GETCOMSPEC  Gets the name of the shell program running
; Exit:   DS:SI - pointer to the ASCIIZ name of the shell porgram.
;-----------------------------------------------------------------------------
getcom_srchenv	equ	[bx-6]
getcom_off	dw	0			 ;Pointer to offset in COM file
getcom_size	dw	offset getcom_end - offset getcom_start
getcom_next	dw	0			 ;Ptr to next routine to append
getcom_lnks	dw	2
getcom_start	=	$
		dw	offset searchenv_next	 ;Call to search environment blk

getcomspec	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	getcom_1
getcom_str	db	"COMSPEC"
getcom_1:
		pop	bx
		mov	dx,offset getcom_1 - offset getcom_str
		mov	si,bx
		call	getcom_srchenv		;Get pointer to comspec string
		pop	bx
		ret
getcomspec	endp
getcom_end	=	$

;----------------------------------------------------------------------------
;LAUNCH PROG  Routine used to load and run programs.
; Entry   DS:SI - pointer to the program name to run.
;	  ES:DI - pointer to the command line tail.
; Exit	  return code variable set.
;----------------------------------------------------------------------------
launch_fcb1	equ	[bx]
launch_fcb2	equ	[bx+16]
launch_ptr	dw	0			;Pointer to offset in COM file
launch_size	dw	offset launch_end - offset launch_start
launch_next	dw	0			;Ptr to next routine to append
launch_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
launch_start	=	$

launch_code	proc	near
		assume	cs:code,ss:code
		push	bx
		call	launch_1		;Push IP on stack
		db	0			;Dummy FCB for program
		db	"DUMMY   FCB"
		db	0,0,0,0
		db	0			;Dummy FCB for program
		db	"DUMMY   FCB"
		db	0,0,0,0
launch_1:
		pop	bx			;Get pointer to local vars.

		push	ds
		push	es			;Save stack ptr since it is not
		mov	com_stack_ptr,sp	;  saved under DOS 2.x.
;Parse first two parameters into FCBs.
		push	di			;Save ptr to tail
		push	si			;Save ptr to name
		mov	si,di			;Copy ptr to cmd line
		inc	si			;Skip size byte
		lea	di,launch_fcb1
		mov	ax,2903h		;Parse FCB
		int	21h
		lea	di,launch_fcb2
		mov	ax,2903h
		int	21h
		pop	si
		pop	di

		push	cs			;Create Parameter block on
		lea	dx,launch_fcb2		;  the stack. Start with 2nd
		push	dx			;  FCB.
		push	cs
		lea	dx,launch_fcb1
		push	dx
		push	cs			;Push ptr to command line tail.
		push	di
		mov	ax,cs:[environment_seg]	;Use env block set by prog.
		push	ax
		mov	bx,sp			;Copy pointer to parameter blk

		mov	ax,4b00h		;DOS EXEC program.
		mov	dx,si			;Get pointer to filename
		int	21h
		mov	bp,offset data_start_ptr - offset init_code + 100h
		mov	bp,cs:[bp]
		mov	bx,cs			;Reload BP to access local data
		cli
		mov	ss,bx			;Restore stack
		mov	sp,com_stack_ptr
		sti
		cld				;Restore default direction
		pop	es
		pop	ds
;Get return code
		mov	ah,4dh			;Get return code
		int	21h
		mov	process_rc,al		;Save
		pop	bx
		ret
launch_code	endp
launch_end    =       $

;----------------------------------------------------------------------------
;GOTO  Routine to jump to a label pointed to by a cmd line parameter or
;      environment variable.
; Entry   DS:SI - pointer to label to find.
;            DI - Wait flag/ptr. If <> 0, put goto address at pointer.
; Exit	  This routine does not return unless the label is not found.
;----------------------------------------------------------------------------
goto_echomsg	equ	[bx-7]
goto_lblsrch	equ	[bx-5]
goto_ptr	dw	0			;Pointer to offset in COM file
goto_size	dw	offset goto_end - offset goto_start
goto_next	dw	0			;Ptr to next routine to append
goto_lnks	dw	4			;Bytes in the dependancy header ;Number of routines called
goto_start	=	$
		dw	echo_msg_next		;Used to print error msg
		dw	lblsrch_next		;Used to find label

goto_code	proc	near
		assume	cs:code,ds:code
		call	goto_1
		db	8 dup (" ")
		db	" Label not found",13,10,0
goto_1:
		pop	bx
		push	di			;Save wait flag/pointer
		mov	di,bx			;Load label into error msg
		xor	dx,dx			;Get size of label as it is
		mov	cx,8			;  copied into error msg.
goto_2:
		lodsb
		cmp	al," "
		jbe	goto_4
		cmp	al,'a'
		jb	goto_3
		and	al,0dfh 		;Capitalize label
		cmp	al,'Z'
		ja	goto_4
goto_3:
		stosb
		inc	dx
		loop	goto_2
gotpšàx8y9yàÄÁù99 çÂD'ÀgÀÇDø8y9y ÆAù99 çÃÀ@ÄgÀÀ'ÀÇDø8y9yÁ ÆÇDÀÇ@ù çÃÀÂgÀÀ'ÂÇ@çÀgÀÇDçÀÁø8y9ÄùÁ ÆCù99 ÄçÀDBçÀgÄÇÇ88y9ÀùAù99 D'ÃÀÂgÀÀ'À88y9ÀùÀÆ@ÂFù9 ÀçÂFçÂ@ÁD88y9ÀùÁù99 DÀD'ÃÀÂgÀÀ'À88y9Àù999 DÀD'ÀçÄ@Ç À@Äx8y9F9À 999 gÂàçÀÇDçÀ@çÄ@Dàx8y9yàÀAà"@99 çÄÄÁGÁgÃÀÂx8y9y Â999 'ÁgÄÄÇ'Ä@Ç'ààgãø8y9D9À ã999 eader ;Number of routines called
gotodly_start	=	$

gotodly_code	proc	near
		assume	cs:code,ds:code
		xor	ax,ax			;Get pointer, test if zero.
		or	ax,[si]			;If zero, return.  If not
		je	gotodly_exit		;  push the new destination
		pop	bx			;  on the stack and return.
		push	ax
gotodly_exit:
		ret
gotodly_code    endp
gotodly_end     =	     $

;----------------------------------------------------------------------------
;LABEL SEARCH Routine to search list of labels to determine goto destination.
; Entry   ES:DI - pointer to the first entry in the list. (Assume ES = DS)
;	  DS:SI - pointer to label to find.
;	     CX - Length of label
; Exit	     BX - pointer to matching list entry, or last entry if not found.
;	     CF - Set if label not found.
;----------------------------------------------------------------------------
lblsrch_ptr	dw	0			;Pointer to offset in COM file
lblsrch_size	dw	offset lblsrch_end - offset lblsrch_start
lblsrch_next	dw	0			;Ptr to next routine to append
lblsrch_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
lblsrch_start	=	$

lblsrch_code	proc	near
		assume	cs:code,ds:code
		push	si
		mov	dx,si			;Save ptr to label
		mov	ax,cx			;Save label length
		mov	bx,di			;Get ptr to list
lblsrch_1:
		mov	di,bx
		add	di,4			;Move to label string
		mov	si,dx			;Get ptr to new label
		mov	cx,ax			;Get length of label
		cmp	[di],cl 		;Compare lengths of the labels
		jne	lblsrch_2
		inc	di			;Skip past length byte
		repe	cmpsb			;Compare labels.
		je	lblsrch_4
lblsrch_2:
		cmp	word ptr [bx],-1	;See if at end of list
		je	lblsrch_3
		add	bx,[bx] 		;Point to next label
		jmp	short lblsrch_1
lblsrch_3:
		stc				;No label found.
		jmp	short lblsrch_exit
lblsrch_4:
		clc				;Label found
lblsrch_exit:
		pop	si
		ret
lblsrch_code	endp
lblsrch_end	=	$

;-----------------------------------------------------------------------------
; IFEQUAL  Compares two strings.
; Entry:  DS:SI - Pointer to first ASCIIZ String
;	  ES:DI - Pointer to second ASCIIZ String
;	     CX - Length of strings.
; Exit:      CF - Clear if equal
;-----------------------------------------------------------------------------
ifequal_off	dw     0			;Pointer to offset in COM file
ifequal_size	dw     offset ifequal_end - offset ifequal_start
ifequal_next	dw     0			;Ptr to next routine to append
ifequal_lnks	dw     0
ifequal_start	=      $

ifequal 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		xor	cx,cx
		mov	cl,[si-1]		;Get size of string
		cmp	cl,[di-1]		;Compare sizes of strings
		jne	ifequal_notequal
		rep	cmpsb			;Compare strings
		jne	ifequal_notequal
		clc
		ret
ifequal_notequal:
		stc
		ret
ifequal 	endp
ifequal_end	=	$

;-----------------------------------------------------------------------------
; IFEXIST  Determines if a file exists.
; Entry:  DS:SI - Pointer to ASCIIZ filename.
;	     DI - Pointer to buffer for disk transfer area.
; Exit:      CF - Clear if file exists
;	     DI - IF file exists, points to filename.
;-----------------------------------------------------------------------------
ifexist_off	dw     0			;Pointer to offset in COM file
ifexist_size	dw     offset ifexist_end - offset ifexist_start
ifexist_next	dw     0			;Ptr to next routine to append
ifexist_lnks	dw     0
ifexist_start	=      $

ifexist 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		mov	dx,di
		mov	ah,1ah			;Set DTA
		int	21h

		mov	dx,si			;Copy pointer to filename
		xor	cx,cx			;Normal attributes
		mov	ah,4eh			;DOS Find First
		int	21h
		jc	ifexist_exit
		add	di,1eh			;Point DI to filename in DTA
		clc
ifexist_exit:
		ret
ifexist 	endp
ifexist_end	=	$

;-----------------------------------------------------------------------------
; IFERRLEV  Compares the value passed with the return code of the last
;	    program executed.
; Entry:     SI - Pointer to ASCIIZ Error level.
; Exit:      CF - Clear if process error level above or equal to SI
;-----------------------------------------------------------------------------
iferrlev_off	dw     0			;Pointer to offset in COM file
iferrlev_size	dw     offset iferrlev_end - offset iferrlev_start
iferrlev_next	dw     0			;Ptr to next routine to append
iferrlev_lnks	dw     0
iferrlev_start	=      $

iferrlev	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		xor	ax,ax			;Convert ASCIIZ number into
		mov	bx,ax			;  decimal
iferrlev_1:
		mov	bl,[si] 		;Convert error level number
		sub	bl,'0'                  ;  from ASCII to decimal.
		jb	iferrlev_2
		cmp	bl,9
		ja	iferrlev_2
		mov	dx,10
		mul	dx
		jc	iferrlev_2
		add	ax,bx			;Add in digit
		inc	si
		jmp	short iferrlev_1
iferrlev_2:
		cmp	process_rc,al		;Compare to last program
		ret
iferrlev	endp
iferrlev_end	=	$

;-----------------------------------------------------------------------------
; FORLOOP  Processes commands in a FOR loop.
; Entry:     SI - Pointer to ASCIIZ set of parameters.
;	     DI - Pointer loop structure in data area
;		  LoopCnt	db	 0	Member of the set to use
;		  FirstFlag	db	 0	Indicates Find first/next
; Exit:      CF - Set if FOR loop complete.
;-----------------------------------------------------------------------------
forloop_getmem	equ	[bx-6]
forloop_savchr	equ	[bx]
forloop_eptr	equ	[bx+1]
forloop_dta   	equ	[bx+3]
forloop_off	dw	0			;Pointer to offset in COM file
forloop_size	dw	offset forloop_end - offset forloop_start
forloop_next	dw	0			;Ptr to next routine to append
forloop_lnks	dw	2
forloop_start	=	$
		dw	getmember_next

forloop 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	forloop_0
		db	0			;Saved termaination character
		dw	0			;Ptr to term character address
		db	43 dup (0)		;Used for DTA
forloop_0:
		pop	bx
		push	si
		mov	si,forloop_eptr
		or	si,si
		je	forloop_01
		mov	al,forloop_savchr
		mov	byte ptr [si-1],al 	;Remove zero terminator
forloop_01:
		pop	si
;
;Set DTA. If 2nd time or later time looping on a member, use find next to
;determine the string for this time through the loop.
;
		lea	dx,forloop_dta		
		mov	ah,1ah			;Set DTA
		int	21h

		cmp	byte ptr [di+1],0	;See if first time for member
		je	forloop_1
		mov	ah,4fh			;Find next file
		int	21h
		jc	forloop_1		;Not found, go to next member
		lea	dx,[bx+21h]		;Point to filename in DTA
		jmp	short forloop_notdone	;Execute loop body
;
;First time with this member of the set, if wildcards are in the member,
;use DOS find first to get the loop string.
;
forloop_1:
		mov	byte ptr [di+1],0	;Clear first/next flag
		inc	byte ptr [di]		;Look at next member of the set
		mov	dh,[di] 		;Get loop count
		call	forloop_getmem		;Get member of set.  If no
		jc	forloop_done		;  more members, loop done.
		mov	dx,si			;Save pointer to member
		xor	ah,ah			;Clear wildcard flag
forloop_2:
		lodsb				;Scan set member to check for
		cmp	al,' '                  ;  any wildcard chars.
		jbe	forloop_4
		cmp	al,'?'
		jne	forloop_3
		inc	ah			;Set wildcard found flag
forloop_3:
		cmp	al,'*'
		jne	forloop_2
		inc	ah			;Set wildcard found flag
		jmp	short forloop_2
forloop_4:
		xor	al,al
		xchg	byte ptr [si-1],al	;Set zero terminator.
		mov	forloop_savchr,al	;Save char over written
		or	ah,ah			;If no wildcards, execute
		je	forloop_notdone 	;Execute loop body
		mov	byte ptr [di+1],1	;Set first/next flag
		xor	cx,cx			;Normal attributes
		mov	ah,4eh			;DOS find first
		int	21h
		jc	forloop_1		;If not found, get next member
		lea	dx,[bx+1eh]		;Set ptr to filename in DTA
forloop_notdone:
		mov	forloop_ptr,dx		;Set loop data ptr
		mov	forloop_eptr,si
		clc
forloop_exit:
		pop	bx
		ret
forloop_done:
		mov	word ptr [di],0 	;Clear loop variables
		mov	word ptr forloop_eptr,0
		stc
		jmp	short forloop_exit
forloop 	endp
forloop_end	=	$

;-----------------------------------------------------------------------------
; FINDENV  Finds the master environment block.
; Exit:    Variable Environment_seg set with segment of master environment.
;-----------------------------------------------------------------------------
findenv_dosver	equ	[bx+7]
findenv_cmdname equ	[bx]
findenv_off	dw	0			 ;Pointer to offset in COM file
findenv_size	dw	offset findenv_end - offset findenv_start
findenv_next	dw	0			 ;Ptr to next routine to append
fip‰Š·;/¶75¹„²;„˜…34·22·;/¹º0¹:ž„’……34·22·;¸97±„·2°¹…„°¹¹º¶²„±¹1·²2–291¸Ä Ä FÄ Â FÄ88y9ÀùAø8y9ÀùA™[—ÌBƒB‚BY‚HÓÓSPS‘ƒB‚BYÂLB™š[™[—ÌƒB‚B\ÜXžB‚B\\ÚY\ÃBƒB‚B[[Ý‚X^Î–Ì˜ÚBBNÑÙ]Y˜][[ƒB‚BXÛ\Y[š\›Û›Y[ÜÙYË^NÐÚXÚÈÈÙYHYˆ[™XYCB‚BZ›™BYš[™[—Ú›tion.
; Entry   ES:DI - pointer to the first entry in the list. (Assume ES = DS)
;	  DS:SI - pointer to label to find.
;	     CX - Length of label
; Exit	     BX - pointer to matching list entry, or last entry if not found.
;	     CF - Set if label not found.
;----------------------------------------------------------------------------
lblsrch_ptr	dw	0			;Pointer to offset in COM file
lblsrch_size	dw	offset lblsrch_end - offset lblsrch_start
lblsrch_next	dw	0			;Ptr to next routine to append
lblsrch_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
lblsrch_start	=	$

lblsrch_code	proc	near
		assume	cs:code,ds:code
		push	si
		mov	dx,si			;Save ptr to label
		mov	ax,cx			;Save label length
		mov	bx,di			;Get ptr to list
lblsrch_1:
		mov	di,bx
		add	di,4			;Move to label string
		mov	si,dx			;Get ptr to new label
		mov	cx,ax			;Get length of label
		cmp	[di],cl 		;Compare lengths of the labels
		jne	lblsrch_2
		inc	di			;Skip past length byte
		repe	cmpsb			;Compare labels.
		je	lblsrch_4
lblsrch_2:
		cmp	word ptr [bx],-1	;See if at end of list
		je	lblsrch_3
		add	bx,[bx] 		;Point to next label
		jmp	short lblsrch_1
lblsrch_3:
		stc				;No label found.
		jmp	short lblsrch_exit
lblsrch_4:
		clc				;Label found
lblsrch_exit:
		pop	si
		ret
lblsrch_code	endp
lblsrch_end	=	$

;-----------------------------------------------------------------------------
; IFEQUAL  Compares two strings.
; Entry:  DS:SI - Pointer to first ASCIIZ String
;	  ES:DI - Pointer to second ASCIIZ String
;	     CX - Length of strings.
; Exit:      CF - Clear if equal
;-----------------------------------------------------------------------------
ifequal_off	dw     0			;Pointer to offset in COM file
ifequal_size	dw     offset ifequal_end - offset ifequal_start
ifequal_next	dw     0			;Ptr to next routine to append
ifequal_lnks	dw     0
ifequal_start	=      $

ifequal 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		xor	cx,cx
		mov	cl,[si-1]		;Get size of string
		cmp	cl,[di-1]		;Compare sizes of strings
		jne	ifequal_notequal
		rep	cmpsb			;Compare strings
		jne	ifequal_notequal
		clc
		ret
ifequal_notequal:
		stc
		ret
ifequal 	endp
ifequal_end	=	$

;-----------------------------------------------------------------------------
; IFEXIST  Determines if a file exists.
; Entry:  DS:SI - Pointer to ASCIIZ filename.
;	     DI - Pointer to buffer for disk transfer area.
; Exit:      CF - Clear if file exists
;	     DI - IF file exists, points to filename.
;-----------------------------------------------------------------------------
ifexist_off	dw     0			;Pointer to offset in COM file
ifexist_size	dw     offset ifexist_end - offset ifexist_start
ifexist_next	dw     0			;Ptr to next routine to append
ifexist_lnks	dw     0
ifexist_start	=      $

ifexist 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		mov	dx,di
		mov	ah,1ah			;Set DTA
		int	21h

		mov	dx,si			;Copy pointer to filename
		xor	cx,cx			;Normal attributes
		mov	ah,4eh			;DOS Find First
		int	21h
		jc	ifexist_exit
		add	di,1eh			;Point DI to filename in DTA
		clc
ifexist_exit:
		ret
ifexist 	endp
ifexist_end	=	$

;-----------------------------------------------------------------------------
; IFERRLEV  Compares the value passed with the return code of the last
;	    program executed.
; Entry:     SI - Pointer to ASCIIZ Error level.
; Exit:      CF - Clear if process error level above or equal to SI
;-----------------------------------------------------------------------------
iferrlev_off	dw     0			;Pointer to offset in COM file
iferrlev_size	dw     offset iferrlev_end - offset iferrlev_start
iferrlev_next	dw     0			;Ptr to next routine to append
iferrlev_lnks	dw     0
iferrlev_start	=      $

iferrlev	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		xor	ax,ax			;Convert ASCIIZ number into
		mov	bx,ax			;  decimal
iferrlev_1:
		mov	bl,[si] 		;Convert error level number
		sub	bl,'0'                  ;  from ASCII to decimal.
		jb	iferrlev_2
		cmp	bl,9
		ja	iferrlev_2
		mov	dx,10
		mul	dx
		jc	iferrlev_2
		add	ax,bx			;Add in digit
		inc	si
		jmp	short iferrlev_1
iferrlev_2:
		cmp	process_rc,al		;Compare to last program
		ret
iferrlev	endp
iferrlev_end	=	$

;-----------------------------------------------------------------------------
; FORLOOP  Processes commands in a FOR loop.
; Entry:     SI - Pointer to ASCIIZ set of parameters.
;	     DI - Pointer loop structure in data area
;		  LoopCnt	db	 0	Member of the set to use
;		  FirstFlag	db	 0	Indicates Find first/next
; Exit:      CF - Set if FOR loop complete.
;-----------------------------------------------------------------------------
forloop_getmem	equ	[bx-6]
forloop_savchr	equ	[bx]
forloop_eptr	equ	[bx+1]
forloop_dta   	equ	[bx+3]
forloop_off	dw	0			;Pointer to offset in COM file
forloop_size	dw	offset forloop_end - offset forloop_start
forloop_next	dw	0			;Ptr to next routine to append
forloop_lnks	dw	2
forloop_start	=	$
		dw	getmember_next

forloop 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	forloop_0
		db	0			;Saved termaination character
		dw	0			;Ptr to term character address
		db	43 dup (0)		;Used for DTA
forloop_0:
		pop	bx
		push	si
		mov	si,forloop_eptr
		or	si,si
		je	forloop_01
		mov	al,forloop_savchr
		mov	byte ptr [si-1],al 	;Remove zero terminator
forloop_01:
		pop	si
;
;Set DTA. If 2nd time or later time looping on a member, use find next to
;determine the string for this time through the loop.
;
		lea	dx,forloop_dta		
		mov	ah,1ah			;Set DTA
		int	21h

		cmp	byte ptr [di+1],0	;See if first time for member
		je	forloop_1
		mov	ah,4fh			;Find next file
		int	21h
		jc	forloop_1		;Not found, go to next member
		lea	dx,[bx+21h]		;Point to filename in DTA
		jmp	short forloop_notdone	;Execute loop body
;
;First time with this member of the set, if wildcards are in the member,
;use DOS find first to get the loop string.
;
forloop_1:
		mov	byte ptr [di+1],0	;Clear first/next flag
		inc	byte ptr [di]		;Look at next member of the set
		mov	dh,[di] 		;Get loop count
		call	forloop_getmem		;Get member of set.  If no
		jc	forloop_done		;  more members, loop done.
		mov	dx,si			;Save pointer to member
		xor	ah,ah			;Clear wildcard flag
forloop_2:
		lodsb				;Scan set member to check for
		cmp	al,' '                  ;  any wildcard chars.
		jbe	forloop_4
		cmp	al,'?'
		jne	forloop_3
		inc	ah			;Set wildcard found flag
forloop_3:
		cmp	al,'*'
		jne	forloop_2
		inc	ah			;Set wildcard found flag
		jmp	short forloop_2
forloop_4:
		xor	al,al
		xchg	byte ptr [si-1],al	;Set zero terminator.
		mov	forloop_savchr,al	;Save char over written
		or	ah,ah			;If no wildcards, execute
		je	forloop_notdone 	;Execute loop body
		mov	byte ptr [di+1],1	;Set first/next flag
		xor	cx,cx			;Normal attributes
		mov	ah,4eh			;DOS find first
		int	21h
		jc	forloop_1		;If not found, get next member
		lea	dx,[bx+1eh]		;Set ptr to filename in DTA
forloop_notdone:
		mov	forloop_ptr,dx		;Set loop data ptr
		mov	forloop_eptr,si
		clc
forloop_exit:
		pop	bx
		ret
forloop_done:
		mov	word ptr [di],0 	;Clear loop variables
		mov	word ptr forloop_eptr,0
		stc
		jmp	short forloop_exit
forloop 	endp
forloop_end	=	$

;-----------------------------------------------------------------------------
; FINDENV  Finds the master environment block.
; Exit:    Variable Environment_seg set with segment of master environment.
;-----------------------------------------------------------------------------
f