'===========================================================================
' Subject: CHECK IF FILE EXISTS               Date: Unknown Date           
'  Author: Logan Ashby/Andy Thomas            Code: QB, PDS                
'  Origin: FidoNet QUIK_BAS Echo            Packet: DOS.ABC
'===========================================================================
' >    5) Procedures must be bulletproof.
' >       FUNCTION Exist - Returns true if file is present.
'
' Sounds like some interesting challenges, but it struck me as
' odd, you want to see "bulletproof" routines, which I take to
' mean as routines that do a lot of error-checking, yet your
' Exist function could be shot full of holes, to continue the
' metaphor, fairly easily. Here's something I whipped up from my
' own Exist function, I bulletproofed and commented it as heavily
' as I could. 

 DECLARE FUNCTION Exist% (seed$, SearchAttrb%)
 DECLARE FUNCTION floppyDriveReady% (drive$)

 TYPE regtype             ' Also found in QB.BI
   ax AS INTEGER
   bx AS INTEGER
   cx AS INTEGER
   dx AS INTEGER
   bp AS INTEGER
   si AS INTEGER
   di AS INTEGER
  flags AS INTEGER
   ds AS INTEGER
   es AS INTEGER
 END TYPE

 TYPE DTAdata                     'used by DOS services
   Reserved  AS STRING * 21       'reserved for use by DOS
   Attribute AS STRING * 1        'the file's attribute
   FileTime  AS STRING * 2        'the file's time
   Filedate  AS STRING * 2        'the file's date
   FileSize  AS LONG              'the file's size
   filename  AS STRING * 13       'the file's name
 END TYPE

 END

 DEFINT A-Z
 FUNCTION Exist% (Name$, SearchAttrb%)

 ' Format:
 ' EXIST Name$, SearchAttrb%
 ' Name$ can be any valid DOS filename, directory name, or volume label.
 '     wildcards (* and ?) are accepted.
 ' Attrb% can be the following:
 '     0 == Test for any file
 '    39 == Test for any file
 '    16 == Test for Directory names ONLY
 '     8 == Test for Volume labels ONLY
 '     4 == Test for System files ONLY
 '     2 == Test for Hidden files ONLY
 '     1 == Test for Read-Only files ONLY
 '    63 == Test for anything file/label/directory
 '
 '  Combinations can be made (ie. search for Read-only
 '  Directories) by following this binary number bit chart:
 '     Bit 7  Shareable (Novell Netware, otherwise ignore)
 '     Bit 6  unused
 '     Bit 5  archive
 '     bit 4  Directory
 '     Bit 3  Volume Label
 '     Bit 2  system
 '     Bit 1  Hidden
 '     Bit 0  Read only
 '  for example a Read-only Directory would be bits 0 and 4,
 '  in binary numbers that's: 10001 or 17 decimal.

 ' If the tested for item exists Exist% will be set to -1, true
 '    and SearchAttrb% can be ignored

 ' If the tested for item does not exist, or there is an error,
 ' Exist% will be set to 0, false, and SearchAttrb% will be set
 ' to one of the following:
 '    -1 == Floppy drive not ready or invalid drive letter.
 '     0 == item does not exist.

 DIM inreg AS regtype, outreg AS regtype
 DIM DTA AS DTAdata

 seed$ = LTRIM$(RTRIM$(UCASE$(Name$)))

 IF SearchAttrb% AND 8 THEN  ' Volume label check
   ' Volume Label searches need to have a "." for the
   ' ninth character if the label is >8 characters.
   ' The following assures a correct search

   IF NOT (INSTR(seed$, ".")) THEN

     ' step backwards through the string

     FOR I = LEN(seed$) TO 1 STEP -1

       ' look for end of string, or drive/directory marker

       IF MID$(seed$, I, 1) = ":" OR MID$(seed$, I, 1) = "\" OR I = 1 THEN

         ' I points to start of name, without drive/directory
         ' marker, see if "." is required

         IF LEN(MID$(seed$, I + 1, LEN(seed$) - I)) > 8 THEN

           ' if no drive/directory, then we're checking the
           ' default drive, in this case I must equal 0 to
           ' place the "." correctly.

           IF I = 1 THEN I = 0

           ' place the "."

           seed$ = LEFT$(seed$, I) + MID$(seed$, I + 1, 8) + "." + MID$(seed$, I + 9, LEN(seed$) - I)
         END IF
         I = 1  ' exit the next loop
       END IF
     NEXT I
   END IF
 END IF

 IF SearchAttrb% = 0 THEN SearchAttrb% = 39  ' default search

 ' if there's a drive in the search string
 IF INSTR(seed$, ":") THEN
   drive$ = LEFT$(seed$, 1)   ' gets the drive
 ELSE
   drive$ = "@"               ' for default drive
 END IF

 ' if it's a floppy drive we need to make sure a disk
 ' is in the drive.
 IF NOT floppyDriveReady(drive$) THEN
   SearchAttrb% = -1   ' Floppy not ready.
   Exist% = 0
   EXIT FUNCTION
 END IF

 inreg.dx = VARPTR(DTA)      'set a new DOS DTA
 inreg.ds = VARSEG(DTA)
 inreg.ax = &H1A00
 CALL interruptx(&H21, inreg, outreg)

 seed$ = seed$ + CHR$(0)     'DOS needs ASCIIZ string
 inreg.ax = &H4E00           'find file name service
 inreg.cx = SearchAttrb%
 inreg.dx = SADD(seed$)      'show where the spec is
 inreg.ds = VARSEG(seed$)    'use this with QB - SSEG for PDS(?)
 CALL interruptx(&H21, inreg, outreg)

 IF (outreg.flags AND 1) THEN
   SearchAttrb% = 0          ' Item does not exist
   Exist% = 0
 ELSE
   Exist% = -1               ' item exists
 END IF

 END FUNCTION

 DEFINT A-Z
 FUNCTION floppyDriveReady% (drive$)
 DIM inreg AS regtype, outreg AS regtype

 ' This function may also be used independently from
 ' the Exist% function. It returns -1, true if the
 ' drive is ready, or 0, false, if the drive is not
 ' ready, or the drive letter is an invalid drive.

 drive% = (ASC(drive$) OR 32) - 97

 'reset floppy drive
 inreg.ax = 0
 inreg.dx = drive%
 CALL interruptx(&H13, inreg, outreg)

 inreg.ax = &H401     'verify disk sector
 inreg.cx = &H101
 inreg.dx = drive%
 CALL interruptx(&H13, inreg, inreg)
 'call the interrupt twice since if a disk has just been
 'inserted, the first time gives a wrong answer
 inreg.ax = &H401
 inreg.cx = &H101
 inreg.dx = drive%
 CALL interruptx(&H13, inreg, outreg)

 'if it was a hard disk we just checked forget the whole thing
 IF outreg.ax AND 256 THEN
   inreg.ax = &H1C00      ' check drive type
   inreg.dx = drive% + 1  ' diff. drive number system must add 1
   CALL interruptx(&H21, inreg, outreg)
   ' check if drive was a valid drive letter.
   IF (outreg.ax AND &HFF) = &HFF THEN HardCheck = 0 ELSE HardCheck = -1
 END IF

 floppyDriveReady% = ((outreg.flags AND 1) = 0) OR HardCheck

 END FUNCTION
