; 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
