               PAGE 60,132
TITLE  FINDFILE.COM  VER.2.0  14-AUG-83  19:15
comment *

                        FINDFILE.COM


        VERSION 2.0     14-AUG-83

        Written by      Warren Craycroft
                        6236 Oakdale Ave.
                        Oakland, CA  94605


        (C)  1983  by Warren Craycroft.  Permission is granted to copy and
        distribute this program, including source code, provided that no
        charge shall be made except for a reasonable charge for the media
        and handling, and that this notice shall remain intact in all copies.


*
comment *
        This program is a utility that can be used with DOS 2.0 to
        find the disk drive number that contains a certain file.

        A common use of FINDFILE is to help a batch file "orient"
        itself by discovering on which drive the Batch File itself is
        running, and on what drives other files (such as useful system
        utilities) can be found.  A lot of this orientation can be done
        without bothering or confusing the user with a lot of questions
        or constraints in locating disks in certain drives.

        A key feature of this search (and where this method of orien-
        tation differs from the use of PATH or IF EXIST ) is that
        searching on the disk drives is done "benignly".  By this we mean
        that FINDFILE doesn't come to a grinding halt if an empty
        disk drive is searched.  Instead, it continues the search on
        the rest of the drives.  This further relieves the user from
        having to locate certain disks on certain drives.  The target
        file may be on the B: or C: drive with the A: drive empty, and
        FINDFILE will find it without the annoying "ignore, abort, retry"
        message.  If the file is not found, then you may gently prod the
        user to place the required disk "in any drive" and try again.

        The syntax of the command line is the following:

                findfile  fname

        where fname is a standard DOS path, filename, and extention.
        If you include a drive specification (like "a:" for example),
        the drive spec will be ignored by FINDFILE.

        FINDFILE benignly searches all disk drives that  are claimed
        to present by the system board switches, in order A:, B:, C:, and
        D:.  If the file is found, the disk number of the first disk on
        which it was found is returned in errorlevel and may be tested
        by a Batch File IF Subcommand.


                If fname found:         errorlev = 0 if fname on A:
                                        errorlev = 1 if fname on B:
                                        errorlev = 2 if fname on C:
                                        errorlev = 3 if fname on D:

                If fname not found:     errorlev = 255

                If fname is missing:    errorlev = 255 , and a CNTRL BREAK
                                                   halts the Batch File

*
;
;               constant equates
;
BEL_CHAR        EQU     07              ;ascii BEL keycode
CR              EQU     0DH             ;ascii carriage return
LF              EQU     0AH             ;ascii line feed
TAB_CHAR        EQU     09              ;ascii tab
BLANK_CHAR      EQU     ' '             ;ascii blank
COLON_CHAR      EQU     ':'             ;ascii colon
NOT_FOUND_CODE  EQU     255             ;errorlev returned for fname not
                                        ;found
DISK_MASK       EQU     00C0H           ;mask for equipment word's disk
                                        ;count field
;
;
;       declare a relocatable segment.  Follow the .COM file requirements
;       of entry point at 100H and making all seg register references relative
;       to CS (no relocatable values MOV'ed into segment registers).
;
;
COM_CODE      SEGMENT
;
                ORG     80H             ;PSP offset 80:  user's command line
PSP_CMD_LINE    LABEL   BYTE            ;define a label for address refs
;
                ORG     100H            ;for COM file
;
;
                ASSUME  CS:COM_CODE,DS:COM_CODE    ;tell assembler value of CS
                ASSUME  ES:COM_CODE                ; DS, and ES
;
;
START           PROC            FAR     ;FAR is meaningless; no RETS
;
                MOV     SI,OFFSET PSP_CMD_LINE  ;offset of user's cmd line
                SUB     CX,CX                   ;clear CX
                MOV     CL,[SI]                 ;byte count of line
                OR      CL,CL                   ;zero?
                JZ      NO_FNAME                ;jump if yes, no fname param
                SUB     BX,BX                   ;else zero index
;
;       find first non-blank or non-tab character of command line
;
STRIP_BLANKS:   INC     SI                      ;point to next char
                MOV     AL,[SI]                 ;get next char from cmd line
                CMP     AL,BLANK_CHAR           ;is it blank?
                JE      LOOP_BLANKS             ;jump if yes, keep looking
                CMP     AL,TAB_CHAR             ;is it a tab?
                JNE     GOT_NON_BLANK           ;jump if not, first non-blank
LOOP_BLANKS:    LOOP    STRIP_BLANKS            ;else keep looking
;
;       fname is missing from command line (all blanks or tabs, or else
;       zero characters on line
;
NO_FNAME:       MOV     DX,OFFSET ERROR_MESSAGE ;adr offset of error message
                MOV     AH,9                    ;DOS fn call, "print string"
                INT     21H                     ;call DOS
                INT     23H                     ;cause a control break, cause
                                                ;this is probably a bug in
                                                ;batch file creation.
                JMP     NO_FILE_EXIT            ;then exit with errorlevel =
                                                ; "NOT_FOUND_CODE"
;
;       found first non-blank/tab character on command line
;       at [SI][BX] .  Check for disk designator " n: " and
;       "strip" it from string by moving up pointer and decreasing
;       remaining bytes count.
;
GOT_NON_BLANK:  CMP     BYTE PTR [SI+1],COLON_CHAR  ;2nd non-blank char ":"?
                JNE     ADD_DRIVE               ;if not, [SI] is start of strng
                INC     SI                      ;else strip off the drive spec
                INC     SI
                DEC     CX                      ;and decr remaining byte count
                JZ      NO_FNAME                ;if string hits zero bytes left,
                DEC     CX
                JZ      NO_FNAME                ;  it's a "no param" error
;
;       SI now points to start of ASCIIZ string that we want to use as
;       the path/file name to search drives for
;
;       Put a byte of zeros at the end of string, and an "A:" in front
;       of string to initialize search.
;
ADD_DRIVE:      MOV     BX,CX                   ;number of bytes remaining
                MOV     BYTE PTR [SI][BX],0     ;points to 1 char past end
                                                ; of string
                MOV     BYTE PTR [SI-2],'A'     ;"A:" in front of string
                MOV     BYTE PTR [SI-1],':'
;
;       Replace the current critical error handler with our own
;
;       First, get the address of the critical error handler assigned
;       by "parent" process (probably system) to this process.  Save its
;       address, so that we can use it if our handler is handed a non-disk
;       error  ( "we don't do non-disk errors ..." )
;
                MOV     AH,35H                  ;DOS fn "get vector"
                MOV     AL,24H                  ;"critical error" vector no.
                INT     21H                     ;call DOS
                MOV     WORD PTR PARENT_HAND,BX ;save offset vector adr
                MOV     WORD PTR PARENT_HAND+2,ES  ;save seg vector adr
;
;       Now, take control of critical error vector by assigning a handler
;       to the vector address.
;
;       DOS will restore the "parent"'s vector when this process returns to
;       DOS.
;
                MOV     DX,OFFSET CRIT_HAND     ;address of handler
                MOV     AL,24H                  ;the vector to be replaced
                MOV     AH,25H                  ;DOS fn call "ret int vector"
                INT     21H                     ;call DOS and replace vector
;
;       Get the number of drives that DOS knows about
;       Dont disturb the current default disk when getting the number
;
                MOV     AH,19H                  ;DOS fn "return current disk"
                INT     21H                     ;current disk returned in AL
                MOV     DL,AL                   ;put current in DL for sel call
                MOV     AH,0EH                  ;DOS fn "sel disk, return number
                INT     21H                     ;get number of drives
                SUB     CH,CH                   ;clear CH
                MOV     CL,AL                   ;number of drives
                INC     CX                      ;is now (1,2,3, or 4)
;
;       Now loop through all drives present, trying to find the file
;       (using fn call 4EH) on each drive.  If successful, return the
;       disk number to caller in errorlevel.
;
;       If not successful, return errorlevel = NOT_FOUND_CODE
;
                MOV     DX,SI                   ;offset adr of drive spec and
                DEC     DX                      ;  filename
                DEC     DX
FIND_FILE:      MOV     AH,4EH                  ;DOS fn "find first file"
                PUSH    CX                      ;save the loop count
                SUB     CX,CX                   ;attribute says search
                                                ;"normal" files only
                PUSH    [SI-2]                  ;save drive letter (DOS call
                                                ;destroys it if file found)
                INT     21H                     ;call DOS
                POP     [SI-2]                  ;restore drive letter
                POP     CX                      ;restore loop count
                JNC     FOUND_FILE              ;if carry not set, then search
                                                ;was successful - jump.
                INC     BYTE PTR [SI-2]         ;increment drive letter char
                LOOP    FIND_FILE               ;and look on next drive
;
;       did not find file.  Return NOT_FOUND_CODE in errorlevel
;
NO_FILE_EXIT:   MOV     AL,NOT_FOUND_CODE       ;file not found on any drive
EXIT:           MOV     AH,4CH                  ;DOS fn call to exit w/ errlev
                INT     21H                     ;call DOS and exit
;
;       found the file.  Retrieve the drive letter used in this last call,
;       map it to disk number (A = 0, B = 1, etc) and return it in errorlevel.
;
FOUND_FILE:     MOV     AL,[SI-2]               ;get the drive letter (upper
                                                ;case ascii code)
                SUB     AL,'A'                  ;map it to 0,1,2,3
                JMP     EXIT                    ;and return it in errorlevel
START           ENDP
page
;
;               CRIT_HAND               Critical Error Handler
;
;       This handler will be called by DOS functions when any critical
;       error (disk or non-disk) occurs during DOS INT 21H calls that
;       are made during execution of this process.
;
;       We only want to ignore disk errors, so we first test if the disk
;       is the cause of error.
;
;       If not a disk error, we jump to the parent's critical error handler
;       (we JMP so that stack looks as if we were never there).
;
;       If it is a disk error, we return an IGNORE code to the calling
;       DOS function.  The effect of all this is that when a DOS function
;       tries to access a disk drive that has a door open, or no disk, or
;       disk is unformatted, or disk is garbled, etc, the DOS function will
;       simply give up and return to the function's caller "empty handed".
;       There will be no "abort, retry, ignore" message from the critical
;       error handler.
;
;       on entry:       Bit 7 of AH = 0 if disk error
;                                   = 1 if not a disk error
;
;       on exit:        If disk error, we set AL = 0, "ignore error"
;
;       registers affected:     AH, AL
;
CRIT_HAND       PROC    FAR                     ;RETS are typed FAR
                TEST    AH,80H                  ;bit 7 on?
                JNZ     NOT_DISK                ;jump if not disk error, we dont
                                                ;treat it
;
;       Treatment of disk errors is easy, just tell system to ignore them
;
                SUB     AL,AL                   ;zero AL, "ignore" code
                IRET                            ;caller used INT so we IRET
;
;       It was not a disk error, so let the "parent" critical error handler
;       treat it by JMPing to its address that we saved.
;
;       We JMP so we dont disturb the stack.  Stack looks like we were
;       never called.
;
NOT_DISK:       JMP     DWORD PTR PARENT_HAND   ;jump to parent crit err hand
;
CRIT_HAND       ENDP
;
;
;       temporary storage of parent's critical handler address
;
PARENT_HAND     DD      1 DUP(?)                ;double word for FAR address
;
;
;
;       error message string
;
ERROR_MESSAGE   DB      CR,LF,'***** No filename was given with '
                DB      'FINDFILE *****',CR,LF,'$'
;
;
COM_CODE        ENDS
                END     START
