		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 switches
cmd_switch_end	=	$			;  needed.

last_routine	dw	offset init_code_next	;Last canned routine used.

codebuff_start	dw	0			;Buffer to construct the code
codebuff_ptr	dd	0			;  image of the COM file.

databuff_start	dw	?			;Buffer to hold the data
databuff_end	dw	?			;  image of the COM file.

firstlabel	dw	-1			;Ptr to 1st label in COM file

outbuff_ptr	dd	0			;Buffer to hold the final
outbuff_end	dw	0			;  image of the COM file.

quote_flag	db	0			;Disable translate flag
redirect_in	db	0			;Input redirection flag
redirect_out	db	0			;Output redirection flag

pipe_toggle	db	0			;Piping file flag
pipein_file	dw	0
pipein_flag	db	0
pipeout_file	dw	0
pipeout_flag	db	0
pipe1_file	dw	0
		db	"T^M^P_$1.!!!",0	;Name of piping file 1
pipe2_file	dw	0
		db	"T^M^P_$2.!!!",0	;Name of piping file 2

inbuff_ptr	dw	offset end_of_code+512	;Buffer for BAT file
inbuff_size	dw	1024 			;Size of input buffer

file_handle	dw	-1			;Handle of BAT file
file_linecount	dw	0			;Line number being processed.

com_string	db	".COM",0		;Extension for output file
outfile_name	db	13 dup (" ")		;Name of output file.

filemsg1	db	13,10,"Error in line $" ;File identification message.

errmsg0 	db	"Need DOS 2.0 or greater$"
errmsg1 	db	13,10,"Syntax: BAT2EXEC filename.ext",13,10,"$"
errmsg2 	db	"Can",39,"t find input file$"
errmsg3 	db	"Not enough memory$"
errmsg4 	db	"No input file specified$"
errmsg5 	db	"COM file size too big$"
errmsg6 	db	"Syntax error$"
errmsg7 	db	"Compiler data buffer full$"
errmsg8 	db	"Label defined more than once$"
errmsg9 	db	"Label "
errmsg9labl	db	8 dup (" ")
		db	"not found$"
errmsg10	db	"Illegal disk specified$"
errmsg11	db	"FOR loops cannot be nested$"

endmsg		db	13,10,"$"

;----------------------------------------------------------------------------
; Start of compiler code.
;----------------------------------------------------------------------------
initialize	proc	near
		assume	cs:code,ds:code,es:code
		mov	dx,offset program	;Display copyright message.
		mov	ah,9
		int	21h
		cld				;Set string operations 'up.'
		mov	ah,30h			;Get DOS version, run only
		int	21h			;  if 2.0 or greater.
		xchg	al,ah			;Swap major, minor numbers
		mov	dx,offset errmsg0	;Bad DOS version
		cmp	ah,2
		jb	jmp_disp_error

		mov	sp,offset end_of_code + 512	;Move stack
		mov	ah,4ah				;Reduce memory
		mov	bx,1000h			; allocation to 64K
		int	21h

		mov	ax,inbuff_ptr		;Compute start of buffer for
		add	ax,inbuff_size		;  COM file data.
		mov	databuff_start,ax

		mov	ah,48h			;Allocate memory block for
		mov	bx,1000h		;  intermediate buffer.
		int	21h
		mov	dx,offset errmsg3	;Not enough memory msg
		jc	jmp_disp_error
		mov	word ptr codebuff_ptr[2],ax	;Save segment.

		mov	ah,48h			;Allocate memory block for
		mov	bx,1000h		;  output file buffer.
		int	21h
		jc	jmp_disp_error
		mov	word ptr outbuff_ptr[2],ax	;Save segment.
;
;Parse the command line for switches.
;
		mov	si,offset command_tail
		xor	cx,cx
		or	cl,[si] 		;Get command line length
		je 	init_0		      	;If zero, report no filename
		inc	si
parse_cmdline_l1:
		xor	bl,bl			;Search for the next
		call	scan4char		;  non-space character.
		jnc	init_1		      	;If zero, report no filename
init_0:
		mov	dx,offset errmsg1
		jmp	short jmp_disp_error	;If none, report no filename
init_1:
		mov	al,[si]
		cmp	al,"/"                  ;See if command switch
		je	parse_error
		call	loadbatfile		;Load input BAT file
		jc	jmp_disp_error
		jmp	short parse_cmdline_end
parse_error:
		mov	dx,offset errmsg2
jmp_disp_error:
		jmp	disp_error
parse_cmdline_end:
;
;Compile BAT file line by line.
;
		mov	si,inbuff_ptr		;SI points to input BAT file
		mov	di,databuff_start	;DI points to COM file data.
		add	di,std_data_size	;Make room for 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 word in the line.
		jc 	jmp_disp_error		
		jmp	short new_line
comp_endcheck:
		cmp	al,13			;See if carrage return, if so
		je	new_line		;  continue.
		cmp	file_handle,-1		;If at end of buffer, check
		je	comp_end		;  to see if any more of the
		call	loadbatfile		;  file to read.
		jc	jmp_disp_error
		mov	si,inbuff_ptr		;Reset SI to start of buffer.
		jmp	short new_line1		
comp_end:
;
;Inline code complete, append terminate code.
;
		call	redirect_close
		mov	databuff_end,di 	;Save ptr to end of data.
		les	di,codebuff_ptr
		assume	es:nothing
		mov	si,offset end_code	;Append terminate code
		mov	cx,offset end_code_end-offset end_code
		rep	movsb
		mov	word ptr codebuff_ptr,di ;Save ptr to end of COM code.
;
;File processed. Assemble data and code into one routine.  Start by
;stringing the canned routines together using the links set by the
;INCLUDE routine.
;
		les	di,outbuff_ptr		 ;Get ptr to output buffer
		mov	si,offset init_code_next ;  for COM file.
file_1: 	mov	cx,[si-2]		;Get size of routine
		push	[si]			;Save pointer to next routine
		add	si,4			;Skip past number of links
		rep	movsb			;Copy routine to COM file.
		pop	si
		or	si,si			;See if last routine
		jne	file_1			;No, loop back.
;
;Save starting offset of data to be used by the COM program.
;
		mov	bp,offset data_start_ptr-offset init_code
		mov	ax,100h 		;AX = end pointer
		add	ax,di			;Add length of canned routines.
		mov	es:[bp],ax		;Save data start pointer
		mov	bp,di			;Use BP as starting data offset

		mov	si,databuff_start	;Compute length of COM data
		mov	cx,databuff_end 	;  plus the canned routines.
		sub	cx,si
		add	ax,cx			;Add data length to end ptr
;
;Now that we have the length of the canned routines and the data, we can
;compute the starting offset of the inline code.  Use this number to adjust
;the jump table entries in the label list.  While fixing offsets check to
;see that all labels have code offsets, if not, print no label error msg.
;When complete, append data to file.
;
		mov	bx,offset firstlabel	;Get pointer to the first label
		mov	dx,[bx] 		;Get offset to 1st label entry
		add	dx,bx			;Add in BX to get absolute off
file_11:
		cmp	word ptr [bx],-1	;See if at end of label list.
		je	file_13
		add	bx,[bx] 		;Get next in list
		cmp	word ptr [bx+2],-1	;See if label ptr to code
		je	file_12 		;  initialized. No, error.
		add	[bx+2],ax		;Add starting offset of code
		jmp	short file_11
file_12:
		mov	si,bx			;Label not defined as a
		add	si,5			;  destination.  Print error
		xor	cx,cx			;  message inc lost label.
		mov	cl,[bx+4]		;Get size of label
		push	cs
		pop	es
		mov	di,offset errmsg9labl
		rep	movsb
		mov	dx,offset errmsg9	;Label not found msg
		jmp	disp_error
file_13:
		rep	movsb			;Append data to COM file.
		sub	dx,databuff_start	;Set pointer to 1st label list
		mov	es:com_label_start,dx	;Initialize pointer.
;
;Append the inline code to the program.
;
		mov	ax,100h
		add	ax,di
		mov	es:code_start_ptr,ax	;Save offset of main code.

		mov	cx,word ptr codebuff_ptr
		mov	si,codebuff_start	;Get pointer to code
		mov	ds,word ptr codebuff_ptr[2]
		assume	ds:nothing
		sub	cx,si			;Get size of code
		add	ax,cx			;Make sure canned routines
		jnc	file_2			;  and data < 64 K bytes.
file_too_big:	mov	dx,offset errmsg5	;COM file too big msg
		jmp	disp_error
file_2:
		rep	movsb			;Append inline code.
;
;Compute the size of the data buffers and the stack used by the COM program.
;Write these numbers to the COM file data area.
;
		push	cs
		pop	ds
		assume	ds:code
		add	ax,512			;Add room for stack
		jc	file_too_big
		and	ax,0fffeh		;Set stack on even word.
		mov	es:com_stack_ptr,ax	;Save run time stack pointer
		add	ax,2			;Make room for buff length word
		mov	es:parse_buff,ax	;Save ptr to parsing buffer.
		add	ax,BUFF_SIZE+2		;Add room for buffer 2
		jc	file_too_big
		mov	es:parse2_buff,ax	;Save ptr to parsing buffer 2
		add	ax,BUFF_SIZE+2		;Add room for buffer
		jc	file_too_big
		mov	es:exec_buff,ax		;Save ptr to exec buffer 
		add	ax,BUFF_SIZE+2		;Add room for buffer
		jc	file_too_big
		mov	es:forloop_buff,ax	;Save ptr to For loop buffer
		add	ax,BUFF_SIZE		;Add room for buffer
		jc	file_too_big

		mov	outbuff_end,di		;Save end pointer to file
		add	ax,15
		jc	file_3			;Compute size of COM file
		mov	cl,4			;  in paragraphs.
		shr	ax,cl
		mov	es:com_prog_size,ax	;Save file size
		mov	byte ptr es:shift_count,0	;Clear shift count
		mov	byte ptr es:process_rc,0	;Clear return code
file_3:
;
;Write file to disk.
;
		mov	ah,3ch			;Create file
		xor	cx,cx			;Normal attributes
		mov	dx,offset outfile_name	;Name of file
		int	21h

		mov	bx,ax			;Copy handle
		mov	ah,40h			;Write file
		push	ds
		lds	dx,outbuff_ptr		;Get start of buffer
		mov	cx,cs:[outbuff_end]	;Get number of bytes to write
		sub	cx,dx
		int	21h			;Write file to disk
		pop	ds

		mov	ah,3eh			;Close file
		int	21h
;
;Compile complete. Clean up and end.
;
good_exit:
		xor	al,al			;Return code = 0
exit:
		push	ax			;Save return code
		mov	ah,49h			;Release memory block used
		mov	es,word ptr cs:[codebuff_ptr+2] ;  for file buffer.
		int	21h

		mov	bx,file_handle
		cmp	bx,-1			;See if input file closed.
		jne	exit_1    		;  If not, do so.
		mov	ah,3eh			;Close file.
		int	21h
exit_1:
		pop	ax			;Get back return code
		mov	ah,4Ch			;Terminate
		int	21h
;
;Display error message.
;
disp_error:
		push	cs
		pop	ds
		assume	ds:code
		cmp	file_linecount,0
		je	disp_error1		;If processing a file, print
		push	dx			;  a message informing the
		mov	dx,offset filemsg1	;  user the line that
		call	printmsg		;  contained the error.
		mov	ax,file_linecount
		call	hex2asc
		mov	dx,offset endmsg
		call	printmsg
		pop	dx
disp_error1:
		call	printmsgcr		;print string
		mov	al,01			;Terminate with RC = 1
		jmp	short exit

;-----------------------------------------------------------------------------
; PARSE  Parse a statment.
; Entry:  SI - Pointer to string to parse.
;	  DI - Pointer to end of COM file data.
; Exit:   CF - Set if error.
;	  SI - Updated.
;-----------------------------------------------------------------------------
parse		proc	near
		mov	bx,14
		mov	al,[si] 		;See if label, if so, skip 
		cmp	al,":"			;  cmd search.
		je	parse_2

		mov	bx,12
		cmp	al,"%"                  ;See if cmd line param or env
		je	parse_2 		;  var. If so, internal cmd

		mov	ax,[si+1]		;See if change to default disk
		cmp	al,":"
		jne	parse_1

		mov	bx, 12			;Set internal command.
		cmp	ah," "
		jbe	parse_2
parse_1:
		call	capsalpha		;Capitalize batch command
		push	di			;Search list of BAT cmds.
		mov	di,offset command_table
		call	findstr
		pop	di
parse_2:
		push	bx			;Scan past command except for
		cmp	bx,8			;  external progs, inter cmds,
		ja	parse_3 		;  and labels.
		mov	bl,1
		call	scan4char
		jnc	parse_3 		;If end of line, back up to
		dec	si			;  show CR.
parse_3:
		pop	bx
		shl	bx,1			;Compute offset of routine to
		add	bx,offset batcmd_jmptbl ;  call.
		call	[bx]			;Call routine to compile line.
		ret
parse		endp

;-----------------------------------------------------------------------------
; FINDSTR  determines if a string is in a list.
; Entry:  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
		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
		call	redirect_openi
		call	erase_redirect		;Erase redirect from line.
		jmp	redirect_1
redirect_10:
		cmp	al,13
		je	redirect_exit
		jmp	redirect_1
redirect_exit:
		clc
		pop	si
		ret
redirect_check	endp

;-----------------------------------------------------------------------------
; REDIRECT OPENO process redirection commands
; Entry:  AX - 1 = append file.
; Entry:  DX - Offset from data buffer start of file name
;	  BX - 0 no translation of filename.
;-----------------------------------------------------------------------------
redirect_openo	proc	near
		assume	cs:code,ds:code
		push	si
		push	ax			;Save append flag
		mov	cx,0031h		;Indicate parameter type
		or	bx,bx			;See if env var or cmd line
		je	redirect_oo1		;  parms.

		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine. Include if needed.

		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,0032h		;Indicate parameter type
		call	inline_code		;Insert translate code.
redirect_oo1:
		mov	bx,offset rediroo_next	;Append open redirect file
		call	include_code
		pop	bx			;Restore append flag
		call	inline_code
		mov	redirect_out,1		;Set redirect active flag
		pop	si
		ret
redirect_openo	endp

;-----------------------------------------------------------------------------
; REDIRECT OPENI loads code to open a file for input redirection
; Entry:  DX - Offset from data buffer start of file name
;	  BX - 0 no translation of filename.
;-----------------------------------------------------------------------------
redirect_openi	proc	near
		assume	cs:code,ds:code
		push	si
		mov	cx,1			;Indicate parameter type
		or	bx,bx			;See if env var or cmd line
		je	redirect_oi1		;  parms.

		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine. Include if needed.

		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,2			;Indicate parameter type
		call	inline_code		;Insert translate code.
redirect_oi1:
		mov	bx,offset rediroi_next	;Append open redirect file
		call	include_code
		call	inline_code
		mov	redirect_in,1		;Set redirect active flag
		pop	si
		ret
redirect_openi	endp

;-----------------------------------------------------------------------------
; ERASE REDIRECT
; Entry:  BP - Pointer to redirect character
;	  SI - Pointer to end of redirect phrase.
;-----------------------------------------------------------------------------
erase_redirect	proc	near
		assume	cs:code,ds:code
		push	di			;Save ptr to data buffer
		mov	al,' '
		mov	di,bp			;Get start of redirect string
		dec	si
erase_1:
		stosb
		cmp	di,si
		jb	erase_1
		pop	di
		ret
erase_redirect	endp

;-----------------------------------------------------------------------------
; IF CMD compiles an IF command.
; Entry:  SI - Pointer to character after the IF command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
if_cmd		proc	near
		assume	cs:code,ds:code
		xor	bl,bl			;Find 1st char of next word
		call	scan4char
		mov	cx,si			;Save pointer to test
		mov	dx,di
		mov	bp,0			;Clear 'NOT' flag
;
;See if NOT prefix is used in test
;
		lodsw				;Get 1st two chars of test
		or	ax,2020h
		cmp	ax,'on'                 ;See if 'no'
		jne	if_cmd_chk_cmp
		lodsw				;Get 2nd and 3rd characters
		or	al,20h			;Convert 3rd char to lower
		cmp	al,'t'                  ;  case.   See if last char is
		jne	if_cmd_chk_cmp 		;  't' followed by s space
		cmp	ah,' '
		ja	if_cmd_chk_cmp 
		inc	bp
		xor	bl,bl			;Find next word
		call	scan4char
		mov	cx,si
;
;Test for string compare by looking for == signs.
;
if_cmd_chk_cmp:
		mov	si,cx			;Restore pointer to condition
		mov	bl,3			;Find next space or equal sign
		call	scan4char
		jc	if_syntax_jmp
		xor	bl,bl
		call	scan4char		;Find next word
		cmp	word ptr [si],"=="	;See if string compare		
		je 	if_cmd_cmp1		;No, check other tests
		jmp	if_cmd_chk_errlev
if_cmd_cmp1:
		inc	si			;Move SI past equals signs
		inc	si
		xor	bl,bl			;Find second string
		call	scan4char
		jnc	if_cmd_cmp2
if_syntax_jmp:
		jmp	if_syntax
if_cmd_cmp2:
		push	si			;Save pointer to 2nd string
		mov	si,cx			;Restore ptr to 1st string
		push	di			;Save ptr to size byte
		inc	di
		mov	dx,di			;Save ptr to start of string
		sub	dx,databuff_start
		mov	ah,2			;Copy only one word
		call	copy_string		;Load string into data space
		xor	al,al
		stosb
		or	bx,bx			;See if translation is needed
		pop	bx			;Restore pointer to size byte
		mov	[bx],cl 		;Save length of string
		mov	cl,1			;Assume LEA parameter call
		je	if_cmd_03

		mov	bx,offset procstr_next	;Append translate string
		call	include_code		;  routine to code.
		mov	bx,offset prs2_buff_ptr - offset data_start
		mov	cx,0021h		;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	dx,bx			;Copy ptr to buffer.
		mov	cx,2			;Use MOV parameter type
if_cmd_03:
		pop	si			;Restore ptr to 2nd string
		push	dx			;Save pointer to 1st string
		push	cx			;Save parameter type
if_cmd_04:
		push	di			;Save ptr to size byte
		inc	di
		mov	dx,di			;Save ptr to start of string
		sub	dx,databuff_start
		mov	ah,2
		call	copy_string		;Load string into data space
		xor	al,al
		stosb
		or	bx,bx			;See if translation is needed
		pop	bx
		mov	[bx],cl 		;Save length of string
		mov	cx,10h			;Set parameter type
		je	if_cmd_06

		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			;Copy ptr to buffer.
		mov	cx,20h			;Use proper parameter type
if_cmd_06:
		mov	bx,offset ifequal_next	;Append comparison code
		call	include_code
		pop	bx			;Restore param 1 type
		and	bl,0fh
		or	cl,bl			;Combine parameter types
		mov	bx,dx			;Move parameter 2
		pop	dx			;Restore parameter 1
		call	inline_code		;Add call to string compare
		jmp	if_cmd_10
if_syntax:
		mov	dx,offset errmsg6	;Syntax error.
		stc
		jmp	if_exit
;
;See if ERRORLEVEL test.
;
if_cmd_chk_errlev:
		mov	si,cx			;Restore pointer to 1st string
		mov 	bx,cx			;Save pointer to 1st string
		mov	di,offset ifstr1	;See if ERRORLEVEL test
		call	capsalpha
		cmp	cx,10
		jne	if_cmd_2
		repe	cmpsb			;Compare strings
		jne	if_cmd_2
		mov	bx,offset iferrlev_next ;Save offset of errlev code
		push	bx			;  on stack.  Use exist code
 		xor	bl,bl
		call	scan4char		;Scan to next word.
		cmp	byte ptr [si],'='
		jne	if_cmd_21
		inc	si
 		xor	bl,bl
		call	scan4char		;Scan to next word.
		jmp	short if_cmd_21 	;  to append error code.
;
;See if EXIST test.
;
if_cmd_2:
		mov	si,bx			;Restore pointer to 1st string
		mov	di,offset ifstr2	;See if EXIST test
		cmp	cx,5			;Check length of string
		jne	if_syntax
		repe	cmpsb			;Compare strings
		jne	if_syntax
		mov	bx,offset ifexist_next	;Save test existance code off
		push	bx
 		xor	bl,bl
		call	scan4char		;Scan to next word.
if_cmd_21:
		mov	di,dx			;Restore data pointer
		sub	dx,databuff_start
		mov	ah,2
		call	copy_string
		xor	al,al			;Terminate string with zero.
		stosb
		mov	cl,21h			;Set parameter type
		or	bx,bx			;See if translation is needed
		je	if_cmd_3

		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			;Copy ptr to buffer.
		mov	cl,22h			;Use proper parameter type
if_cmd_3:
		pop	bx			;Restore test code offset
		call	include_code
		mov	bx,offset prs2_buff_ptr - offset data_start
		call	inline_code		;Insert exist test call
if_cmd_10:
		mov	cx,200h 		;Include Jump if carry opcode
		or	bp,bp
		je	if_cmd_12
		mov	cx,300h 		;Change to Jump if not carry
if_cmd_12:
		call	inline_code
		push	ax			;Save address of jmp to modify
		push	word ptr codebuff_ptr	;Save current code buff ptr
;
;Compile the remainder of the line as if it were a normal statment.
;
		xor	bl,bl			;Scan for next word
		call	scan4char
		call	parse			;Compile remainder of line.

		pop	cx			;Compute the difference for the
		pop	bx			;  jmp opcode.
		jc	if_exit 		;If error during parse, exit.
		push	es
		mov	ax,word ptr codebuff_ptr
		mov	es,word ptr codebuff_ptr[2]
		sub	ax,cx
		mov	es:[bx],ax		;Save jmp offset
		pop	es
		clc
if_exit:
		ret
if_cmd		endp

;-----------------------------------------------------------------------------
; FOR CMD compiles an FOR command.
; Entry:  SI - Pointer to character after the FOR command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
for_cmd 	proc	near
		assume	cs:code,ds:code	
		cmp	for_active_flag,0		
		je	for_cmd_0
		mov	dx,offset errmsg11	;No nested FOR loops
		stc
		jmp	for_exit
for_cmd_0:
		mov	goto_active,0		;Clear goto detect flag
		inc	for_active_flag
		xor	bl,bl			;Find 1st char of next word
		call	scan4char
		jc	jmp_for_syntax
		mov	bp,si			;Save pointer to loop variable
		mov	dx,di
		sub	dx,word ptr databuff_start
;
;Copy the set data to the 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 calling 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
		xor	al,al			;Terminate string with zero.
		stosb
		mov	cx,11h
		or	bx,bx
		je	for_cmd_3

		mov	bx,offset procstr_next	;Append translate string
		call	include_code		;  routine to code.
		mov	bx,offset for_buff_ptr - offset data_start
		mov	cx,0021h		;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	dx,bx			;Copy ptr to buffer.
		mov	cl,12h			;Use proper parameter type
for_cmd_3:
		push	word ptr codebuff_ptr	;Save current code buff ptr
		mov	bx,offset forloop_next
		call	include_code
		mov	bx,di			;Initialize for loop data
		sub	bx,databuff_start	;  structure.
		mov	word ptr [di],0
		inc	di
		inc	di
		call	inline_code		;Insert call to loop code.
		mov	cx,200h 		;Include Jump if carry opcode
		call	inline_code
		push	ax			;Save address of jmp to modify
		push	word ptr codebuff_ptr	;Save current code buff ptr

;
;Scan the remainder of the line. Replace all instances of the loop variable
;with a special code that the run time translate routine understands.
;
		xor	bl,bl			;Find DO
		call	scan4char
		jc	for_syntax_1
		push	si
		lodsw				;Confirm DO exists
		and	ax,0dfdfh		;Convert to upper case
		cmp	ax,"OD"
		pop	si
		jne	for_syntax_1
		mov	bl,1			;Find end of 'DO'
		call	scan4char
		jc	for_syntax_1
		xor	bl,bl			;Find start of loop command.
		call	scan4char
		jc	for_syntax_1
		push	si			;Save pointer to command.
		push	di			;Save data buffer pointer.
		mov	di,si
		mov	bl,2			;Find the end of the line.
		call	scan4char
		mov	dx,si			;Save pointer to end of line
for_cmd_4:
		mov	si,bp			;Get ptr to loop variable.
		mov	cx,3			;Scan the remainder of the
		repe	cmpsb			;  line for the loop variable.
		jne	for_cmd_5		;  When found, replace with
		dec	di			;  % followed by 7f7fh. This
		dec	di			;  flag tells the runtime
		mov	ax,7f7fh		;  xlate routine to sub in
		stosw				;  the loop string.
for_cmd_5:
		cmp	di,dx			;See if at the end of the line.
		jb	for_cmd_4
		pop	di			;Restore data buffer ptr
		pop	si			;Restore ptr to command.
;
;Compile the remainder of the line as if it were a normal statment.
;
		call	parse			;Compile remainder of line.

		pop	cx			;Get code ptr after loop code
		pop	bx			;Get addr of JC
		pop	ax			;Get code ptr to loop code
		jc	for_exit1
		mov	dx,cx			;DX may have had an error msg

		sub	ax,word ptr codebuff_ptr ;Compute displacment from END
		sub	ax,3			 ;  of JMP opcode.
		mov	cx,400h 		;Insert JMP opcode after code
		call	inline_code		;  for statments inside FOR
		mov	ax,word ptr codebuff_ptr;  loop.
		sub	ax,dx
		push	es
		mov	es,word ptr codebuff_ptr[2]
		mov	es:[bx],ax
		pop	es
;
; If goto parsed during FOR, place jump for goto after FOR loop
;
		cmp	goto_active,0
		je 	for_exit
		mov	dx,goto_data_ptr
		mov	bx,offset gotodly_next	;Append goto delay 
		call	include_code		;  routine to code.
		mov	cx,1
		call	inline_code		;Add code to COM file.
for_exit:
		clc
for_exit1:
		mov	for_active_flag,0	;Clear flag
		ret
for_syntax_1:
		add	sp,6			;Clean off stack
for_syntax:
		mov	dx,offset errmsg6	;Syntax error
		stc
		jmp	short for_exit1
for_cmd 	endp

;-----------------------------------------------------------------------------
; GOTO CMD compiles a goto command.
; Entry:  SI - Pointer to first 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.
;-----------------------------------------------------------------------------
goto_cmd	proc	near
		assume	cs:code,ds:code
		mov	goto_active,1		;Needed for FOR loop
		mov	bp,di			;Save ptr to start of string.
		xor	bl,bl			;Find the first nonspace char.
		call	scan4char
		mov	dx,offset errmsg6	;Check for Syntax error
		jc	goto_cmd_error
		cmp	byte ptr [si],':'	;See if leading :.  If so,
		jne	goto_c0			;  don't include in label.
		inc	si
goto_c0:
		push	si
		xor	ax,ax
		stosw				;Save word in case FOR loop
		xor	ah,ah			;  delay goto.
		call	copy_string		;Copy label to COM data.
		xor	al,al			;Terminate label with zero
		stosb
		pop	si
		mov	dx,offset errmsg8	;Check to see if label found.
		or	cx,cx			;If not, error.
		je	goto_cmd_error
		cmp	for_active_flag,0	;If in for loop, force eval
		jne	goto_c1			;  Jmp must occur after FOR

		or	bx,bx			;See if env var or cmd line
		je	goto_c2 		;  parms.  If not hard code jmp
goto_c1:
		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine. Include if needed.

		mov	dx,bp			;Get pointer to label.
		sub	dx,databuff_start	;Compute offset.
		mov	goto_data_ptr,dx	;Save offset in case FOR loop
		add	dx,2			;Move past word ptr
		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,32h			;Use proper parameter type
		mov	bx,offset goto_next	;Append goto string
		call	include_code		;  routine to code.
		xor	bx,bx
		cmp	for_active_flag,0	;If in FOR loop delay goto.
		je	goto_c11		;  Pass ptr to word to save
		mov	bx,goto_data_ptr	;  destination address.
goto_c11:
		call	inline_code		;Add code to COM file.
		jmp	goto_cmd_exit
;
;Since label doesn't use env vars or cmd line params, hard code JMP.
;
goto_c2:
		mov	di,bp			;Reset data ptr to ignore label
		call	getlabel		;See if label in list.
		mov	ax,bx			;Copy pointer to label pointer
		add	ax,2			;  to code.
		sub	ax,databuff_start	;Compute offset of pointer
		mov	cx,100h 		;Insert JMP opcode.
		call	inline_code
goto_cmd_exit:
		mov	bl,2
		call	scan4char		;Scan to the end of the line.
		clc
goto_cmd_exit1: ret
goto_cmd_error:
		stc
		jmp	short goto_cmd_exit1
goto_cmd       endp

;-----------------------------------------------------------------------------
; LABEL CMD processes a label found in the bat file.
; Entry:  SI - Pointer to first character of the label
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
label_cmd	proc	near
		assume	cs:code,ds:code
		inc	si			;Move past ':'
		call	getlabel
		cmp	word ptr [bx+2],-1	;See if list entry initialized
		jne	label_error		;  if so error.
		mov	ax,word ptr codebuff_ptr
		mov	[bx+2],ax		;Save code ptr in COM data
		mov	bl,2
		call	scan4char		;Scan to the end of the line.
		clc
label_exit:
		ret
label_error:
		mov	dx,offset errmsg8	;Two identical labels
		stc
		jmp	short label_exit
label_cmd	endp

;-----------------------------------------------------------------------------
; GETLABEL searches the label list in the data area.  If a matching label entry
;   is found it is returned, if not a label entry is created.
; Entry:  SI - Pointer to first character of the label
;	  DI - Pointer to end of compiled data.
; Exit:   BX - Points to label entry.
;	  SI,DI updated.
;-----------------------------------------------------------------------------
getlabel	proc	near
		assume	cs:code,ds:code
		call	capsword		;Convert to caps & get length.
		cmp	cx,8			;Max length of a label 8
		jbe	getlabel_1		;  characters to be consistant
		mov	cx,8			;  with DOS.
getlabel_1:
		mov	bp,di			;Save data ptr
		mov	di,offset firstlabel	;Get ptr to the 1st label
		mov	bx,di
		cmp	word ptr [di],-1	;See if any labels defined.
		je	getlabel_2		;No, skip label list search.

		push	cx			;Save label size
		add	bx,[bx] 		;Point to first label
		call	lblsrch_code		;Search label list.
		pop	cx
		mov	di,bp			;Create label entry.
		jnc	getlabel_exit
getlabel_2:
		mov	di,bp			;Create label entry.
		mov	ax,-1
		stosw				;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	copy_string		;Copy command line tail.
		mov	ax,000dh		;Append CR and zero
		stosw
		pop	bp			;Save length of cmd line tail.
		inc	cl			;  Adjust for the CR since 
		mov	[bp-1],cl		;  ProcStr will add CR to the
		sub	bp,databuff_start	;  count.  Exec prog fixes 
		mov	cx,11h			;  this at run time.
		or	bx,bx			;See if translation code needed
		je	externalcmd_1
;
;See if we need translation for command line tail.
;
		push	dx
		mov	dx,bp			;Get pointer to cmd line tail
		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine.
		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,21h			;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	cx,21h			;Use proper parameter type
		mov	bp,bx
		pop	dx
externalcmd_1:
;
;See if we need translation for param.
;
		mov	ax,temp1		;Get translate flag for cmd
		or	ax,ax			;See if extern cmd needs
		je	externalcmd_2		;  translation.
		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine.
		mov	bx,offset prs2_buff_ptr - offset data_start
		push	cx
		mov	cx,21h			;Indicate parameter type
		call	inline_code		;Insert translate code.		
		pop	cx
		and	cl,0f0h			;Use proper parameter type
		or	cl,02h			;  retain the prev param type
		mov	dx,bx			;  for cmd line tail.
externalcmd_2:
		mov	bx,offset external_next ;Append prog launch code
		call	include_code
		mov	bx,bp			;Get ptr to filename
		call	inline_code		;Add code to COM file.
		clc
		ret
external_cmd	endp

;-----------------------------------------------------------------------------
; INTERNAL CMD compiles a command internal to command.com
; 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.
;-----------------------------------------------------------------------------
internal_cmd	proc	near
		assume	cs:code,ds:code
		inc	di			;Make room for string size.
		push	di			;Save ptr to internal command.
		push	si
		mov	si,offset internal_cmdsw
		movsw				;Copy /C switch to tell
		movsb				;  COMMAND.COM to execute and
		pop	si			;  terminate.
		xor	ah,ah
		call	copy_string		;Copy internal command
		mov	ax,000dh		;Append CR and zero
		stosw
		pop	bp
		add	cl,3			;Add length of /c switch
		mov	[bp-1],cl		;Save length of command line

		mov	dx,bp			;Get pointer to internal cmd
		sub	dx,databuff_start	;Compute offset.
		mov	cx,1
		or	bx,bx			;See if translation code needed
		je	internalcmd_1

		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine.
		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,0021h		;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	cx,2			;Use proper parameter type
		mov	dx,bx			;Get input from str1 buffer
internalcmd_1:
		mov	bx,offset intcmd_next	;Append internal cmd routine
		call	include_code		;  to code.
		call	inline_code		;Add code to COM file.
		clc
		ret
internal_cmd	endp

;-----------------------------------------------------------------------------
; PATH CMD compiles a path command
; Entry:  Same as SET command
;-----------------------------------------------------------------------------
path_cmd 	proc	near
		assume	cs:code,ds:code
		mov	bp,2
		push	si
		add	si,4			;Move past path cmd		
		xor	bx,bx			;Find the first nonspace char.
		call	scan4char		;If no characters, set flag
		pop	si
		jnc	set_entry1		;  to print path string.
		mov	bp,3
		jmp	short set_entry1
path_cmd 	endp

;-----------------------------------------------------------------------------
; PROMPT CMD compiles a path command
; Entry:  Same as SET command
;-----------------------------------------------------------------------------
prompt_cmd 	proc	near
		assume	cs:code,ds:code
		mov	bp,1
		jmp	short set_entry1
prompt_cmd 	endp

;-----------------------------------------------------------------------------
; SET CMD compiles a command to change variables in the environment
; Entry:  SI - Pointer to character after the command
;	  DI - Pointer to end of compiled data.
; Exit:   CF - Set if error
;	  DX - Offset to error message if CF set.
;	  SI,DI updated.
;-----------------------------------------------------------------------------
set_cmd 	proc	near
		assume	cs:code,ds:code
		mov	bp,0			;Assume not PATH or PROMPT	
set_entry1:
		push	bp
		xor	bx,bx			;Find the first nonspace char.
		call	scan4char
		mov	dx,di			;Save ptr to set command.
		jc	setcmd_0
		xor	ah,ah
		call	copy_string		;Copy string to COM data
setcmd_0:
		xor	al,al			;Append zero
		stosb
		sub	dx,databuff_start	;Compute offset of data.
		mov	cx,31h
		or	bx,bx			;See if translation code needed
		je	setcmd_1

		mov	bx,offset procstr_next	;Get offset of translate
		call	include_code		;  routine.
		mov	bx,offset prse_buff_ptr - offset data_start
		mov	cx,21h			;Indicate parameter type
		call	inline_code		;Insert translate code.
		mov	cx,32h			;Use proper parameter type
		mov	dx,bx			;Get input from str1 buffer
setcmd_1:
		mov	bx,offset setenv_next	;Append set routine to code.
		call	include_code
		pop	bx			;Get PATH/PROMPT flag
		call	inline_code		;Add code to COM file.
		clc
		ret
set_cmd 	endp

;-----------------------------------------------------------------------------
; ECHO CMD compiles an ECHO command.
; Entry:  SI - Pointer to character after the ECHO command
;	  DI - Pointer to end of compiled data.
; Exit:   AL - Error code if CF set
;	  CF - Set if error
;	  SI,DI updated.
;-----------------------------------------------------------------------------
echo_cmd	proc	near
		assume	cs:code,ds:code
		mov	dx,si			;Save pointer to string start
		inc	dx			;Move pointer past 1st space
		xor	bl,bl			;Echo can either echo a
		call	scan4char		;  string to the keyboard or
		jc	echo_c01		;  toggle the echo flag.
		or	al,20h			;Check for on or off keyword
		cmp	al,"o"                  ;  to indicate what type of
		jne	echo_c0 		;  Echo this is.
		mov	ax,[si+1]		;Get next two characters
		or	al,20h			;Make lower case
		mov	bl,1
		cmp	al,"n"                  ;Check for echo on
		je	echo_c00
		or	ah,20h			;Make lower case
		cmp	ax,"ff"                 ;Check for echo off
		jne	echo_c0
		dec	bl
		mov	ah,[si+3]		;Get character past word
echo_c00:
		cmp	ah," "                  ;Make sure not just the start
		ja	echo_c0 		;  of another word.
		mov	bl,2
		call	scan4char		;Find end of line.
		jmp	short echo_exit
;
;Echo ASCII line, Include code in COM file to echo string.
;
echo_c01:
		mov	si,dx			;Restore data pointer
		cmp	byte ptr [si-2],'.'	;See if Echo.
		jne	echo_exit
		mov	dx,di
		jmp	short echo_c1
echo_c0:
		mov	si,dx			;Restore data pointer
		mov	dx,di
		xor	ah,ah
		call	copy_string		;Copy string to COM data.
echo_c1:
		mov	ax,0d0ah		;Terminate line with CRLF
		stosw
		xor	al,al			;Append zero byte
		stosb
		mov	cx,1			;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:code
		mov	bx,offset shift_next	 ;Append shift 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
		ret
shift_cmd	endp

;-----------------------------------------------------------------------------
; REM CMD  Processes remark lines in batch file.
; Entry:  SI - pointer to line in BAT file
;-----------------------------------------------------------------------------
rem_cmd 	proc near
		assume	cs:code,ds:code
		dec	si			;Back up to make sure we don't
rem_c1: 	lodsb				;  miss a carrage return.
		cmp	al,1ah			;Loop until end of line or end
		je	rem_exit		;  of file.
		cmp	al,13
		jne	rem_c1
rem_exit:
		clc
		ret
rem_cmd 	endp

;-----------------------------------------------------------------------------
; COPY STRING copies a string to the com file data buffer.
; Entry:  SI - Pointer to 1st character of the string.
;	  DI - Pointer to end of compiled data.
;	  AH - 0 = copy until end of line
;	       1 = copy only one word
;	       2 = copy only one word, break on /
;	       3 = copy until ')'
; Exit:   BX - 0 if no environment variables or command line parameters.
;	  CX - Size of string.
;	  SI,DI updated.
;-----------------------------------------------------------------------------
copy_string	proc	near
		assume	cs:code,ds:code
		push	dx
		mov	dx,di			;Copy pointer to string
		xor	bx,bx			;Clear param/env flag
		xor	cx,cx			;Clear count
copystr_1:
		lodsb				;Get byte
		cmp	al,13			;See if carrage return
		je	copystr_exit		;Yes, quit.
		cmp	al,1ah			;See if EOF
		je	copystr_exit
copystr_2:
		cmp	ah,3			;See if set copy
		jne	copystr_3		;No, skip next test.
		cmp	al,')'                  ;See if end of set
		je	copystr_exit		;Yes, quit.
		jmp	short copystr_4
copystr_3:
		or 	ah,ah			;See if phrase or word copy
		je 	copystr_4		;Phase, skip next test.
		cmp	al,' '                  ;See if end of word.
		jbe	copystr_exit		;Yes, quit.
		cmp	al,'='                  ;Equal sign indicates end of
		je	copystr_exit		;  word.
		cmp	ah,2
		je 	copystr_4
		cmp	al,'/'                  ;/ indicates end of word.
		je	copystr_exit		
copystr_4:
		inc	cx
		cmp	al,'%'                  ;See if translation needed.
		jne	copystr_6
		inc	bh
		or	bl,bl			;See if already set.
		mov	bl,0			;Clear flag
		jne	copystr_6		;If set, this must be the
		cmp	byte ptr [si],7fh	;Check for For loop var. If
		jne	copystr_5		;  found, copy it removing
		stosb				;  the extra 7f.
		lodsb
		inc	si
		jmp	short copystr_6
copystr_5:
		cmp	byte ptr [si],'9'       ;  trailing % of an env var.
		jbe	copystr_6
		call	capsword		;Env var, capitialize it.
		inc	bl
copystr_6:
		stosb				;Save byte
		jmp	short copystr_1 	;Loop back.
copystr_exit:
		pop	dx
		ret
copy_string	endp

;-----------------------------------------------------------------------------
; INLINE CODE  Adds the necessary inline code to call a canned
;		routine.
; Entry:  AX - Offset of canned routine.
;	  CL - (low nibble) Method to pass parameter one
;	  CL - (high nibble) Method to pass parameter two
;	  CH - (low nibble) Call/Jump method
;	  DX - Parameter one
;	  BX - Parameter two
;-----------------------------------------------------------------------------
inline_code	proc	near
		push	bp
		push	si			;Save pointer to input buffer.
		push	di
		push	es
		les	di,codebuff_ptr 	;Point ES:DI to buffer
		mov	bp,cx
		and	cl,0fh			;Look only at SI code nibble
		cmp	cl,1			;Check for LEA SI
		jne	inline_1
		mov	si,offset code_leasi
		mov	[si+2],dx		;Load paramter one
		mov	cx,code_leasi_size
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_10
inline_1:
		cmp	cl,2			;Check for MOV SI
		jne	inline_2
		mov	si,offset code_movsi
		mov	[si+2],dx		;Load parameter one
		mov	cx,code_movsi_size
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_10
inline_2:
		cmp	cl,3			;Check for MOV SI Immediate
		jne	inline_10
		mov	si,offset code_movsiim
		mov	[si+1],dx		;Load parameter one
		mov	cx,code_movsiim_size
		rep	movsb			;Copy routine into COM file.
inline_10:
		mov	cx,bp			;Get back code
		and	cl,0f0h
		cmp	cl,10h			;Check for LEA DI
		jne	inline_11
		mov	si,offset code_leadi
		mov	[si+2],bx		;Load paramter two
		mov	cx,code_leadi_size
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_13
inline_11:
		cmp	cl,20h			;Check for MOV DI
		jne	inline_12
		mov	si,offset code_movdi
		mov	[si+2],bx		;Load parameter two
		mov	cx,code_movdi_size
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_13
inline_12:
		cmp	cl,30h			;Check for MOV immed DI
		jne	inline_13
		mov	si,offset code_movdiim
		mov	[si+1],bx		;Load parameter two
		mov	cx,code_movdiim_size
		rep	movsb			;Copy routine into COM file.
inline_13:
		mov	cx,bp
		and	ch,0fh
		jne	inline_20
		mov	si,offset code_call	;Code to call the canned
		mov	cx,code_call_size	;  routine.
		mov	[si+1],ax		;Insert the destination offset.
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_30
inline_20:
		cmp	ch,01			;Check for jmp
		jne	inline_21
		mov	si,offset code_jmp	;Code to jmp to destination.
		mov	cx,code_jmp_size
		mov	[si+2],ax		;Insert the destination offset.
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_30
inline_21:
		cmp	ch,02			;Check for jc
		jne	inline_22
		mov	si,offset code_jc	;Code to jc to destination.
		mov	cx,code_jc_size
		lea	ax,[di+3]		;Save offset of JMP to mod
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_30
inline_22:
		cmp	ch,03			;Check for jnc
		jne	inline_23
		mov	si,offset code_jnc	;Code to jnc to destination.
		mov	cx,code_jnc_size
		lea	ax,[di+3]		;Save offset of JMP to mod
		rep	movsb			;Copy routine into COM file.
		jmp	short inline_30
inline_23:
		cmp	ch,04			;Check for jmp displacment
		jne	inline_30
		mov	si,offset code_jmpdis	;Code to jmp IP relative.
		mov	[si+1],ax
		mov	cx,code_jmpdis_size
		rep	movsb			;Copy routine into COM file.
inline_30:
		mov	word ptr cs:codebuff_ptr,di
		pop	es
		pop	di
		pop	si
		pop	bp
		ret
inline_code	endp

;-----------------------------------------------------------------------------
; INCLUDE CODE - 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-zero.
		mov	ah,2			;DOS character output
		int	21h
hex_1:
		loop	hex_loop2
hex_exit:
		pop	bx
		ret
hex2asc 	endp

;-----------------------------------------------------------------------------
; LOADBATFILE loads the input BAT file.
; Entry:  DS:SI - pointer to the name of the file to open
; Exit:      CF - clear if successful
;
;  Support for BAT files larger than 16 K is not currently implimented.
;-----------------------------------------------------------------------------
loadbatfile	proc	near
		assume	cs:code,ds:code
		push	di
		push	si
		mov	bx,file_handle
		cmp	bx,-1
		jne	loadfile_3
		mov	dx,si			;Save filename pointer
		mov	di,offset outfile_name	;Point DI to buffer to hold
		mov	cx,8			;  the output file name.
loadfile_1:
		lodsb				;Copy the name until the 
		cmp	al,' '			;  extension is reached.
		je	loadfile_2		
		cmp	al,'.'
		je 	loadfile_2
		stosb
		loop	loadfile_1
loadfile_2:	
		mov	si,offset com_string	;Append COM extension to 
		movsb				;  output file name.
		movsw
		movsw
		mov	si,dx			;Get back filename pointer
		mov	bl,1			;Find end of filename
		call	scan4char
		dec	si
		mov	byte ptr [si],0 	;Make filename ASCIIZ.
		mov	ax,3d00h		;Open file (Read only)
		int	21h
		jc	loadfile_error
		mov	bx,ax			;Copy file handle
;
;Read contents into file buffer.
;
loadfile_3:
		mov	file_handle,-1		;Assume file completely read.
		mov	ah,3fh			;Read input BAT file into
		mov	dx,inbuff_ptr		;  memory above stack space
		mov	cx,inbuff_size 		;Get size of buffer
		sub	cx,4
		int	21h			
		mov	di,dx			;Point DI to end of the file
		add	di,ax	
		cmp	ax,cx			;Check if complete file read.
		jb 	loadfile_4
		std				;If there is more of the file
		push	dx			;  to read, scan backwards to
		mov 	dx,ax			;  the end of the last line.
		mov	cx,ax			;Get file length

		mov	al,13			;Scan for last CR
		repne	scasb
		cld				;Reset string flag
		add  	di,3
		add	cx,3
		xchg	cx,dx			;Compute the number of bytes
		sub	dx,cx			;  to back up the file pointer
		mov	cx,0			;  The filepointer is a 32
		jz	loadfile_31		;  bit number in CX,DX.
		dec	cx
loadfile_31:
		mov	ax,4201h		;Move file pointer backwards
		int	21h
		mov	file_handle,bx
		pop	dx
loadfile_4:
		mov	ax,1a1ah
		stosw				;Append EOF bytes.
		stosw

		cmp	file_handle,-1
		jne	loadfile_exit
		mov	ah,3eh			;Close file.
		int	21h
loadfile_exit:
		clc
loadfile_exit1:
		pop	si
		pop	di
		ret
loadfile_error:
		stc
		mov	dx,offset errmsg2	;Bad filename specified.
		jmp	short loadfile_exit1
loadbatfile	endp

;-----------------------------------------------------------------------------
; SCAN4CHAR scans a string to find the first character.
; Entry:  SI - pointer to ASCII string
;	  BL - 0 = find next char, 
;              1 = find next space, 
;              2 = find end of line,
;              3 = find next space or =.
; Exit:   AL - matching character
;	  SI - pointer to matching character
;	  CF - set if carriage return or EOF found
;-----------------------------------------------------------------------------
scan4char	proc near
		assume	ds:nothing,es:nothing
scan4loop:
		lodsb
		cmp	al,13			;Check for carriage return.
		je	scan4_eol
		cmp	al,1ah			;Check for end of file char.
		jne	scan4_1
scan4_eol:
		stc
		jmp	short scan4_exit1
scan4_1:
		cmp	bl,3
		je	scan4_equal
		cmp	bl,1			;Check if searching for space,
		je	scan4_space 		;  character, or end of line.
		ja	scan4loop
		cmp	al," "                  ;Check for space or other
		jbe	scan4loop		;  'white' characters.
		jmp	short scan4_exit
scan4_equal:
		cmp	al,"="			;Check for exit
		je	scan4_exit
scan4_space:
		cmp	al," "                  ;Check for characters.
		ja	scan4loop
scan4_exit:
		dec	si			;Back up before character
		clc
scan4_exit1:
		ret
scan4char	endp

;============================================================================
;Routines used by compiled program.
;============================================================================
;!!--------------------------------------------------------------------------
;Code fragments used to call canned routines.
;----------------------------------------------------------------------------
code_call	proc	near
		mov	ax,1234h		;Set address to call routine.
		call	ax			;Call canned routine.
code_call_end	=	$
code_call	endp

code_jmp	proc	near
		mov	ax,[bp+1234h]		;Set address to call routine.
		jmp	ax			;Jump to new offset.
code_jmp_end	=	$
code_jmp	endp

code_jc 	proc	near
		jnc	code_jc_end		;Jump over long jmp
		jmp	initialize		;This jmp will be modified
code_jc_end	=	$
code_jc 	endp

code_jnc	proc	near
		jc	code_jnc_end		;Skip over long jmp
		jmp	initialize		;This jmp will be modified
code_jnc_end	=	$
code_jnc	endp

code_jmpdis	proc	near
		jmp	initialize		;This jmp will be modified
code_jmpdis_end =	$
code_jmpdis	endp

code_leasi	proc	near
		lea	si,[bp+1234h]		;Load address of data
code_leasi_end	=	$
code_leasi	endp

code_movsi	proc	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_end-offset echo_msg_start
echo_msg_next	dw	0			;Ptr to next routine to append
echo_msg_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
echo_msg_start	=	$

echo_msg_code	proc	near
		assume	cs:code,ds:code
		mov	dl,[si] 		;Get character
		inc	si
		or	dl,dl
		je	echo_msg_1
		mov	ah,2			;Print character
		int	21h
		jmp	short echo_msg_code
echo_msg_1:
		ret
echo_msg_code	endp
echo_msg_end	=	$

;----------------------------------------------------------------------------
;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_stat_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
		call	echo_stat_1		;Push IP on stack
echo_msg	db	"ECHO is $"
echo_on 	db	"on",10,13,"$"
echo_off	db	"off",10,13,"$"
echo_stat_1:
		pop	dx			;Pop offset of echo message
		push	ax			;Save status of echo flag
		mov	ah,9
		int	21h
		add	dx,offset echo_on-offset echo_msg ;Point to 'on' msg
		pop	ax
		or	al,al			;Check status of echo flag
		je	echo_report_1
		add	dx,offset echo_off-offset echo_on ;Point to 'off' msg
		add	dx,5			;Point to off message.
echo_report_1:
		mov	ah,9			;Print last part of echo stat
		int	21h
		ret
echo_stat_code	endp
echo_stat_end	=	$

;----------------------------------------------------------------------------
;PAUSE CODE Routine used pause execution of the COM file.
;----------------------------------------------------------------------------
pause_ptr	dw	0			;Pointer to offset in COM file
pause_size	dw	offset pause_end-offset pause_start
pause_next	dw	0			;Ptr to next routine to append
pause_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
pause_start	=	$

pause_code	proc	near
		assume	cs:code,ds:code
		call	pause_1 	;Push IP on stack
pause_msg	db	"Strike any key when ready...",13,10,"$"
pause_1:
		pop	dx			;Pop offset of message
		mov	ah,9			;Print message.
		int	21h

		mov	ah,7			;Keyboard unfiltered input
		int	21h			;  without echo.
		ret
pause_code	endp
pause_end	=	$

;----------------------------------------------------------------------------
;SHIFT CODE Routine used shift the input parameters by one.
;----------------------------------------------------------------------------
shift_ptr	dw	0			;Pointer to offset in COM file
shift_size	dw	offset shift_end-offset shift_start
shift_next	dw	0			;Ptr to next routine to append
shift_lnks	dw	0			;Bytes in the dependancy header ;Number of routines called
shift_start	=	$

shift_code	proc	near
		assume	cs:code,ds:code
		inc	byte ptr shift_count	 ;Inc shift count.
		ret
shift_code	endp
shift_end	=	$

;-----------------------------------------------------------------------------
; SCAN_CHAR scans a string to find the first character.
; Entry:  SI - pointer to ASCII string
;	  DL - 0 = find next char, 1 = find next space
;	  CX - file length
; Exit:   AL - first nonspace character
;	  CF - set if carriage return found
;-----------------------------------------------------------------------------
scan4_off	dw	0			;Pointer to offset in COM file
scan4_size	dw	offset scan4_end - offset scan4_start
scan4_next	dw	0			;Ptr to next routine to append
scan4_lnks	dw	0
scan4_start	=	$

scan_char	proc near
		assume	ds:nothing,es:nothing
scan_loop:
		lodsb
		cmp	al,9			;See if char is tab
		je	scan_space		;If before, end of line
		cmp	al," "                  ;See if char is space.
		jb	scan_eol		;If before, end of line
		je	scan_space
		or	dl,dl			;Not space, if looking
		jne	scan_loop		;  for space continue.
		jmp	short scan_exit
scan_space:
		or	dl,dl			;Space found, see if looking
		je	scan_loop		;  for one.
scan_exit:
		clc
		ret
scan_eol:
		stc
		ret
scan_char	endp
scan4_end	=	$

;-----------------------------------------------------------------------------
; GETMEMBER  returns a pointer to the Nth word in a line.
; Entry:     SI - pointer to line of words
;	     DH - number of the word to return
; Exit:      SI - pointer to word
;	     CF - Set if word not in line.
;-----------------------------------------------------------------------------
getmember_scan4 equ	[bx-6]
getmember_off	dw	0			;Pointer to offset in COM file
getmember_size	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 count.
		or	dh,dh			;Check count of param to find.
		jnz	sublineparam_12
;
;For parameter 0, attempt to look in the env block for the name of the prog.
;
		push	ax			;Save buffer size in AH
		mov	ah,30h			;Get DOS version 
		int	21h			;If DOS 2.x, program nane not
		cmp	al,2			;  in the env segment.  Use
		pop	ax
		ja	sublineparam_10		;  dunny name instead.
		lea	si,[bx]			;Point to dummy parameter
		jmp	short sublineparam_2
sublineparam_10:
		push	es
		push	di
		mov	es,ds:[2ch]		;Get segment of local env	
		xor	al,al
		xor	di,di
		mov	cx,8000h
sublineparam_11:
		repne	scasb			;Find double zero
		scasb
		jne	sublineparam_11
		scasw				;Scan to name
		mov	si,di			;Copy pointer to name
		pop	di
		pop	es
		mov	ds,ds:[2ch]
		jmp	short sublineparam_2
sublineparam_12:
		call	subparm_getmem		;Get pointer to proper word.
		jc	sublineparam_exit
sublineparam_2:
		lodsb				;Get character from parameter
		cmp	al," "                  ;If space, parameter done
		jbe	sublineparam_exit
		stosb
		dec	ah			;Dec buffer size counter
		jnz	sublineparam_2
sublineparam_exit:
		pop	ds
		pop	si
		pop	cx
		pop	bx
		ret
sublineparam	endp
subparm_end	=	$
;-----------------------------------------------------------------------------
; SUBENVVAR substitutes a parameter from the program environment block.
; Entry:  DS:SI - pointer to enviroment variable.
;	  ES:DI - pointer to buffer.
;	     AH - size of buffer.
; Exit:   ES:DI - pointer to byte after the parameter in the buffer
;	  DS:SI - pointer to the character after the line parameter number
;	     CX - remaining free bytes in the buffer.
;-----------------------------------------------------------------------------
subenv_srchenv	equ	[bx-6]
subenv_off	dw	0			;Pointer to offset in COM file
subenv_size	dw	offset subenv_end-offset subenv_start
subenv_next	dw	0			;Ptr to next routine to append
subenv_lnks	dw	2			;Bytes in the dependancy header
subenv_start	=	$
		dw	offset searchenv_next	;Call to search environment blk

subenvvar	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	subenvvar_0
subenvvar_0:
		pop	bx
		push	ds
;
;Compute the length of the variable name.
;
		mov	cx,255
		push	di			;Save pointer to internal buff
		mov	di,si			;Compute the length of the
		mov	dx,cx			;  environment variable by
		mov	al,"%"                  ;  searching for the trailing
		repne	scasb			;  % sign.
		sub	dx,cx			;Compute length of variable.
		dec	dx			;Subtract % byte from length.
		mov	cx,di
		pop	di
		push	cx			;Save ptr to end of var
		call	subenv_srchenv		;Search environment block.
		jc	short subenvvar_exit	;CF set, variable not found.
;
;Environment variable found. Substitute into string.
;
subenvvar_1:
		lodsb				;Get env var character
		or	al,al			;Check for end of string
		je	subenvvar_exit
		stosb				;Save character in string
		dec	ah			;Dec buffer size count.
		jne	subenvvar_1		;If buffer not full, continue
subenvvar_exit:
		pop	si			;Restore string pointer
		pop	ds			;Restore segment register
		pop	bx
		ret
subenvvar	endp
subenv_end	=	$

;-----------------------------------------------------------------------------
; SEARCH_ENV scans the environment block for a string.
; Entry:  DS:SI - pointer to ASCII string
;	     DX - length of string
; Exit:   DS:SI - points to first character of environment string
;	     CF - clear if string found
;-----------------------------------------------------------------------------
searchenv_off	dw	0		       ;Pointer to offset in 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,al			;Termainate with zero
		stosb
		push	dx
		push	si
		push	cx
		mov	si,dx			;Get ptr to start of name
		call	extern_ifexits		;Search for file
		pop	cx
		pop	si
		pop	dx
		jnc	external_6
		sub	di,4			;Backup to file extension
		loop	external_5
		inc	byte ptr extern_pathcnt ;Look in the next path str
		jmp	external_1
external_6:
		cmp	cx,1			;See if BAT extension
		jne	external_8
		lea	si,extern_cmdparm	;Get pointer to /C
		sub	dx,3
		mov	di,dx			;Get ptr to string buffer
		movsw				;Copy /C param
		movsb
		xor	al,al
		mov	cx,252
		repne	scasb			;Find end of filename
		mov	byte ptr [di-1],' '     ;Fill in zero with space
		mov	si,extern_filetail	;Get ptr to command line tail
		cmp	[si-1],cl
		ja	external_7
		mov	cl,[si-1]		;Get length of cmd line tail
external_7:
		rep	movsb
		mov	byte ptr [di],13	;Append CR to cmd line.
		mov	si,dx			;Get pointer to BAT filename
		mov	ax,di			;Compute length of cmd line.
		sub	ax,dx
		mov	[si-1],al
		call	extern_intcmd		;Launch COMMAND.COM
		jmp	short external_exit
external_8:
		mov	di,extern_filetail	;Get ptr to command line tail
		dec	di			;Back up to buffer length
		dec	byte ptr [di]		;Sub CR from length
		push	di
		mov	si,dx			;Get ptr to start of name
		call	extern_launch		;Execute program
		pop	di
		inc	byte ptr [di]		;Restore cmd line length
external_exit:
		pop	bx
		ret
external_code	endp
external_end	=	$

;-----------------------------------------------------------------------------
; PARSEPATH  Parses the PATH and returns a qualified directory from the path.
; Entry:  ES:DI - pointer to destination buffer.
;	     CX - index into the path variable. (zero based.)
; Exit:   ES:DI - pointer to ASCIIZ destination filename.
;	     CF - Set if past end of the path
;-----------------------------------------------------------------------------
parsepath_srenv equ	[bx-6]
parsepath_off	dw	0			 ;Pointer to offset in COM file
parsepath_size	dw	offset parsepath_end - offset parsepath_start
parsepath_next	dw	0			 ;Ptr 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
goto_4:
		mov	cx,dx			;Get size of label
		mov	si,bx			;Get pointer to label
		mov	di,com_label_start	;Get ptr to start of label list
		add	di,bp			;Add offset of data
		push	bx			;Save ptr to msg
		call	goto_lblsrch		;Call search routine
		pop	dx			;Restore ptr to message
		pop	si			;Restore Wait flag/pointer
		jc	goto_5			;CF set, label not found.
		mov	ax,[bx+2]		;Get destination ptr
		or	si,si			;See if delay flag <> 0
		je	goto_41			;If 0, no delay
		mov	[bp+si],ax		;Save ret addr at pointer
		ret
goto_41:
		pop	ax			;Remove return address
		push	[bx+2]			;Push new return address
goto_exit:
		ret
goto_5:
		mov	bx,dx			;Set addressability to call
		call	goto_echomsg		;Print error message.
		ret
goto_code	endp
goto_end     	=	$

;----------------------------------------------------------------------------
;GOTO DLY  Routine used if goto statment is inside a FOR loop.  Since a for
;      loop cannot be exited before it ends, this routine checks to see if
;      the GOTO statment was ever executed. If so, we now can jump.
; Entry   DS:SI - pointer to destination pointer. 0 = no jump.
; Exit	  This routine does not return unless the ptr = 0
;----------------------------------------------------------------------------
gotodly_ptr	dw	0			;Pointer to offset in COM file
gotodly_size	dw	offset gotodly_end - offset gotodly_start
gotodly_next	dw	0			;Ptr to next routine to append
gotodly_lnks	dw	0			;Bytes in the dependancy header ;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
findenv_lnks	dw	0
findenv_start	=	$

findenv 	proc	near
		assume	cs:code,ds:code,es:code,ss:code
		push	bx
		call	findenv_0

		db	"COMMAND"
		dw	0
findenv_0:
		pop	bx
		push	es

		mov	ax,ds:[2ch]		;Get default env
		cmp	environment_seg,ax	;Check to see if already
		jne	findenv_jmp_exit	;  found.

		push	bx			;Ver changes BX
		mov	ah,30h			;Get dos Version
		int	21h
		xchg	al,ah
		mov	findenv_dosver,ax

		mov	ah,52h			;get address of first MCB
		int	21h
		mov	ax,es:[bx-2]		;point ES to MCB
		mov	cx,20			;Allow only 20 loops.
		pop	bx
findenv_1:
		mov	es,ax
		cmp	byte ptr es:[0],"M"     ;check for mcb signature
		jne	short findenv_exit
		inc	ax			;point AX to memory block
		cmp	ax,es:[1]		;See if this is a PSP block
		je	findenv_4
findenv_2:
		add	ax,es:[3]		;Get size of memory block
		loop	findenv_1
findenv_jmp_exit:
		jmp	short findenv_exit
findenv_4:
		cmp	word ptr findenv_dosver,0a00h ;If OS/2, use DOS 3.3 method.
		jae	findenv_5
		cmp	word ptr findenv_dosver,0400h;If DOS 4.00 or greater,
		jb	findenv_5		  ;  COMMAND.COM may not be the
		push	ds			  ;  first program loaded.  Look
		lea	si,findenv_cmdname	  ;  at the name of the program
		mov	di,8			  ;  stored in the last 8 bytes
		mov	cx,7			  ;  of the memory control
		repe	cmpsb			  ;  block.  If the string
		pop	ds			  ;  "COMMAND" isn't found,
		jne	findenv_2		  ;  keep looking.
findenv_5:
		mov	dx,ax			;Save PSP seg of command.com
		mov	es,ax
		mov	bx,es:[2ch]		;Get seg of prog environment
		mov	ax,bx
		dec	bx
		mov	es,bx
		cmp	byte ptr es:[0],"M"     ;See if valid memory block
		je	findenv_found
		mov	ax,dx			;Get back cmd.com segment
		dec	ax			;Point back to mcb
		mov	es,ax
findenv_6:
		add	ax,es:[3]		;If master env segment not 
		inc	ax			;  saved at 2Ch of the PSP, 
		mov	es,ax			;  scan the memory blocks
		cmp	es:[1],dx		;  for first segment
		je	findenv_7		;  owned by command.com.
		loop	findenv_6
		jmp	short findenv_exit	;Master env not found.
findenv_7:	
		inc	ax			;Point AX to env segment
findenv_found:
		mov	environment_seg,ax	;Save pointer to master env 
findenv_exit:
		pop	es
		pop	bx
		ret
findenv 	endp
findenv_end	=	$

;-----------------------------------------------------------------------------
; SET_ENV  Sets/resets environment variables.
; Entry:  DS:SI - pointer to ASCII string containing the environment variable
;		  and, optionally, the string to assign.
;                 If no string specified, the environment is dumped to the
;                 screen.
;            DI - Flag to indicate setting of PATH and PROMPT vars
;                 0 = Normal Env var, 1 = PROMPT var, 2 = PATH var.
;-----------------------------------------------------------------------------
setenv_findenv	equ	[bx-10]
setenv_echo	equ	[bx-8]
setenv_serchenv equ	[bx-6]
setenv_pathflag	equ	[bx]
setenv_varlen 	equ	[bx+1]
setenv_fullmsg	equ	[bx+2]
setenv_crmsg  	equ	[bx+29]
setenv_nopath	equ	[bx+32]
setenv_off	dw	0			;Pointer to offset in COM file
setenv_size	dw	offset setenv_end - offset setenv_start
setenv_next	dw	0			;Ptr to next routine to append
setenv_lnks	dw	6
setenv_start	=	$
		dw	offset findenv_next	;Call to find the master env
		dw	offset echo_msg_next	;Call to print line.
		dw	offset searchenv_next	;Call to search environment blk

set_env 	proc near
		assume	ds:nothing,es:nothing
		push	bx
		call	setenv_1
		db	0			;PATH/PROMPT flag storage
		db	0			;Var length.
		db	"Out of environment space",13,10,0
		db	13,10,0
		db	"No Path",0
setenv_1:
		pop	bx			;Set up local addressing
		mov	ax,di
		xchg	al,ah
		mov	setenv_pathflag,ah
		push	ax
		call	setenv_findenv		;Find master env block
		pop	ax
		push	bp
		push	es
;See if we need to simply print the PATH statment.
		cmp	ah,3
		jne	setenv_19
		mov	dx,4
		call	setenv_serchenv 	;Get pointer to variable
		jnc	setenv_11
		lea	si,setenv_nopath	;If path string not found, 
		push	cs			;  print no path msg.
		pop	ds
		jmp	short setenv_12
setenv_11:
		sub	si,5			;Back up to start of PATH
setenv_12:
		call	cs:setenv_echo		;Print string
		push	cs
		pop	ds
		lea	si,setenv_crmsg		;Append CR.
		call	setenv_echo
		jmp	setenv_8
setenv_19:	
		mov	cx,BUFF_SIZE
		xor	dx,dx
		push	si			;Save ptr to new env var
		mov	di,si
setenv_2:
		lodsb
		or 	ah,ah			;See if PATH or PROMPT
		je	setenv_23
		cmp	al," "			;If PATH or PROMPT statments,
		jne	setenv_23		;  remove any spaces between	
		mov	al,"="			;  variable and assignment.
		push	di
		stosb
setenv_20:
		lodsb				;Scan past equal sign 
		cmp	al," "			
		je	setenv_20
		cmp	al,"="
		je	setenv_20
setenv_21:
		stosb	
		or	al,al
		je	setenv_22
		lodsb				;Remove spaces in set string.
		jmp	short setenv_21
setenv_22:
		pop	di
		jmp	short setenv_4
setenv_23:
		or 	al,al
		je	setenv_4
		cmp	al,"="                  ;Scan until end of variable
		je	setenv_4		;  found.
		cmp	al,'a'
		jb	setenv_3
		cmp	al,'z'
		ja	setenv_3
		and	al,0dfh 		;Capitalize variable
setenv_3:
		cmp	al," "			;Keep a count of non-space
		jbe	setenv_31		;  characters.
		inc	dh
setenv_31:
		stosb
		inc	dl			;Var length count.
		loop	setenv_2
setenv_4:
		pop	si
;
;If no characters after SET, dump env vars to screen.
;
		mov	setenv_varlen,dl	;Save length of env var.
		mov	al,dh
		xor	dh,dh
		or	dh,dh			;See if path or prompt
		jne	setenv_40
setenv_40:
		or	al,al			;If second Zero, end of vars.
		jne	setenv_48
		push	ds
		mov	ds,cs:[environment_seg]	;Get segment of environment
		xor	si,si
		lea	di,setenv_crmsg		;Get address of CR string
		mov	cx,cs
setenv_41:
		call	cs:setenv_echo		;Print var
		push	ds
		push	si
		mov 	ds,cx
		mov 	si,di
		call	cs:setenv_echo		;Print CR
		pop	si
		pop	ds
		cmp	byte ptr ds:[si],0	;If next char 0, end of block
		jne	setenv_41
		pop	ds
		jmp	setenv_8
;
;Set or Clear env var.
;
setenv_48:
		mov	ax,si
		inc	di
		cmp	byte ptr cs:[di]," "    ;If nothing past '=' then
		jae	setenv_49		;  simply erase var from env.
		xor	ax,ax
setenv_49:
		push	ax			;Save set/erase flag
		call	setenv_serchenv 	;Get pointer to variable
		pop	dx			
		push	ds			;ES = env segment
		pop	es
		mov	di,si			;Copy pointer to variable
		jc	setenv_61

		xor	al,al			;Scan backwards to find start
		std				;  of var name
		mov	cx,256
		repne	scasb
		inc	di
		inc	di
		push	di
		cld				;Scan forward to find end of
		mov	cx,256			;  variable.
		repne	scasb
		mov	si,di
		pop	di
		xor	ah,ah
setenv_5:
		lodsb				;Move byte from past var to
		stosb				;  cover up current assignment
		or	ah,al			;If two zeros in a row then
		je	setenv_6		;  end of env vars.
		mov	ah,al
		jmp	short setenv_5
setenv_6:
		dec	di			;Back up before last zero
setenv_61:
		push	cs
		pop	ds
		mov	si,dx			;Get back ptr to env var
		or	si,si			;See if var assignment
		je	setenv_8

		push	es			;Get size of the environment
		mov	ax,es			;  block by reading the size
		dec	ax			;  in the memory control block.
		mov	es,ax
		mov	bp,es:[3]
		mov	cl,4
		shl	bp,cl			;Convert to bytes.
		dec	bp
		dec	bp
		pop	es
		xor	ax,ax			;See if there is room in the
		mov	al,setenv_varlen	;  env block for the var name.
		inc	al			;  If not, don't even start.
		add	ax,di			;  If room for name, copy as 
		cmp	ax,bp			;  much of the var as possible
		jae	setenv_72
		mov	ah,setenv_pathflag	;Get pathflag byte
setenv_7:
		cmp	di,bp			;Check for end of env block.
		jae	setenv_72
		lodsb				;Copy new env var
		cmp	ah,2	
		jne	setenv_71		;If PATH var, make upper case.
		cmp	al,'a'
		jb 	setenv_71
		cmp	al,'z'
		ja	setenv_71
		and	al,0dfh			
setenv_71:	
		stosb
		or	al,al			;Check for end of variable.
		jne	setenv_7
		stosb
		jmp	short setenv_8
setenv_72:
		xor	al,al
		stosb
		push	cs
		pop	es
		lea	si,setenv_fullmsg	;If environment block full,
		call	setenv_echo		;  print message.
setenv_8:
		pop	es
		pop	bp
		pop	bx
		ret
set_env 	endp
setenv_end	=	$

;-----------------------------------------------------------------------------
; REDIROO  Opens a file for output redirection.
; Entry:  DS:SI - pointer to ASCII filename to open.
;	     DI - 0  open new file, 1 append to existing file.
;-----------------------------------------------------------------------------
rediroo_off	dw	0			;Pointer to offset in COM file
rediroo_size	dw	offset rediroo_end - offset rediroo_start
rediroo_next	dw	0			;Ptr to next routine to append
rediroo_lnks	dw	0
rediroo_start	=	$

rediroo 	proc near
		assume	ds:nothing,es:nothing

		mov	word ptr stdout_hdl,-1	;Clear handle
		mov	dx,si			;Get ptr to outfile file
		xor	cx,cx			;Normal attributes
		mov	ax,3c02h		;Create file
		or	di,di			;See if append or new file
		je	rediroo_2
		inc	ah			;Open file
rediroo_2:
		int	21h
		jc	rediroo_exit
		mov	bx,ax			;Copy file handle
		or	di,di
		je	rediroo_3
		mov	ax,4202h		;Move file ptr to end of file
		xor	dx,dx
		mov	cx,dx
		int	21h
		jc	rediroo_exit
rediroo_3:
		mov	outfile_hdl,bx		;Save output file handle
		push	bx
		mov	ah,45h			;Duplicate output handle
		mov	bx,1			;Std output handle
		int	21h
		mov	stdout_hdl,ax		;Save dup std output handle

		mov	cx,1
		pop	bx
		mov	ah,46h			;Force dup file handle
		int	21h
rediroo_exit:
		ret
rediroo 	endp
rediroo_end	=	$

;-----------------------------------------------------------------------------
; REDIRCO  Closes a file used for output redirection.
;-----------------------------------------------------------------------------
redirco_off	dw	0			;Pointer to offset in COM file
redirco_size	dw	offset redirco_end - offset redirco_start
redirco_next	dw	0			;Ptr to next routine to append
redirco_lnks	dw	0
redirco_start	=	$

redirco 	proc near
		assume	ds:nothing,es:nothing
		cmp	word ptr stdout_hdl,-1	;If error on redirect, skip
		je	redirco_exit		;  restore.

		mov	ah,46h			;Force restore of std out
		mov	bx,stdout_hdl
		mov	cx,1
		int	21h

		mov	ah,3eh			;Close file
		mov	bx,outfile_hdl		;Get output file handle
		int	21h
redirco_exit:
		ret
redirco 	endp
redirco_end	=	$

;-----------------------------------------------------------------------------
; REDIROI  Opens a file for input redirection.
; Entry:  DS:SI - pointer to ASCII filename to open.
;	     DI - 0  open new file, 1 append to existing file.
;-----------------------------------------------------------------------------
rediroi_off	dw	0			;Pointer to offset in COM file
rediroi_size	dw	offset rediroi_end - offset rediroi_start
rediroi_next	dw	0			;Ptr to next routine to append
rediroi_lnks	dw	0
rediroi_start	=	$

rediroi 	proc near
		assume	ds:nothing,es:nothing

		mov	word ptr stdin_hdl,-1	;Clear handle
		mov	dx,si			;Get ptr to outfile file
		mov	ax,3d00h		;Open file, Read only
		int	21h
		jc	rediroi_exit
		mov	infile_hdl,ax		;Save input file handle
		push	ax

		mov	ah,45h			;Duplicate input handle
		xor	bx,bx
		int	21h
		mov	stdin_hdl,ax		;Save dup std input handle

		xor	cx,cx
		pop	bx
		mov	ah,46h			;Force dup file handle
		int	21h
rediroi_exit:
		ret
rediroi 	endp
rediroi_end	=	$

;-----------------------------------------------------------------------------
; REDIRCI  Closes a file used for input redirection.
;-----------------------------------------------------------------------------
redirci_off	dw	0			;Pointer to offset in COM file
redirci_size	dw	offset redirci_end - offset redirci_start
redirci_next	dw	0			;Ptr to next routine to append
redirci_lnks	dw	0
redirci_start	=	$

redirci 	proc near
		assume	ds:nothing,es:nothing
		cmp	word ptr stdin_hdl,-1	;If error on redirect, skip
		je	redirci_exit		;  restore.

		mov	ah,46h			;Force restore of std in hdl
		xor	cx,cx
		mov	bx,stdin_hdl
		int	21h

		mov	ah,3eh			;Close file
		mov	bx,infile_hdl		;Get input file handle
		int	21h
redirci_exit:
		ret
redirci 	endp
redirci_end	=	$

;-----------------------------------------------------------------------------
; REDIRDEL Deletes piping file.
; Entry:  DS:SI - pointer to ASCII filename to delete.
;-----------------------------------------------------------------------------
redirdel_off	dw	0			;Pointer to offset in COM file
redirdel_size	dw	offset redirdel_end - offset redirdel_start
redirdel_next	dw	0			;Ptr to next routine to append
redirdel_lnks	dw	0
redirdel_start	=	$

redirdel 	proc near
		assume	ds:nothing,es:nothing

		mov	dx,si			;Copy pointer to filename
		mov	ah,41h			;Delete file
		int	21h
		ret
redirdel 	endp
redirdel_end	=	$

initialize	endp
		even				;compiler stack on word boundry
end_of_code	=	$
code		ends

end		main
e.
		cmp	al,'a'
		jb 	setenv_71
		cmp	al,'z'
		ja	setenv_71
		and	al,0dfh			
setenv_71:	
		stosb
		