; WILD Public Domain 1988 by Charles Lazo III, v1.0

; WILD.ASM	This program is used to run any other program (or DOS command)
;		by expanding program wild card parameters given on the command
;		line.  E.g., WILD PROG *.* would run a program, say PROG.EXE,
;		multiple times and supply it each time with a file from the
;		current directory matching *.* (i.e., every file in the current
;		directory would be supplied to PROG for execution).

rt		equ	0dh
lf		equ	0ah
of		equ	offset
bptr		equ	byte ptr
wp		equ	word ptr

code		segment
		assume	cs:code, ds:code

		org	2ch
env_seg		dw	?		; pointer to segment of our environment

		org	100h
begin:		jmp	start

;-------------------------------------------------------------------------------
; The wild card specification is placed here at the bottom of our stack to avoid
; having to specially set aside space for it.  It is used by the find_first
; routine to find the first file in the current directory meeting the wild card
; specification using the DOS Find First function.
;-------------------------------------------------------------------------------
wild_spec	db	16 dup('STACK   ')	; 128 bytes for stack
our_stack	label	word

ss_save		dw	?	; place to store ss:sp
sp_save		dw	?

memory_used	dw	?	; keep track of memory paragraphs used
env_size	dw	?	; number of bytes in the environment stored here
cspc_addr	label	dword
		dw	2 dup(?); store address of comspec variable here
left_blank	dw	?	; stores pointer to start of wild card spec
right_blank	dw	?	; stores pointer to end of wild card spec
copy_size	dw	?	; stores size of command on command line (WILD)
buffer_ptr	dw	0	; pointer to buffer for list of filenames

switches	db	0		; record presence of QUERY & NOEXT here
file_attr	dw	0		; store file attributes for file find

cmd_line	db	128 dup(0)	; save original command line here

cmdl		db	?,'/c',128 dup(?)	; command line for EXEC

;-------------------------------------------------------------------------------
; The parameter table to be passed to the DOS EXEC function:
;-------------------------------------------------------------------------------
params		dw	?	; segment of environment block stored here
		dw	of cmdl	; offset of command line for program EXECed
		dw	?	; segment of command line for program EXECed
		dw	55h	; offset of first FCB
		dw	?	; segment of first FCB
		dw	65h	; offset of second FCB
		dw	?	; segment of second FCB

;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
; The following routines (up to the label buffer:) operate under the assumption
; that both ds and es are set to the code segment of WILD.
;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

;-------------------------------------------------------------------------------
; When the WILD environment variable has NOEXT in its assignment string, then
; filename extensions are ignored (not supplied) when commands are passed to
; the DOS EXEC function.  This routine will remove any extension from the file-
; name in the DTA placed there by the DOS Find First and Continue File Search
; functions if NOEXT was found in the WILD environment variable.  The extension
; is removed by replacing the period separating the main part from the extension
; with a null.  Note that if a period is the only part of the filename, then it
; must be the dot representing the current directory so it is not removed.
;-------------------------------------------------------------------------------

remove_ext?	proc	near		; removes any extension from filename
		test	switches,noe	; is WILD NOEXT env variable set?
		jz	no_remove	; no, don't remove extension
		xor	al,al		; find null terminating filename
		mov	cx,14		; max number of characters in ASCIIZ+1
		cld			; (for maintenance and documentation)
		repne	scasb		; search for null
		sub	di,9eh		; compute number of filename characters
		mov	cx,di		; and place it in cx for next search
		mov	al,'.'		; search for period prior to extension
		mov	di,9eh		; offset of filename in DTA
		repne	scasb		; search for period
		jcxz	no_remove	; can't remove extension if not there
		cmp	di,9fh		; if the period was the ONLY character,
		jz	no_remove	;   then it is the current directory
		mov	bptr [di-1],0	; set it to null to remove the extension
no_remove:	ret
remove_ext?	endp

;-------------------------------------------------------------------------------
; find_size computes the length of a filename in the DTA placed there by the DOS
; find first and continue file search functions.  The length of the filename is
; returned in cx (the null character at the end of the ASCIIZ string is included
; in the count).  This routine is similar to file_size, qv.
;-------------------------------------------------------------------------------

find_size	proc	near		; find size of filename (returned in cx)
		call	remove_ext?	; eliminate filename extension?
		xor	al,al		; find null at end of ASCIIZ filename
		mov	di,9eh		; offset of filename in DTA
		mov	cx,14		; max number of characters in ASCIIZ+1
		cld			; (for maintenance and documentation)
		repne	scasb		; search for null in ASCIIZ filename
		sub	di,9eh		; compute number of filename characters
		mov	cx,di		; return value in cx
		ret
find_size	endp

;-------------------------------------------------------------------------------
; In this routine we check to see if there is enough memory presently allocated
; for the filename buffer to place another filename in the buffer.  First di is
; is set to the location in the buffer where the next filename will go.  If not
; enough memory has been allocated to allow the placement of another filename
; in the buffer, then more memory is allocated.  By using only the memory that
; is needed by the buffer (additional memory is allocated in blocks of 16 para-
; graphs or 256 bytes; an arbitrary value) the remaining memory can be used in
; the EXEC call.
;-------------------------------------------------------------------------------

set_di		proc	near		; sets di to point to next available
		mov	di,buffer_ptr	;   location in filename buffer
		push	cx		; save filename size
		or	di,di		; is it zero (uninitialized)?
		jz	init_di		; yes, point it to start of buffer
		mov	bx,di		; use bx to find paragraphs used now
		add	bx,0fh		; round up to next paragraph
		shr	bx,1		; determine paragraph count
		shr	bx,1
		shr	bx,1
		shr	bx,1
		inc	bx		; reserve one more than used
		mov	ax,memory_used	; memory paragraphs presently allocated
		cmp	bx,ax		; is memory to be used > allocated?
		ja	get_more	; yes, get more memory
di_set:		pop	cx		; restore filename size
		ret
get_more:	add	ax,16		; ask for 256 more bytes (16 paragraphs)
		mov	bx,ax		; request sent in bx
		mov	dx,ax		; store in dx to assure request granted
		mov	ah,4ah		; DOS memory modify function (es is ok)
		int	21h		; ask DOS for it
		cmp	bx,dx		; DOS give what we wanted?
		mov	dx,of mem_need	; address error message in case not
		jz	got_it		; got requested memory
		jmp	error_exit	; nope, DOS don't have it, terminate
init_di:	mov	di,of buffer
		jmp	short di_set	; exit with di at start of buffer
got_it:		mov	memory_used,bx	; update memory allocated
		pop	cx		; restore filename size
		ret
set_di		endp

;-------------------------------------------------------------------------------
; With this call we copy the filename in the DTA (placed there by DOS Find First
; and Continue File Search functions) into the filename buffer.
;-------------------------------------------------------------------------------

store_filename	proc	near		; store filename of file find in buffer
		call	find_size	; find size of filename in DTA (to cx)
		call	set_di		; set di to next location in buffer
		mov	si,9eh		; offset of filename in DTA
		cld			; (for maintenance and documentation)
		rep	movsb		; move ASCIIZ filename to buffer
		mov	buffer_ptr,di	; update buffer pointer
		ret
store_filename	endp

;-------------------------------------------------------------------------------
; This one is similar to the find_size routine in that the size of a file is
; returned in cx.  While the find_size routine gets its input from the filename
; in the DTA, this one finds sizes of filenames set into the filename buffer.
; Additionally it differs in that find_size includes the terminal null in the
; count and this one does not.  Also the zero flag is set if no more filenames
; exist (else reset).
;-------------------------------------------------------------------------------

file_size	proc	near		; size of next filename in buffer to cx
		mov	di,buffer_ptr	; get current buffer position
		cmp	bptr [di],0	; was the last the final filename?
		jz	last_done	; yes, return with zero flag set
		mov	cx,di		; save current buffer position
		push	cx
		mov	cx,14		; longer than any ASCIIZ filename string
		mov	al,0		; search for null in ASCIIZ filename
		cld			; (for maintenance and documentation)
		repne	scasb		; find the null
		mov	buffer_ptr,di	; store new buffer position
		pop	cx		; recover last buffer position
		sub	di,cx		; compute size of filename in cx
		xchg	cx,di
		dec	cx		; discard null and reset the zero flag
last_done:	ret
file_size	endp

;-------------------------------------------------------------------------------
; A copy of the command line as given by the user (WILD's command line) is
; maintained at the location cmd_line.  The variables left_blank and right_blank
; point to the first and last characters of the wild card specification within
; cmd_line when this routine is called the first time or the first and last
; characters of the previous filename when the routine is called at succeeding
; times.  This blank is resized--either made larger or smaller--depending upon
; the size of the current filename that is to be copied into it.
;-------------------------------------------------------------------------------

resize_blank	proc	near		; adjust filename blank to right size
		mov	ax,left_blank	; first character of blank
		mov	bx,right_blank	; last character of blank
		sub	bx,ax		; compute length of blank
		inc	bx

		mov	di,right_blank	; point to last character of blank
		mov	al,rt		; search for return character
		push	cx		; save filename size
		mov	cx,127		; larger than necessary, so ok
		cld			; (for maintenance and documentation)
		repne	scasb		; find the return character
		dec	di		; point di to return character
		mov	cx,di		; compute number to move in cx
		mov	ax,right_blank
		sub	cx,ax
		pop	ax		; get filename size

		cmp	ax,bx		; greater, less than, or equal?
		je	rb_done		; finished if equal
		jb	decrease	; less than, so decrease blank
		sub	ax,bx		; compute amount filename is greater
		mov	si,di		; point si to return character
		add	di,ax		; di to new location of return character
		std			; decrement move this time
		rep	movsb		; make room for filename
		mov	bx,right_blank	; adjust this variable
		add	bx,ax
		mov	right_blank,bx
		mov	bx,copy_size	; and this one also
		add	bx,ax
		mov	copy_size,bx
		mov	bl,cmd_line	; and this one too
		add	bl,al
		mov	cmd_line,bl
rb_done:	ret

decrease:	sub	bx,ax		; compute amount blank is greater
		mov	si,right_blank	; point to first character after blank
		inc	si
		mov	di,si		; compute new location for these chars
		sub	di,bx
		cld			; (for maintenance and documentation)
		rep	movsb		; resize blank to filename size
		mov	ax,right_blank	; adjust this variable
		sub	ax,bx
		mov	right_blank,ax
		mov	ax,copy_size	; and this one also
		sub	ax,bx
		mov	copy_size,ax
		mov	al,cmd_line	; and this one too
		sub	al,bl
		mov	cmd_line,al
		ret
resize_blank	endp

;-------------------------------------------------------------------------------
; cx contains filename size due to maintenance of this value on the stack while
; the resize_blank routine was called previous to the call to this one.  The
; routine copies a filename from the filename buffer into a holding area (the
; location in cmd_line called blank) prior to it's being copied to the command
; line used by EXEC.
;-------------------------------------------------------------------------------

copy_filename	proc	near		; copy a buffer filename to cmd_line
		mov	si,buffer_ptr	; point si to current filename
		sub	si,cx		;   (since file_size updated buffer_ptr
		dec	si		;    we must go back for current file)
		mov	di,left_blank	; the place in cmd_line to copy to
		cld			; (for maintenance and documentation)
		rep	movsb		; filename --> cmd_line
		ret
copy_filename	endp

;-------------------------------------------------------------------------------
; This routine prepares the next command line that will be passed to EXEC by
; using the next available filename from the filename buffer.  If all filenames
; have been processed, then the zero flag is set on return, else it is reset.
;-------------------------------------------------------------------------------

copy_command	proc	near		; copy command to EXEC's command line
		call	file_size	; find size of filename (returned in cx)
		jz	all_done	; no more, so return with zero flag set
		push	cx		; save filename size
		call	resize_blank	; adjust filename blank to right size
		pop	cx		; restore filesize for copy_filename
		call	copy_filename	; filename from buffer --> command line

		mov	cx,copy_size	; get size of command line copy
		mov	al,cmd_line	; set size of EXEC's command line
		add	al,2		;   account for increase due to "/c"
		mov	cmdl,al		;   set in size for EXEC
		mov	si,of cmd_line+1; copy command to be executed
		mov	di,of cmdl+3	;   to proper location for EXEC
		cld			; (for maintenance and documentation)
		rep	movsb		; do the copy
		or	al,al		; al is nonzero so zero flag is reset
all_done:	ret
copy_command	endp

;-------------------------------------------------------------------------------
; If the WILD environment variable contains the QUERY option, then this routine
; prompts the user with "(Y/N/A)?" for Yes, No, or Abort.  The zero flag is set
; on return if the user replies No, it is reset if the reply is Yes, and the
; carry flag is set if Abort is the reply (carry flag reset if Y or N).  These
; flag settings are preserved by the calling routine, wild_prompt, so that the
; code that calls wild_prompt can test them.  This code responds as if a Yes
; response were given if QUERY is not active.
;-------------------------------------------------------------------------------

wild_query?	proc	near		; inquire Yes, No, or Abort if QUERY set
		test	switches,que	; is QUERY set in switches variable?
		jz	wq_yes		; no, return to caller as if Yes
		mov	dx,of wq_prompt	; prompt to send to standard error
		mov	cx,wqp_size	; number of characters to send
		mov	bx,2		; write to file handle 2 (std error)
		mov	ah,40h		; with DOS function 40h
		int	21h
wq_next:	mov	ah,7		; DOS keyboard input without echo
		int	21h
		or	al,al		; an ASCII character?
		jnz	wq_ascii	; yes, process the response
		mov	ah,7		; no, get and discard auxiliary byte
		int	21h
		jmp	short wq_next	; continue input
wq_ascii:	and	al,0dfh		; capitalize
		mov	response,al	; and store it for (possible) output
		cmp	al,'Y'		; a Yes response?
		je	wq_yes		; uhuh, reset zero flag and exit
		cmp	al,'N'		; a No response?
		je	wq_no		; yeah, just exit (ZF is set)
		cmp	al,'A'		; an Abort response?
		je	wq_abort	; yep, set carry flag and exit
		jmp	short wq_next	; none of the above; try again
wq_yes:		or	al,al		; reset zero flag to indicate Yes
wq_no:		pushf			; save flags
		mov	dx,of response	; write this
		mov	cx,1		; one character
		mov	bx,2		; to standard error
		mov	ah,40h		; with DOS function 40h
		int	21h
		popf			; recover return flags
		ret
wq_abort:	or	al,al		; reset zero flag so not taken as No
		stc			; set carry flag to indicate abort
		jmp	short wq_no	; write response to screen
wq_prompt	db	' (Y/N/A)? '	; prompt user with <(Yes, No, Abort)?>
wqp_size	equ	$-wq_prompt	; size of prompt
response	db	?		; place to store user response to query
wild_query?	endp

;-------------------------------------------------------------------------------
; Here we show the user the command that will be executed next by WILD and if
; the WILD environment variable contains the QUERY option, then we will prompt
; (Y/N/A)? also.
;-------------------------------------------------------------------------------

wild_prompt	proc	near		; output to std out the command EXECed
		mov	dx,of wild_pmt	; display "WILD>" lead in
		mov	cx,wildpmt_size	; size of lead in
		mov	bx,2		; write to file handle 2 (std error)
		mov	ah,40h		; with DOS function 40h
		int	21h
		mov	dx,of cmdl+3	; point to command to be EXECed
		mov	cx,copy_size	; number of bytes to send to std out
		dec	cx		; but not the return at end of line
		mov	bx,2		; write to file handle 2 (std error)
		mov	ah,40h		; with DOS function 40h
		int	21h
		call	wild_query?	; send a query?
		pushf			; save response
		mov	dx,of wild_rtlf	; return/linefeed to std out
		mov	cx,2		; just 2 characters to send
		mov	bx,2		; write to file handle 2 (std error)
		mov	ah,40h		; with DOS function 40h
		int	21h
		popf			; restore response
		ret
wild_pmt	db	rt,lf,'WILD>'	; lead in for WILD
wildpmt_size	equ	$-wild_pmt
wild_rtlf	db	rt,lf		; newline after "prompt"
wild_prompt	endp

find_rest:	call	store_filename	; store filename as ASCIIZ in buffer
		mov	ah,4fh		; DOS continue file search function
		int	21h
		or	ax,ax		; return code nonzero?
		jnz	all_found	; yes, all files have been found
		jmp	short find_rest	; continue file find

all_found:	mov	di,buffer_ptr	; get file buffer pointer
		mov	bptr [di],0	; double null indicates end of buffer
		mov	di,of buffer	; start now at beginning of buffer
		mov	buffer_ptr,di	;   to process filenames
next_file:	call	copy_command	; copy command with filename to EXEC's
					;   copy of the command line
		jz	exit		; zero flag set indicates completion
		call	wild_prompt	; command to screen and maybe prompt
		jz	next_file	; user said no to this one
		jc	exit		; user wants to abort
		mov	ss_save,ss	; save stack to restore upon return from
		mov	sp_save,sp	;   the EXEC

		lds	dx,cspc_addr	; ds:dx points to name of prog to EXEC
		mov	bx,of params	; es:bx points to parameter block
		mov	ax,4b00h	; execute program after loading it
		int	21h

		mov	ss,cs:ss_save	; restore our stack
		mov	sp,cs:sp_save

		mov	ax,cs		; cs --> ds, es
		mov	ds,ax
		mov	es,ax
		jmp	short next_file

exit:		mov	ax,4c00h	; terminate with error code 0
		int	21h

buffer:		; store list of ASCIIZ filenames here prior to processing

;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
; The following routines will be used before the buffer is used and not again
; after the buffer begins to fill with ASCIIZ filenames from the current
; directory.  Since memory is saved by overwriting them, that is why they are
; placed here.  Even though this code and data will be overwritten as the file-
; name buffer begins to fill with files, the memory saved for the program's use
; includes all memory where this code and data reside.  A filename buffer is
; used to store the names of files obtained from the current directory because
; after the EXEC function is used DOS somehow looses some of the information it
; needs for the performance of the Continue File Search function.
;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

;-------------------------------------------------------------------------------
; We use this routine to check for the presence of a space, tab or the beginning
; of the command line in order to determine the location of the start of a wild
; card specification.
;-------------------------------------------------------------------------------

space_tab_left	proc	near		; return with Z flag set if di points to
		cmp	bptr [di],' '	;   a space
		je	stl_z
		cmp	bptr [di],9	;   a tab
		je	stl_z
		cmp	di,of cmd_line	;   or the start of the command line
		je	stl_z
		or	al,al		; reset Z flag
stl_z:		ret
space_tab_left	endp

;-------------------------------------------------------------------------------
; Checks for invalid filename characters following a wild card character to
; signal the end of the wild card spec.  Thus the space character--although it
; is not included in my PC DOS 3.20 manual as an invalid character (oversight?)
; --is included among them.  Also the period, even though it is an invalid
; filename character is not included among those checked since it is valid as a
; character in a wild card specification (same with * and ?).
;-------------------------------------------------------------------------------

check_invalid	proc	near		; return with Z flag set if di points to
		mov	ah,[di]		;   an invalid filename character
		push	ax		; save wild card test character
		cmp	ah,' '		; space or less than?
		jbe	ci_z		; yes, return with zero flag set
		mov	si,of inv_chars	; point to set of invalid characters
		cld			; (for maintenance and documentation)
get_inv:	lodsb			; get one of these
		or	al,al		; end of invalid character set?
		jz	ci_nz		; yes, return with zero flag reset
		cmp	ah,al		; an invalid character
		je	ci_z		; yes, return with zero flag set
		jmp	short get_inv	; test another
ci_z:		pop	ax		; restore wild card test character
		xor	ah,ah		; set zero flag
		ret			; return (zero flag is set)
ci_nz:		pop	ax		; restore wild card test character
		or	al,al		; reset zero flag
		ret
inv_chars	db	'"/\[]:|< >+=;,',0	; invalid filename characters
check_invalid	endp

;-------------------------------------------------------------------------------
; This routine will find the wild card specification in the copy of the command
; line (cmd_line), execute the DOS find first function, place the filename
; obtained from this function into the command line copy (cmd_line), copy this
; command into the command line that will be passed to EXEC, and then return.
; The location of the wild card spec in cmd_line is referred to by the term
; "blank", since this is a "fill in the blank" operation.
;-------------------------------------------------------------------------------
		
find_first	proc	near		; find first file of wild card spec
		mov	al,rt		; search the end of command line first
		mov	di,81h		; start at first character
		mov	cx,127		; maximum is fewer, but this will do
		cld			; (for maintenance and documentation)
		repne	scasb		; find return character
		sub	di,81h		; compute number of characters to search
		mov	copy_size,di	; store here for later use

		mov	al,'*'		; first test for presence of an asterisk
search_wild:	mov	cx,copy_size	; install number of characters to search
		mov	di,of cmd_line+1; command line passed by DOS (copy)
		cld			; (for maintenance and documentation)
		repne	scasb		; do search
		jcxz	not_this_wild	; this wild card not found

		push	di		; save position of char after wild card
		dec	di		; move to wild card
back_one:	dec	di		; test character left
		call	space_tab_left	; check for space, tab or beginning
		jnz	back_one	; go back a character and look again
		inc	di		; point to first character in blank
		mov	left_blank,di	; store position as start of blank
		pop	di		; points to character after wild card
		dec	di		; undone by next instruction
over_one:	inc	di		; test one character right
		call	check_invalid	; check if an invalid filename character
		jnz	over_one	; go forward a character and look again
		dec	di		; point to last character in blank
		mov	right_blank,di	; store position as end of blank
		mov	si,left_blank	; address blank for copy
		mov	cx,di		; construct size of blank in cx
		inc	cx
		sub	cx,si
		mov	di,of wild_spec	; copy blank (wild spec) here
		cld			; (for maintenance and documentation)
		rep	movsb		; do the copy to work space
		mov	bptr [di],0	; make it an ASCIIZ string
		mov	dx,of wild_spec	; address with ds:dx for find first
		mov	cx,file_attr	; find files with these attributes
		mov	ah,4eh		; DOS find first function
		int	21h
		or	ax,ax		; return code nonzero?
		jnz	none_found	; yes, no files were found, return
		xor	ax,ax		; return code is zero
none_found:	ret

not_this_wild:	cmp	al,'?'		; did we search for a "?" yet?
		je	no_wild		; yes, no other wild
		mov	al,'?'		; no, so search for it too
		jmp	short search_wild
no_wild:	mov	ax,255		; return 255 if no wild card found
		ret
find_first	endp

cspc_str	db	'COMSPEC='	; COMSPEC string to find in environment
cspc_size	equ	$-cspc_str	; size of "COMSPEC=" string

notice		db	rt,lf,'WILD v1.0  Public Domain 1988 by Charles Lazo '
		db	'III, CIS userid 72210,17',rt,lf
notice_size	equ	$-notice

bad_DOS		db	rt,lf,'WILD:  Requires DOS version 2 or above.',rt,lf,0
mem_need	db	rt,lf,'WILD:  Not enough memory.',rt,lf,0
no_comspec	db	rt,lf,"WILD:  Can't find COMSPEC in environment."
		db	rt,lf,0
none_wild	db	rt,lf,'WILD:  No wild card characters were found.'
		db	rt,lf,0
no_file		db	rt,lf,'WILD:  No files were found.',rt,lf,0

;-------------------------------------------------------------------------------
; Execution is transferred here when an error has occured.  The code that has
; transferred control here will place in dx the offset of the error message that
; will be sent to standard error.  This error message must be terminated by a
; null.  The program then exits with error level set to 1.
;-------------------------------------------------------------------------------

error_exit:	mov	ax,cs		; assure ds and es at cs
		mov	ds,ax
		mov	es,ax
		mov	di,dx		; find the null terminating error string
		xor	al,al		; search for null
		cld			; (for maintenance and documentation)
		mov	cx,0ffffh	; largest possible
		repne	scasb		; find the null
		mov	cx,di		; compute number of characters to send
		sub	cx,dx		;   in cx
		dec	cx
		mov	bx,2		; send them to standard error
		mov	ah,40h		; with DOS function 40h
		int	21h
		mov	ax,4c01h	; exit with error code 1
		int	21h

;-------------------------------------------------------------------------------
; Execution comes here when the program begins.  First the DOS version is
; checked to be sure it is 2 or greater, then all memory is released except that
; which is needed to continue operation after the filename buffer begins to fill
; with filenames from the current directory.  (All code and data is retained,
; but that which lies beyond the label buffer: is overwritten by filenames as
; the filename buffer begins to fill.  If more memory is needed, then it is
; allocated by the set_di routine.)  The size of the environment is determined
; so that it can be used as a limit to the number of bytes to be searched for
; the two environment variables WILD and COMSPEC.  Next the string "WILD=" is
; searched for in the environment and if found, then the two data variables,
; switches and file_attr (defined at the top of this file), are initialized
; based upon the settings in this string.  Finally, the string "COMSPEC=" is
; searched for in the environment and if not found, the program sends a message
; to the screen and terminates with the error level set to 1.  If COMSPEC is
; found its segment and offset are stored in the variable cspc_addr to be used
; later to find the program (usually COMMAND.COM) that shall be used as a shell
; to run the command given by the user.
;-------------------------------------------------------------------------------

start:		mov	dx,of bad_DOS	; prepare wrong version message
		mov	ah,30h		; get DOS version number
		int	21h
		cmp	al,2		; DOS ver 2 or greater?
		jae	DOS_ok		; yes, good DOS version
		jmp	short error_exit; else send error and exit
DOS_ok:		mov	dx,of notice	; show notice information
		mov	cx,notice_size	; length of notice message
		mov	bx,2		; send notice message to standard error
		mov	ah,40h		; with DOS function 40h
		int	21h
		mov	bx,of last	; assure all code retained
		mov	ax,of buffer	; assure at least 256 bytes for buffer
		add	ax,256
		cmp	bx,ax		; use the larger of these two
		jae	bx_good		; bx larger is good enough
		mov	bx,ax		; make bx good enough
bx_good:	add	bx,0fh		; round up to next paragraph
		shr	bx,1		; calculate paragraphs
		shr	bx,1
		shr	bx,1
		shr	bx,1
		mov	memory_used,bx	; save here for later use
		mov	ah,4ah		; DOS modify block to reserve bx
		int	21h		;   paragraphs for code and buffer
		mov	dx,memory_used	; get number requested
		cmp	bx,dx		; is number available same as requested?
		mov	dx,of mem_need	; address memory needed error
		jz	modify_ok	; okay, on modify memory block
		jmp	error_exit	; error, not able to modify block

modify_ok:	mov	sp,of our_stack	; move stack to memory owned by us
		mov	si,80h		; offset of command line given us by DOS
		mov	di,of cmd_line	; place to copy command line (it is
		cld			;   overwritten by DOS find file calls)
		mov	cx,128		; copy 128 bytes (DOS legal limit)
		rep	movsb

		mov	ax,env_seg	; get segment of environment
		mov	params,ax	; and place it in parameter table
		mov	params+4,cs	; segment of command line
		mov	params+8,cs	; segment of first FCB
		mov	params+12,cs	; segment of second FCB

		jmp	find_env_size	; determine size of environment

;-------------------------------------------------------------------------------
; This one searches the environment for the string pointed to by es:di.  The
; number of characters in the string is provided in dx, bx has the number of
; bytes to be searched and ds:si points to the starting point of the search.
; If the string is found, then the carry flag is reset to indicate success and
; the offset of the string into the segment of the environment is returned in
; si.  If the search fails, then the carry flag is set prior to return.
;-------------------------------------------------------------------------------

search_str	proc	near		; search environment for string at di
		mov	cx,bx		; set number of bytes to search
next_byte:	push	cx		; save number of bytes to search
		push	si		; save start positions
		push	di
		mov	cx,dx		; number of characters in string at di
		cld			; (for maintenance and documentation)
		repe	cmpsb		; compare the two strings
		jcxz	str_found	; found string at di in the environment
		pop	di		; get 'em back
		pop	si
		inc	si		; bump pointer
		pop	cx		; get count
		loop	next_byte
		stc			; set carry; string at di not found
		ret
str_found:	pop	di		; remove stacked registers
		pop	si
		pop	cx
		clc			; clear carry; string at di found
		ret
search_str	endp

;-------------------------------------------------------------------------------
; Here we search for the meaningful values that may be contained in the WILD
; environment variable.  These values are QUERY, NOEXT, H, S, and D (capital-
; ization is not significant).  The file WILD.DOC gives a description of the
; meaning of each.  First the whole string is capitalized and then a search is
; made for QUERY and for NOEXT.  The first and/or second bit of the byte
; switches is set depending upon the success of these searches.  Then H(idden),
; S(ystem) and D(irectory) values are searched and if found, then corresponding
; bits are set in the file_attr variable.  Also data for this and related code
; is defined at the end of this routine.
;-------------------------------------------------------------------------------

wild_settings	proc	near		; set switches variable by WILD= string
		mov	cs:ws_start,si	; save offset of WILD variable setting
		xor	bx,bx		; count here the number of characters
		cld			; (for documentation and maintenance)
ws_next:	lodsb
		or	al,al		; is it the null at end of ASCIIZ?
		jz	ws_search	; yes, capitalization is done
		inc	bx		; count it
		cmp	al,'a'		; is it below an 'a'?
		jb	ws_next		; yes, continue
		cmp	al,'z'		; is it above a 'z'?
		ja	ws_next		; yes, continue
		and	bptr [si-1],0dfh; capitalize it
		jmp	short ws_next	; continue
ws_search:	mov	si,cs:ws_start	; get start of WILD variables
		mov	di,of query	; search for QUERY
		mov	dx,query_size	; size of query string
		call	search_str	; do the search
		jc	do_noext	; QUERY not found; look for NOEXT
		or	cs:switches,que	; set bit 0 to indicate QUERY found
do_noext:	mov	si,cs:ws_start	; get start of WILD variables
		mov	di,of noext	; search for NOEXT
		mov	dx,noext_size	; size of query string
		call	search_str	; do the search
		jc	do_hidden	; no NOEXT; look for hidden switch
		or	cs:switches,noe	; set bit 1 to indicate NOEXT found
do_hidden:	mov	si,cs:ws_start	; get start of WILD variables
		mov	di,of hidden	; search for hidden string
		mov	dx,hidden_size	; size of hidden string
		call	search_str	; do the search
		jc	do_system	; no hidden; look for system switch
		or	cs:file_attr,hid; set bit 1 in file attribute
do_system:	mov	si,cs:ws_start	; get start of WILD variables
		mov	di,of system	; search for system string
		mov	dx,system_size	; size of system string
		call	search_str	; do the search
		jc	do_direct	; no system; look for direct switch
		or	cs:file_attr,sys; set bit 2 in file attribute
do_direct:	mov	si,cs:ws_start	; get start of WILD variables
		mov	di,of direct	; search for direct string
		mov	dx,direct_size	; size of direct string
		call	search_str	; do the search
		jc	ws_done		; no direct; all done
		or	cs:file_attr,dir; set bit 4 in file attribute
ws_done:	ret
ws_start	dw	?		; pointer to start of WILD env variables
query		db	'QUERY'		; string to find in WILD env variable
query_size	equ	$-query
noext		db	'NOEXT'		; another string to find
noext_size	equ	$-noext
hidden		db	'H'		; find this, then include hidden files
hidden_size	equ	$-hidden
system		db	'S'		; find this, then include system files
system_size	equ	$-system
direct		db	'D'		; find this, then directory files
direct_size	equ	$-direct
que		equ	1		; set into switches if QUERY present
noe		equ	2		; set into switches if NOEXT present
hid		equ	2		; set into file_attr if hidden string
sys		equ	4		; set into file_attr if system string
dir		equ	16		; set into file_attr if direct string
wild_settings	endp

;-------------------------------------------------------------------------------
; Find the size of the environment so only this number of bytes will be searched
; for environment strings.
;-------------------------------------------------------------------------------

find_env_size:	mov	ax,env_seg	; segment of environment --> es
		mov	es,ax
		xor	di,di		; search begins at start of environment
		xor	al,al		; search for nulls in environment
		mov	cx,8000h	; environment is no larger than 32k
next_word:	repne	scasb		; find a null
		scasb			; a double null here?
		jz	found_env_end	; yes, signals end of environment block
		jmp	short next_word	; else continue searching

found_env_end:	mov	ax,8000h	; number of bytes in environment --> cx
		xchg	ax,cx
		sub	cx,ax
		mov	env_size,cx	; store size of our environment

;-------------------------------------------------------------------------------
; Setup segment registers for environment searches.
;-------------------------------------------------------------------------------
		mov	ax,env_seg	; segment of environment --> ds
		mov	ds,ax
		push	cs		; cs --> es
		pop	es
		jmp	short wild_env	; handle wild environment variables

;-------------------------------------------------------------------------------
; First search for the string "WILD=" in the environment and if it is found,
; then set program switches depending upon what the WILD environment variable
; is set to.
;-------------------------------------------------------------------------------
wild_str	db	'WILD='		; WILD's environment variable string
wild_size	equ	$-wild_str	; size of "WILD=" string

wild_env:	xor	si,si		; point to start of environment
		mov	dx,wild_size	; characters in string "WILD=" --> dx
		mov	di,of wild_str	; address WILD environment string
		mov	bx,cs:env_size	; number of bytes to search for string
		call	search_str	; find WILD string (if present)
		jc	do_cspc		; WILD string not found; do COMSPEC
		add	si,wild_size	; point si to value of WILD variable
		call	wild_settings	; record switch settings in environment

;-------------------------------------------------------------------------------
; Now, search for "COMSPEC=" in the environment.  If it is found, then it will
; be used to provide the copy of COMMAND.COM to be used for the EXEC call(s).
; If it is not found, then we exit with an error message.
;-------------------------------------------------------------------------------
do_cspc:	mov	bx,cs:env_size	; number of bytes to search for COMSPEC
		xor	si,si		; point to start of environment
		mov	dx,cspc_size	; characters in string "COMSPEC=" --> dx
		mov	di,of cspc_str	; address COMSPEC string
		call	search_str	; find COMSPEC string (if present)
		jnc	comspec_found	; no carry, COMSPEC string was found

		mov	dx,of no_comspec; tell can't find COMSPEC, then exit
		jmp	error_exit

comspec_found:	add	si,cspc_size		; make si point to COMSPEC value
		mov	wp cs:cspc_addr,si	; store comspec offset
		mov	wp cs:cspc_addr+2,ds	; store comspec segment
		push	cs		; cs --> ds
		pop	ds		; (now cs, ds, and es are all same)
		call	find_first	; find the first file in the directory
		cmp	ax,255		; special return for no wild card found?
		jne	wild_found	; no, check if a file was found
		mov	dx,of none_wild	; send this message and end
		jmp	error_exit
wild_found:	or	ax,ax		; a file found?
		jnz	none_here	; no, say so and end
		jmp	find_rest	; yes, find the remainder of files
none_here:	mov	dx,of no_file	; address no file message
		jmp	error_exit

last:		; offset of end of program.  Used to assure that all code is
		; retained with memory modify operation at the start of code.

code		ends
		end	begin
