REM file: Whereis.bas - Public Domain DOS Utility
REM Version 1.0a created 01/20/1995
REM Version 1.1a created 02/05/1995
REM Version 1.2a created 06/08/1995
REM Version 1.3a created 04/30/1996
REM Version 1.4a created 05/09/1996
REM Version 1.5a created 01/21/1997
REM Version 1.6a created 01/21/1997

REM Compiling with Microsoft BASIC Professional Development System 7.1:
REM    BC WHEREIS/FS/X/O;
REM    LINK WHEREIS,,,QBX+DTFMTER/E;

' declare subroutines
DECLARE SUB Directories (D$, F$)
DECLARE SUB Filenames (D$, F$)
DECLARE SUB DisplayFiles (D$, F$)
DECLARE SUB ExecuteCommand (F$)
DECLARE SUB ReadAccessDate (D$, F$, C%)

' default integer variables
DEFINT A-Z
REM $DYNAMIC

' define boolean values
CONST True = -1
CONST False = NOT True
CONST TrueD = -1#
CONST FalseD = NOT TrueD
CONST NUL = ""

' define color values
CONST Black = 0
CONST Cyan = 11
CONST Green = 10
CONST Plain = 7
CONST Red = 12
CONST White = 15
CONST Yellow = 14

' get include files
REM $INCLUDE: 'qbx.bi'
REM $INCLUDE: 'dta.bi'
REM $INCLUDE: 'dta2.bi'
REM $INCLUDE: 'fcb.bi'
REM $INCLUDE: 'wdta.bi'
REM $INCLUDE: 'bpb.bi'
REM $INCLUDE: 'format.bi'

' declare functions
DECLARE FUNCTION ParseLine (S$)

' initialize filename buffer
DIM ASCIZ AS STRING * 260
COMMON SHARED Directory.ASCIZ AS STRING * 260, ASCIZ.Sub AS STRING * 260
COMMON SHARED Drive.Search AS STRING * 1, ASCIZ.File AS STRING * 260
COMMON SHARED ASCIZ.Display AS STRING * 260, ASCIZ.Short AS STRING * 68

' declare program dta
DIM BASIC.DTA.SEG AS INTEGER, BASIC.DTA.OFF AS INTEGER
COMMON SHARED TempDTA AS DTAtype, TempWDTA AS WDTAtype
COMMON SHARED TreeWDTA AS WDTAtype, FileWDTA AS WDTAtype

' declare registers
COMMON SHARED InregsX AS RegTypeX, OutregsX AS RegTypeX
COMMON SHARED Inregs AS RegType, Outregs AS RegType

' declare work variables
COMMON SHARED Files.Counter AS INTEGER, Quit.Searching AS INTEGER
COMMON SHARED Continuous.Display AS INTEGER, Double.Line AS INTEGER
COMMON SHARED Files.Counted AS INTEGER, More.Display AS INTEGER

' declare search work variables
COMMON SHARED Search.From.Date AS SINGLE, Search.To.Date AS SINGLE
COMMON SHARED Search.From.Time AS SINGLE, Search.To.Time AS SINGLE
COMMON SHARED File.Size AS DOUBLE, Recurse.Directories AS INTEGER
COMMON SHARED Display.Hidden AS INTEGER, Display.System AS INTEGER
COMMON SHARED Display.Readonly AS INTEGER, Display.Archive AS INTEGER
COMMON SHARED Display.Attribute AS INTEGER, Display.Errors AS INTEGER
COMMON SHARED Display.Volume AS INTEGER, Redirected.Input AS INTEGER
COMMON SHARED Display.Any AS INTEGER, No.Display.Archive AS INTEGER
COMMON SHARED No.Display.Readonly AS INTEGER, No.Display.System AS INTEGER
COMMON SHARED No.Display.Hidden AS INTEGER, No.Display.Any AS INTEGER
COMMON SHARED No.Display.Directory AS INTEGER, No.Display.Drive AS INTEGER
COMMON SHARED Display.Directory AS INTEGER, Short.Display AS INTEGER
COMMON SHARED Wide.Filename AS INTEGER, Wide.List AS INTEGER
COMMON SHARED Wide.Display AS INTEGER

' declare file date/time and filesize work variables
COMMON SHARED File.Work.Date AS SINGLE, File.Work.Time AS SINGLE
COMMON SHARED Total.Bytes AS DOUBLE, Parameters() AS STRING * 260
COMMON SHARED Search.Size.From AS DOUBLE, Search.Size.To AS DOUBLE
COMMON SHARED Search.File.Size, Display.Lowercase AS INTEGER
COMMON SHARED Creation.Time AS INTEGER, Short.Filenames AS INTEGER
COMMON SHARED Access.Time AS INTEGER, Modified.Time AS INTEGER

' declare nest recursion variables
COMMON SHARED Nested.Recurse AS INTEGER, Nested.Levels AS INTEGER

' declare command line work variables
COMMON SHARED Command.Line AS STRING, Command.Line.Redirect AS STRING
COMMON SHARED Command.Work AS STRING

' declare dos command work variables
COMMON SHARED DOS.Command AS STRING, Windows.Detected AS INTEGER
COMMON SHARED Windows.DOS AS INTEGER, Comspec AS STRING * 128
REDIM Parameters(1 TO 12) AS STRING * 260

' increase stack size
STACK STACK

' declare standard error trap
ON ERROR GOTO Error.Routine

' command line parser
FUNCTION ParseLine (X$)
 Imbedded = INSTR(Command.Line, LCASE$(X$))
 IF Imbedded THEN
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + LEN(X$))
    ParseLine = True
 ELSE
    Imbedded = INSTR(Command.Line, UCASE$(X$))
    IF Imbedded THEN
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + LEN(X$))
       ParseLine = True
    ELSE
       ParseLine = False
    END IF
 END IF
END FUNCTION 

' store basic dta
InregsX.AX = &H2F00
CALL InterruptX(&H21, InregsX, OutregsX)
BASIC.DTA.SEG = OutregsX.ES
BASIC.DTA.OFF = OutregsX.BX

' get current drive
InregsX.AX = &H1900
CALL InterruptX(&H21, InregsX, OutregsX)
Current.Drive$ = CHR$((OutregsX.AX AND &HFF) + 65)

' check windows dos
InregsX.AX = &H160A
CALL InterruptX(&H2F, InregsX, OutregsX)
IF OutregsX.AX = False THEN
   Temp = (OutregsX.BX And &HFF00) / 256
   IF Temp >= 4 THEN
      Windows.Detected = True
   END IF
END IF
InregsX.AX = &H4A33
CALL InterruptX(&H2F, InregsX, OutregsX)
IF OutregsX.AX = False THEN
   Windows.DOS = True
END IF

' check windows dos
IF Windows.Detected THEN
   ' get current directory
   InregsX.AX = &H7147
   InregsX.DX = ASC(Current.Drive$) - 64
   InregsX.DS = VARSEG(Directory.ASCIZ)
   InregsX.SI = VARPTR(Directory.ASCIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
ELSE
   ' get current directory
   InregsX.AX = &H4700
   InregsX.DX = ASC(Current.Drive$) - 64
   InregsX.DS = VARSEG(Directory.ASCIZ)
   InregsX.SI = VARPTR(Directory.ASCIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
END IF

' display any errors
CALL DisplayError ("Error accessing drive.")

' store directory
Directory.ASCIZ = "\" + RTRIM$(Directory.ASCIZ) + CHR$(0)

' check command line
SELECT CASE COMMAND$
CASE "/?"
   GOTO Boot.Usage
END SELECT

' read command line from PSP
Command.Line = NUL
InregsX.AX = &H6200
CALL InterruptX(&H21, InregsX, OutregsX)
PSPsegment = OutregsX.BX
PSPoffset = 128
DEF SEG = PSPsegment
FOR Count = 1 TO 127
   Command.Char = PEEK(PSPoffset + Count)
   SELECT CASE Command.Char
   CASE 0, 10, 13
      EXIT FOR
   CASE ELSE
      Command.Line = Command.Line + CHR$(Command.Char)
   END SELECT
NEXT
DEF SEG
Command.Line = LTRIM$(Command.Line)

' store and parse command line
IF Command.Line = NUL THEN
   Command.Line = ENVIRON$("WHEREIS")
END IF
Comspec = ENVIRON$("COMSPEC")
DOS.Command = NUL
' get dos command
IF RIGHT$(Command.Line, 1) = "]" THEN
   Imbedded = INSTR(Command.Line, "[")
   IF Imbedded THEN
      DOS.Command = MID$(Command.Line, Imbedded + 1)
      DOS.Command = LEFT$(DOS.Command, LEN(DOS.Command) - 1)
      Command.Line = LEFT$(Command.Line, Imbedded - 1)
   END IF
END IF
IF RIGHT$(Command.Line, 1) = "'" THEN
   Imbedded = INSTR(Command.Line, "'")
   IF Imbedded THEN
      DOS.Command = MID$(Command.Line, Imbedded + 1)
      DOS.Command = LEFT$(DOS.Command, LEN(DOS.Command) - 1)
      Command.Line = LEFT$(Command.Line, Imbedded - 1)
   END IF
END IF

' edit special DOS command parameters
Imbedded = INSTR(DOS.Command, "//1")
WHILE Imbedded
   DOS.Command = LEFT$(DOS.Command, Imbedded - 1) + ">" + MID$(DOS.Command, Imbedded + 3)
   Imbedded = INSTR(DOS.Command, "//1")
WEND
Imbedded = INSTR(DOS.Command, "//2")
WHILE Imbedded
   DOS.Command = LEFT$(DOS.Command, Imbedded - 1) + "<" + MID$(DOS.Command, Imbedded + 3)
   Imbedded = INSTR(DOS.Command, "//2")
WEND
Imbedded = INSTR(DOS.Command, "//3")
WHILE Imbedded
   DOS.Command = LEFT$(DOS.Command, Imbedded - 1) + "|" + MID$(DOS.Command, Imbedded + 3)
   Imbedded = INSTR(DOS.Command, "//3")
WEND
Imbedded = INSTR(DOS.Command, "//4")
WHILE Imbedded
   DOS.Command = LEFT$(DOS.Command, Imbedded - 1) + "%" + MID$(DOS.Command, Imbedded + 3)
   Imbedded = INSTR(DOS.Command, "//4")
WEND

' check command line switches
Display.Archive = ParseLine ("+A")
Display.Hidden = ParseLine ("+H")
Display.Directory = ParseLine ("+I")
Display.Readonly = ParseLine ("+O")
Display.System = ParseLine ("+S")
Display.Any = ParseLine ("+X")
No.Display.Archive = ParseLine ("/A")
No.Display.Hidden = ParseLine ("/H")
No.Display.Directory = ParseLine ("/I")
No.Display.Readonly = ParseLine ("/O")
No.Display.System = ParseLine ("/S")
No.Display.Any = ParseLine ("/X")
No.Display.Drive = ParseLine ("/B")
Continuous.Display = ParseLine ("/C")
Short.Display = ParseLine ("/E")
Short.Filenames = ParseLine ("/K")
Double.Line = ParseLine ("/L")
Recurse.Directories = ParseLine ("/R")
Display.Volume = ParseLine ("/V")
Wide.Filename = ParseLine ("/W")
Display.Lowercase = ParseLine ("/Y")
Display.Errors = ParseLine ("/Z")

' set attribute display variable
Display.Attribute = False
IF Display.Archive OR No.Display.Archive THEN
   Display.Attribute = True
END IF
IF Display.Hidden OR No.Display.Hidden THEN
   Display.Attribute = True
END IF
IF Display.Readonly OR No.Display.Readonly THEN
   Display.Attribute = True
END IF
IF Display.System OR No.Display.System THEN
   Display.Attribute = True
END IF
IF Display.Any OR No.Display.Any THEN
   Display.Attribute = True
END IF
IF Display.Directory OR No.Display.Directory THEN
   Display.Attribute = True
END IF
IF Display.Volume THEN
   Display.Attribute = True
END IF

' reset file counter variable
Files.Counted = False
More.Display = False
   
' get date/time from command line
Search.From.Date = False
Search.To.Date = False
Search.From.Time = False
Search.To.Time = False
Imbedded = INSTR(UCASE$(Command.Line), "/D")
IF Imbedded THEN
   D$ = MID$(Command.Line, Imbedded + 2, 21)
   Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 23)
   IF LEN(D$) <> 21 THEN
      GOTO Boot.Error
   END IF
   IF MID$(D$, 11, 1) <> "-" THEN
      GOTO Boot.Error
   END IF
   S$ = LEFT$(D$, 10)
   D1! = INT(VAL(MID$(S$, 1, 2)))
   D2! = INT(VAL(MID$(S$, 4, 2)))
   D3! = INT(VAL(MID$(S$, 7, 4)))
   Search.From.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
   S$ = RIGHT$(D$, 10)
   D1! = INT(VAL(MID$(S$, 1, 2)))
   D2! = INT(VAL(MID$(S$, 4, 2)))
   D3! = INT(VAL(MID$(S$, 7, 4)))
   Search.To.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
   IF Search.From.Date < False OR Search.To.Date < False THEN
      GOTO Boot.Error
   END IF
END IF
Imbedded = INSTR(UCASE$(Command.Line), "/T")
IF Imbedded THEN
   T$ = MID$(Command.Line, Imbedded + 2, 17)
   Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 19)
   IF LEN(T$) <> 17 THEN
      GOTO Boot.Error
   END IF
   IF MID$(T$, 9, 1) <> "-" THEN
      GOTO Boot.Error
   END IF
   S$ = LEFT$(T$, 8)
   T1! = INT(VAL(MID$(S$, 1, 2)))
   T2! = INT(VAL(MID$(S$, 4, 2)))
   T3! = INT(VAL(MID$(S$, 7, 2)))
   Search.From.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
   S$ = RIGHT$(T$, 8)
   T1! = INT(VAL(MID$(S$, 1, 2)))
   T2! = INT(VAL(MID$(S$, 4, 2)))
   T3! = INT(VAL(MID$(S$, 7, 2)))
   Search.To.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
   IF Search.From.Time < False OR Search.To.Time < False THEN
      GOTO Boot.Error
   END IF
END IF

' get file size from command line
Search.File.Size = False
Search.Size.From = FalseD
Search.Size.To = FalseD
Imbedded = INSTR(UCASE$(Command.Line), "/F")
IF Imbedded THEN
   Search.File.Size = True
   Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
   GOSUB Get.Numeric
   Search.Size.From = Var#
   IF MID$(Command.Line, Imbedded, 1) <> "-" THEN
      GOTO Boot.Usage
   END IF
   Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 1)
   GOSUB Get.Numeric
   Search.Size.To = Var#
   Search.Size.From = Search.Size.From * 1024#
   Search.Size.To = Search.Size.To * 1024#
END IF

Creation.Time = ParseLine ("/1")
Access.Time = ParseLine ("/2")
Modified.Time = ParseLine ("/3")
IF Creation.Time = False THEN
   IF Access.Time = False THEN
      IF Modified.Time = False THEN
         Creation.Time = True
      END IF
   END IF
END IF

' get nested switch from command line
Imbedded = INSTR(UCASE$(Command.Line), "/N")
IF Imbedded THEN
   Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
   GOSUB Get.Numeric
   Nested.Recurse = CINT(Var#)
END IF

' recheck command line
IF INSTR(Command.Line, "/") THEN
   GOTO Boot.Usage
END IF

' recheck command line
IF INSTR(Command.Line, "+") THEN
   GOTO Boot.Usage
END IF

' reset work variables
Files.Counter = False
Nested.Levels = False
Quit.Searching = False
   
' remove blanks from command line
Command.Line = RTRIM$(Command.Line)
Command.Line = LTRIM$(Command.Line)
Command.Line.Redirect = Command.Line

' make header
IF Continuous.Display = False THEN
   COLOR White, Black
   PRINT "Whereis v1.6a: File search utility; "
END IF
   
' search through all input filenames
DO
   ' get standard input
   Redirected.Input = False
   Standard.Input$ = NUL
   Inregs.AX = &HB00
   CALL Interrupt(&H21, Inregs, Outregs)
   DO WHILE (Outregs.AX AND &HFF) = &HFF
      Redirected.Input = True
      Inregs.AX = &H800
      CALL Interrupt(&H21, Inregs, Outregs)
      Char$ = CHR$(Outregs.AX AND &HFF)
      SELECT CASE ASC(Char$)
      CASE 10, 26
      CASE 13
	 EXIT DO
      CASE ELSE
	 Standard.Input$ = Standard.Input$ + Char$
      END SELECT
      Inregs.AX = &HB00
      CALL Interrupt(&H21, Inregs, Outregs)
   LOOP

   ' check standard input
   IF Redirected.Input THEN
      IF Standard.Input$ = NUL THEN
	 EXIT DO
      END IF
   END IF

   ' store original command line
   Command.Line = Command.Line.Redirect

   ' filename processing loop
   DO
      ' store redirected input
      Standard.Input$ = RTRIM$(Standard.Input$)
      Standard.Input$ = LTRIM$(Standard.Input$)
      IF LEFT$(Standard.Input$, 1) = CHR$(34) THEN
         Standard.Input$ = MID$(Standard.Input$, 2)
      END IF
      IF RIGHT$(Standard.Input$, 1) = CHR$(34) THEN
         Standard.Input$ = LEFT$(Standard.Input$, LEN(Standard.Input$) - 1)
      END IF

      ' store entire command
      IF LEFT$(Command.Line, 1) = CHR$(34) THEN
         Imbedded = INSTR(2, Command.Line, CHR$(34))
         IF Imbedded THEN
            Command.Work = Standard.Input$ + MID$(Command.Line, 2, Imbedded - 2)
            Command.Line = MID$(Command.Line, Imbedded + 1)
         ELSE
            Command.Work = Standard.Input$ + Command.Line
            Command.Line = NUL
         END IF
      ELSE
         Imbedded = INSTR(Command.Line, " ")
         IF Imbedded THEN
            Command.Work = Standard.Input$ + LEFT$(Command.Line, Imbedded - 1)
            Command.Line = MID$(Command.Line, Imbedded + 1)
         ELSE
            Command.Work = Standard.Input$ + Command.Line
            Command.Line = NUL
         END IF
      END IF
      Command.Line = LTRIM$(Command.Line)
      Command.Line = RTRIM$(Command.Line)

      ' store current drive
      IF MID$(Command.Work, 2, 1) = ":" THEN
         Drive.Search = LEFT$(Command.Work, 1)
         Command.Work = MID$(Command.Work, 3)
      ELSE
	 Drive.Search = Current.Drive$
      END IF
      Drive.Search = UCASE$(Drive.Search)

      ' check windows dos
      IF Windows.Detected THEN
         ' get current directory
         InregsX.AX = &H7147
         InregsX.DX = ASC(Drive.Search) - 64
         InregsX.DS = VARSEG(ASCIZ)
         InregsX.SI = VARPTR(ASCIZ)
         CALL InterruptX(&H21, InregsX, OutregsX)
      ELSE
         ' get current directory
         InregsX.AX = &H4700
         InregsX.DX = ASC(Drive.Search) - 64
         InregsX.DS = VARSEG(ASCIZ)
         InregsX.SI = VARPTR(ASCIZ)
         CALL InterruptX(&H21, InregsX, OutregsX)
      END IF

      ' display any errors
      CALL DisplayError ("Error accessing drive.")

      ' check find first error
      IF (OutregsX.Flags AND &H1) = &H0 THEN

         ' store current directory
         Directory.Search$ = "\" + LEFT$(ASCIZ, INSTR(ASCIZ, CHR$(0)) - 1)
         Imbedded1 = INSTR(Command.Work, "\")
         Imbedded2 = Imbedded1
         WHILE Imbedded1
            Imbedded2 = Imbedded1
            Imbedded1 = INSTR(Imbedded1 + 1, Command.Work, "\")
         WEND
         IF Imbedded2 THEN
            Directory.Search$ = LEFT$(Command.Work, Imbedded2)
            Command.Work = MID$(Command.Work, Imbedded2 + 1)
         END IF
         IF RIGHT$(Directory.Search$, 1) <> "\" THEN
            Directory.Search$ = Directory.Search$ + "\"
         END IF
   
         ' get filename spec
         Filename.Search$ = Command.Work
         IF Filename.Search$ = NUL THEN
            Filename.Search$ = "*.*"
         END IF
         Command.Work = NUL
   
         ' change to drive
         InregsX.AX = &HE00
         InregsX.DX = ASC(Drive.Search) - 65
         CALL InterruptX(&H21, InregsX, OutregsX)
   
         ' display any errors
         CALL DisplayError ("Error accessing drive.")

         ' check find first error
         IF (OutregsX.Flags AND &H1) = &H0 THEN

            ' display search filename
            IF Continuous.Display = False THEN
               COLOR Yellow, Black
               PRINT "Searching: " + Drive.Search + ":" + Directory.Search$ + Filename.Search$
               Files.Counter = Files.Counter + 1
            END IF

            ' call routine to search for files
            CALL Directories(Directory.Search$, Filename.Search$)
         END IF
      END IF

      ' check search filename
      IF Command.Line = NUL THEN
	 EXIT DO
      END IF
   LOOP

   ' check search filename
   IF Standard.Input$ = NUL THEN
      EXIT DO
   END IF
LOOP

End.Whereis:

' restore basic dta
InregsX.AX = &H1A00
InregsX.DS = BASIC.DTA.SEG
InregsX.DX = BASIC.DTA.OFF
CALL InterruptX(&H21, InregsX, OutregsX)

' restore current drive
InregsX.AX = &HE00
InregsX.DX = ASC(Current.Drive$) - 65
CALL InterruptX(&H21, InregsX, OutregsX)

' check windows dos
IF Windows.Detected THEN
   ' restore current directory
   InregsX.AX = &H713B
   InregsX.DS = VARSEG(Directory.ASCIZ)
   InregsX.DX = VARPTR(Directory.ASCIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
ELSE
   ' restore current directory
   InregsX.AX = &H3B00
   InregsX.DS = VARSEG(Directory.ASCIZ)
   InregsX.DX = VARPTR(Directory.ASCIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
END IF

' display end program
IF Continuous.Display = False THEN
   IF Double.Line = False THEN
      IF Wide.List THEN
         PRINT
      END IF
   END IF
   COLOR Yellow, Black
   PRINT "Files counted"; Files.Counted;
   IF Double.Line THEN
      PRINT "Total bytes ";
      Total$ = FormatD$(Total.Bytes, "#,##0;;0")
      PRINT Total$;
   END IF
   PRINT
   Prompt$ = "Press <enter> to exit to DOS:"
   CALL MorePrompt(Prompt$, CHR$(13), Outpt$)
END IF
COLOR Plain, Black
END

' display program usage
Boot.Usage:
 ' make header
 COLOR White, Black
 PRINT "Whereis v1.6a: File search utility; "
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Whereis [d:\path\filename.ext][+/ahiosx][/bcdeflnrtvyz][command]"
 PRINT "Where:"
 PRINT "   /b  suppress drive letter   /c  continuous display
 PRINT "   /e  short filename display  /l  double line display"
 PRINT "   /r  recurse directories     /v  display volume label"
 PRINT "   /y  display lowercase       /z  supress error messages"
 PRINT "   /k  use 8.3 short filenames /nxxx  nested directories"
 PRINT "   /w  wide file list"
 PRINT "   display ranges: (/1  creation, /2  last access  /3  modify time)"
 PRINT "     /d  is range of file dates in form mm/dd/yyyy-mm/dd/yyyy"
 PRINT "     /t  is range of file times in form hh:mm:ss-hh:mm:ss"
 PRINT "     /f  is range of file sizes in form xxx-xxx in kilobytes"
 PRINT "   display file attributes:"
 PRINT "     + prefix for files with, / prefix for files without,"
 PRINT "       a  archive, h  hidden, i  directory, o  read-only, s  system, x  none"
 PRINT "   DOS command enclosed in '', or []s; command replacement parameters:"
 PRINT "     %1 = d:, %2 = d:\, %3 = d:\pathname, %4 = d:\pathname\"
 PRINT "     %5 = d:\pathname\filename.ext, %6 = \pathname, %7 = \pathname\"
 PRINT "     %8 = \pathname\filename.ext, %9 = filename.ext, %a = filename"
 PRINT "     %b = .ext, %c = ext, //1 = >, //2 = <, //3 = |, //4 = %"
 COLOR Plain, Black
 END

Get.Numeric:
 Var# = False
 DO
    Temp$ = MID$(Command.Line, Imbedded, 1)
    IF Temp$ >= "0" AND Temp$ <= "9" THEN
       Var# = Var# * 10 + VAL(Temp$)
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 1)
    ELSE
       EXIT DO
    END IF
 LOOP
 RETURN

Boot.Error:
 COLOR White, Black
 PRINT "Command line error. Type Whereis /? for help."
 COLOR Plain, Black
 END

' subroutine to access directories
SUB Directories (Directory.Search$, Filename.Search$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 IF Display.Volume THEN
    ASCIZ.Sub = "\*.*" + CHR$(0)
 ELSE
    ASCIZ.Sub = Directory.Search$ + "*.*" + CHR$(0)
 END IF
 GOSUB Restore.DTA

 ' find first directory
 IF Display.Volume THEN
    InregsX.AX = &H4E00
    InregsX.CX = &H08
    InregsX.DS = VARSEG(ASCIZ.Sub)
    InregsX.DX = VARPTR(ASCIZ.Sub)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    IF Windows.Detected THEN
       InregsX.AX = &H714E
       InregsX.CX = &H37
       InregsX.SI = &H1
       InregsX.Ds = VARSEG(ASCIZ.Sub)
       InregsX.DX = VARPTR(ASCIZ.Sub)
       InregsX.ES = VARSEG(TreeWDTA)
       InregsX.DI = VARPTR(TreeWDTA)
       CALL InterruptX(&H21, InregsX, OutregsX)
       Wfile.Handle = OutregsX.AX
    ELSE
       InregsX.AX = &H4E00
       InregsX.CX = &H37
       InregsX.DS = VARSEG(ASCIZ.Sub)
       InregsX.DX = VARPTR(ASCIZ.Sub)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF

 ' check find first error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    EXIT SUB
 END IF

 IF Display.Volume THEN
    File.Work.Time = ASC(MID$(DTAfile.FileTime, 2, 1))
    File.Work.Time = File.Work.Time * &H100 + ASC(MID$(DTAfile.FileTime, 1, 1))
    File.Work.Date = ASC(MID$(DTAfile.FileDate, 2, 1))
    File.Work.Date = File.Work.Date * &H100 + ASC(MID$(DTAfile.FileDate, 1, 1))
 END IF

 ' search directory names
 CALL Filenames(Directory.Search$, Filename.Search$)
 GOSUB Restore.DTA

 ' check to recurse directories
 IF Recurse.Directories THEN

    ' recurse directories
    DO
       ' check to quit
       IF Quit.Searching THEN
          EXIT DO
       END IF

       ' check directory attribute
       IF Windows.Detected THEN
          Attribute = ASC(TreeWDTA.FileAttr)
       ELSE
          Attribute = ASC(DTAfile.FileAttr)
       END IF

       ' check for directory
       IF (Attribute AND &H10) = &H10 THEN

          ' store directory name
          IF Windows.Detected THEN
             Directory$ = TreeWDTA.ASCIZfull
          ELSE
             Directory$ = DTAfile.ASCIZfilename
          END IF
          Directory$ = LEFT$(Directory$, INSTR(Directory$, CHR$(0)) - 1)

          ' check directory name
          IF Directory$ <> "." AND Directory$ <> ".." THEN

             ' make next search directory
             Next.Directory$ = Directory.Search$ + Directory$ + "\"

             ' check recursion levels
             Recursion% = True
             IF Nested.Recurse > False THEN
                Nested.Levels = Nested.Levels + 1
                IF Nested.Levels >= Nested.Recurse THEN
                   Recursion% = False
                END IF
             END IF

             ' recursively search subdirectories
             IF Recursion% THEN
                CALL Directories(Next.Directory$, Filename.Search$)
             END IF
             IF Nested.Recurse > False THEN
                Nested.Levels = Nested.Levels - 1
             END IF

             ' restore dta
             GOSUB Restore.DTA
          END IF
       END IF

       ' check windows dos
       IF Windows.Detected THEN
          ' find next long filename
          InregsX.AX = &H714F
          InregsX.BX = Wfile.Handle
          InregsX.SI = &H1
          InregsX.ES = VARSEG(TreeWDTA)
          InregsX.DI = VARPTR(TreeWDTA)
          CALL InterruptX(&H21, InregsX, OutregsX)
       ELSE
          ' find next directory
          InregsX.AX = &H4F00
          CALL InterruptX(&H21, InregsX, OutregsX)
       END IF

       ' check findnext error
       IF (OutregsX.Flags AND &H1) = &H1 THEN
          EXIT DO
       END IF
    LOOP
 END IF
 ' check windows dos
 IF Windows.Detected THEN
    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
 EXIT SUB

Restore.DTA:
 ' restore directory search dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN
END SUB

' subroutine to display a filename
SUB DisplayFiles (Search.Directory$, Search.Filename$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM BPBfile AS BPBtype

 ' make filename
 ASCIZ.Display = Search.Directory$ + Search.Filename$ + CHR$(0)

 ' check file display type
 IF Display.Volume = False THEN
    ' check windows dos
    IF Windows.Detected THEN
       ' get file attributes
       InregsX.AX = &H7143
       InregsX.BX = &H0
       InregsX.DS = VARSEG(ASCIZ.Display)
       InregsX.DX = VARPTR(ASCIZ.Display)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' get file attributes
       InregsX.AX = &H4300
       InregsX.DS = VARSEG(ASCIZ.Display)
       InregsX.DX = VARPTR(ASCIZ.Display)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' display any errors
    CALL DisplayError ("Error reading filename attributes.")

    ' store file attribute
    Attribute = OutregsX.CX

    ' check file attribute
    IF Display.Attribute THEN

       ' check for readonly file
       IF Display.Readonly THEN
	  IF (Attribute AND 1) <> 1 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.Readonly THEN
	  IF (Attribute AND 1) = 1 THEN
	     EXIT SUB
	  END IF
       END IF

       ' check for hidden file
       IF Display.Hidden THEN
	  IF (Attribute AND 2) <> 2 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.Hidden THEN
	  IF (Attribute AND 2) = 2 THEN
	     EXIT SUB
	  END IF
       END IF

       ' check for system file
       IF Display.System THEN
	  IF (Attribute AND 4) <> 4 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.System THEN
	  IF (Attribute AND 4) = 4 THEN
	     EXIT SUB
	  END IF
       END IF

       ' check for directory file
       IF Display.Directory THEN
          IF (Attribute AND &H10) <> &H10 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.Directory THEN
          IF (Attribute AND &H10) = &H10 THEN
	     EXIT SUB
	  END IF
       END IF

       ' check for archive file
       IF Display.Archive THEN
	  IF (Attribute AND &H20) <> &H20 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.Archive THEN
	  IF (Attribute AND &H20) = &H20 THEN
	     EXIT SUB
	  END IF
       END IF

       ' check for no attributes
       IF Display.Any THEN
	  IF (Attribute AND 1) = 1 THEN
	     EXIT SUB
	  END IF
	  IF (Attribute AND 2) = 2 THEN
	     EXIT SUB
	  END IF
	  IF (Attribute AND 4) = 4 THEN
	     EXIT SUB
	  END IF
          IF (Attribute AND &H10) = &H10 THEN
	     EXIT SUB
	  END IF
	  IF (Attribute AND &H20) = &H20 THEN
	     EXIT SUB
	  END IF
       END IF
       IF No.Display.Any THEN
          IF (Attribute AND 1) = False THEN
             IF (Attribute AND 2) = False THEN
                IF (Attribute AND 4) = False THEN
                   IF (Attribute AND &H10) = False THEN
                      IF (Attribute AND &H20) = False THEN
                         EXIT SUB
                      END IF
		   END IF
		END IF
	     END IF
	  END IF
       END IF
    END IF

    ' check file size
    IF Search.File.Size THEN
       IF Search.Size.From = FalseD AND Search.Size.To = FalseD THEN
          IF File.Size <> FalseD THEN
	     EXIT SUB
	  END IF
       ELSE
          IF Search.Size.From > FalseD OR Search.Size.To > FalseD THEN
             IF Search.Size.From > FalseD THEN
                IF Search.Size.To = FalseD THEN
		   IF File.Size < Search.Size.From THEN
		      EXIT SUB
		   END IF
		END IF
	     END IF
             IF Search.Size.From = FalseD THEN
                IF Search.Size.To > FalseD THEN
		   IF File.Size > Search.Size.To THEN
		      EXIT SUB
		   END IF
		END IF
	     END IF
	     IF Search.Size.From <= Search.Size.To THEN
		IF File.Size < Search.Size.From OR File.Size > Search.Size.To THEN
		   EXIT SUB
		END IF
	     END IF
	     IF Search.Size.From > Search.Size.To THEN
		IF File.Size < Search.Size.From AND File.Size > Search.Size.To THEN
		   EXIT SUB
		END IF
	     END IF
	  END IF
       END IF
    END IF

    ' store file date and time
    IF Windows.Detected THEN
       IF Creation.Time THEN
          File.Work.Time = ASC(MID$(TempWDTA.CreateTime, 2, 1))
          File.Work.Time = File.Work.Time * &H100 + ASC(MID$(TempWDTA.CreateTime, 1, 1))
          File.Work.Date = ASC(MID$(TempWDTA.CreateTime, 4, 1))
          File.Work.Date = File.Work.Date * &H100 + ASC(MID$(TempWDTA.CreateTime, 3, 1))
       ELSE
          IF Access.Time THEN
             File.Work.Time = ASC(MID$(TempWDTA.AccessTime, 2, 1))
             File.Work.Time = File.Work.Time * &H100 + ASC(MID$(TempWDTA.AccessTime, 1, 1))
             File.Work.Date = ASC(MID$(TempWDTA.AccessTime, 4, 1))
             File.Work.Date = File.Work.Date * &H100 + ASC(MID$(TempWDTA.AccessTime, 3, 1))
          ELSE
             IF Modified.Time THEN
                File.Work.Time = ASC(MID$(TempWDTA.ModTime, 2, 1))
                File.Work.Time = File.Work.Time * &H100 + ASC(MID$(TempWDTA.ModTime, 1, 1))
                File.Work.Date = ASC(MID$(TempWDTA.ModTime, 4, 1))
                File.Work.Date = File.Work.Date * &H100 + ASC(MID$(TempWDTA.ModTime, 3, 1))
             END IF
          END IF
       END IF
    ELSE
       File.Work.Time = ASC(MID$(TempDTA.FileTime, 2, 1))
       File.Work.Time = File.Work.Time * &H100 + ASC(MID$(TempDTA.FileTime, 1, 1))
       File.Work.Date = ASC(MID$(TempDTA.FileDate, 2, 1))
       File.Work.Date = File.Work.Date * &H100 + ASC(MID$(TempDTA.FileDate, 1, 1))
    END IF
 END IF

 ' check date/time range
 IF Search.From.Date OR Search.To.Date THEN
    IF File.Work.Date < Search.From.Date THEN
       EXIT SUB
    END IF
    IF File.Work.Date > Search.To.Date THEN
       EXIT SUB
    END IF
 END IF
 IF Search.From.Time OR Search.To.Time THEN
    IF File.Work.Time < Search.From.Time THEN
       EXIT SUB
    END IF
    IF File.Work.Time > Search.To.Time THEN
       EXIT SUB
    END IF
 END IF

 ' construct file date and time for display
 Hour! = INT(File.Work.Time / 2048)
 Minute! = INT((File.Work.Time AND &H7E0) / 32)
 Seconds! = INT((File.Work.Time AND &H1F) * 2)

 Year! = INT(File.Work.Date / 512)
 Month! = INT((File.Work.Date AND &H1E0) / 32)
 Day! = INT(File.Work.Date AND &H1F)
 Year! = Year! + 1980

 File.Date$ = RIGHT$(STR$(Month! + 100), 2) + "-"
 File.Date$ = File.Date$ + RIGHT$(STR$(Day! + 100), 2) + "-"
 File.Date$ = File.Date$ + MID$(STR$(Year!), 2)

 File.Time$ = RIGHT$(STR$(Hour! + 100), 2) + ":"
 File.Time$ = File.Time$ + RIGHT$(STR$(Minute! + 100), 2) + ":"
 File.Time$ = File.Time$ + RIGHT$(STR$(Seconds! + 100), 2)

 ' page to list files
 IF Files.Counter >= 22 THEN
    Files.Counter = False
    IF Continuous.Display = False THEN
       IF More.Display = False THEN
          IF Double.Line = False THEN
             IF Wide.List THEN
                PRINT
             END IF
          END IF
          Prompt$ = "More(y/n/c)?"
          CALL MorePrompt(Prompt$, "ync", Outpt$)
          SELECT CASE Outpt$
          CASE "c"
             More.Display = True
          CASE "n"
             Quit.Searching = True
             EXIT SUB
          END SELECT
       END IF
    END IF
 END IF

 ' increment files counted
 Files.Counted = Files.Counted + 1

 ' make filename
 File.List$ = Search.Filename$
 IF Display.Volume = False THEN
    Display.Filename$ = Search.Filename$
    IF Windows.Detected AND Short.Filenames THEN
       Display.Filename$ = TempWDTA.ASCIZshort
       Imbedded = INSTR(Display.Filename$, CHR$(0))
       Display.Filename$ = LEFT$(Display.Filename$, Imbedded - 1)
       Display.Filename$ = RTRIM$(Display.Filename$)
       IF Display.Filename$ = "" THEN
          ' get windows short filename
          InregsX.AX = &H7160
          InregsX.CX = &H8001
          InregsX.DS = VARSEG(ASCIZ.Display)
          InregsX.SI = VARPTR(ASCIZ.Display)
          InregsX.ES = VARSEG(ASCIZ.Short)
          InregsX.DI = VARPTR(ASCIZ.Short)
          CALL InterruptX(&H21, InregsX, OutregsX)
          Display.Filename$ = ASCIZ.Short
          Imbedded = INSTR(Display.Filename$, CHR$(0))
          Display.Filename$ = LEFT$(Display.Filename$, Imbedded - 1)
          IF MID$(Display.Filename$, 2, 1) = ":" THEN
             Display.Filename$ = MID$(Display.Filename$, 3)
          END IF
          Imbedded1 = INSTR(Display.Filename$, "\")
          Imbedded2 = Imbedded1
          WHILE Imbedded1
             Imbedded2 = Imbedded1
             Imbedded1 = INSTR(Imbedded1 + 1, Display.Filename$, "\")
          WEND
          IF Imbedded2 THEN
             Display.Filename$ = MID$(Display.Filename$, Imbedded2 + 1)
          END IF
          Display.Filename$ = RTRIM$(Display.Filename$)
       END IF
    END IF
    IF Short.Display THEN
       File.List$ = Display.Filename$
    ELSE
       File.List$ = Search.Directory$ + Display.Filename$
    END IF
    IF No.Display.Drive = False THEN
       File.List$ = Drive.Search + ":" + File.List$
    END IF
 END IF

 ' display filename
 COLOR Yellow, Black
 IF Display.Lowercase THEN
    PRINT LCASE$(File.List$);
 ELSE
    IF Short.Filenames THEN
       PRINT UCASE$(File.List$);
    ELSE
       PRINT RTRIM$(File.List$);
    END IF
 END IF

 ' check display type
 Wide.Display = True
 IF Wide.Filename THEN
    IF Short.Display THEN
       Wide.Display = False
       PRINT SPACE$(15 - LEN(File.List$));
       Wide.List = Wide.List + 1
       IF Wide.List = 5 THEN
          Wide.List = False
          IF Double.Line = False THEN
             Wide.Display = True
          END IF
       END IF
    END IF
 END IF
 IF Wide.Display THEN
    PRINT
    Files.Counter = Files.Counter + 1
 END IF

 ' check display type
 IF Double.Line THEN
    ' display file date/time
    COLOR Green, Black
    PRINT File.Date$ + " " + File.Time$; " ";

    ' check dos type
    IF Windows.DOS THEN
       ' check for directory bit
       IF (Attribute AND &H10) = &H10 THEN
          CALL ReadAccessDate(Search.Directory$, Search.Filename$, False)
       ELSE
          CALL ReadAccessDate(Search.Directory$, Search.Filename$, True)
       END IF
    END IF

    ' check display type
    IF Display.Volume THEN

       ' get volume info
       InregsX.AX = &H6900
       InregsX.BX = ASC(Drive.Search) - 64
       InregsX.DS = VARSEG(BPBfile)
       InregsX.DX = VARPTR(BPBfile)
       CALL InterruptX(&H21, InregsX, OutregsX)

       ' display any errors
       CALL DisplayError ("Error reading volume attribute.")

       ' check flag error
       IF (OutregsX.Flags AND &H1) = &H0 THEN

	  ' display volume serial number
          COLOR Red, Black
	  FOR Serial.Digit = 4 TO 1 STEP -1
	     IF Serial.Digit = 2 THEN
		PRINT "-";
	     END IF
             Digit = ASC(MID$(BPBfile.Serial, Serial.Digit, 1))
             Digit$ = RIGHT$(HEX$(Digit + &H100), 2)
             PRINT Digit$;
	  NEXT
	  PRINT " ";

	  ' display volume fat type
          COLOR White, Black
	  PRINT BPBfile.System;
       END IF
    END IF

    ' check display type
    IF Display.Volume = False THEN

       ' check for directory bit
       IF (Attribute AND &H10) = &H10 THEN
          Size$ = "<dir>"
       ELSE
          Total.Bytes = Total.Bytes + File.Size
          Size$ = FormatD$(File.Size, "#,##0;;0")
       END IF
       ' display file size
       COLOR Red, Black
       PRINT Size$;
       COLOR White, Black

       ' check for directory file
       IF (Attribute AND &H10) = &H10 THEN
          PRINT " Directory";
       END IF

       ' check for archive file
       IF (Attribute AND &H20) = &H20 THEN
	  PRINT " Archive";
       END IF

       ' check for read-only file
       IF (Attribute AND 1) = 1 THEN
	  PRINT " Read-only";
       END IF

       ' check for hidden file
       IF (Attribute AND 2) = 2 THEN
	  PRINT " Hidden";
       END IF

       ' check for system file
       IF (Attribute AND 4) = 4 THEN
	  PRINT " System";
       END IF
    END IF
    PRINT
    Files.Counter = Files.Counter + 1
 END IF

 ' check to run DOS command
 IF LEN(DOS.Command) THEN
    CALL ExecuteCommand(Search.Directory$ + Display.Filename$)
 END IF
END SUB

' subroutine to execute DOS command
SUB ExecuteCommand (DOS.Filename$)
 DIM ASCIZ AS STRING * 260
 DIM DTAfile AS DTAtype
 DIM Current.DTA.SEG AS INTEGER, Current.DTA.OFF AS INTEGER

 ' make replacement parameters
 DOS.Pathname$ = DOS.Filename$
 Imbedded1 = INSTR(DOS.Pathname$, "\")
 Imbedded2 = False
 WHILE Imbedded1
    Imbedded2 = Imbedded1
    Imbedded1 = INSTR(Imbedded1 + 1, DOS.Pathname$, "\")
 WEND
 Filename$ = MID$(DOS.Pathname$, Imbedded2 + 1)
 IF Imbedded2 THEN
    DOS.Pathname$ = LEFT$(DOS.Pathname$, Imbedded2 - 1)
 ELSE
    DOS.Pathname$ = NUL
 END IF
 Extension$ = NUL
 Imbedded = INSTR(Filename$, ".")
 IF Imbedded THEN
    Extension$ = MID$(Filename$, Imbedded + 1)
    Filename$ = LEFT$(Filename$, Imbedded - 1)
 END IF

 ' store replacement parameters
 Parameters(1) = Drive.Search + ":"
 Parameters(2) = Drive.Search + ":\"
 Parameters(3) = Drive.Search + ":" + DOS.Pathname$
 Parameters(4) = Drive.Search + ":" + DOS.Pathname$ + "\"
 Parameters(5) = Drive.Search + ":" + DOS.Filename$
 Parameters(6) = DOS.Pathname$
 Parameters(7) = DOS.Pathname$ + "\"
 Parameters(8) = DOS.Filename$
 Parameters(9) = Filename$ + "." + Extension$
 Parameters(10) = Filename$
 Parameters(11) = "." + Extension$
 Parameters(12) = Extension$

 ' check display type
 IF Display.Volume = False THEN

    ' make directory filename
    ASCIZ = DOS.Pathname$ + CHR$(0)

    ' check windows dos
    IF Windows.Detected THEN
       ' change to directory
       InregsX.AX = &H713B
       InregsX.DS = VARSEG(ASCIZ)
       InregsX.DX = VARPTR(ASCIZ)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' change to directory
       InregsX.AX = &H3B00
       InregsX.DS = VARSEG(ASCIZ)
       InregsX.DX = VARPTR(ASCIZ)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' display any errors
    CALL DisplayError ("Error accessing drive.")
 END IF

 ' create DOS command
 Execute.Command$ = DOS.Command

 ' replace parameters
 FOR Count = 1 TO 12
    Imbed$ = "%" + HEX$(Count)
    Imbedded = INSTR(UCASE$(Execute.Command$), Imbed$)
    WHILE Imbedded
       Execute.Command$ = LEFT$(Execute.Command$, Imbedded - 1) + _
       RTRIM$(Parameters(Count)) + MID$(Execute.Command$, Imbedded + 2)
       Imbedded = INSTR(UCASE$(Execute.Command$), Imbed$)
    WEND
 NEXT

 ' store current dta
 InregsX.AX = &H2F00
 CALL InterruptX(&H21, InregsX, OutregsX)
 Current.DTA.SEG = OutregsX.ES
 Current.DTA.OFF = OutregsX.BX

 ' store shell dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' start DOS command shell
 IF LEN(Execute.Command$) THEN
    ' check display counter
    IF Double.Line = False THEN
       IF Wide.List THEN
          PRINT
       END IF
    END IF

    ' start DOS command shell
    IF LEN(Execute.Command$) THEN
       ' call shell routine
       Shell.Command$ = "COMMAND.COM /E:4096 /C " + Execute.Command$
       SHELL Shell.Command$
    END IF
 END IF

 ' restore current dta
 InregsX.AX = &H1A00
 InregsX.DS = Current.DTA.SEG
 InregsX.DX = Current.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check display type
 IF Display.Volume = False THEN

    ' check windows dos
    IF Windows.Detected THEN
       ' restore current directory
       InregsX.AX = &H713B
       InregsX.DS = VARSEG(Directory.ASCIZ)
       InregsX.DX = VARPTR(Directory.ASCIZ)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' restore current directory
       InregsX.AX = &H3B00
       InregsX.DS = VARSEG(Directory.ASCIZ)
       InregsX.DX = VARPTR(Directory.ASCIZ)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' display any errors
    CALL DisplayError ("Error accessing drive.")
 END IF
END SUB

' subroutine to access filenames in directory
SUB Filenames (Directory.Search$, Filename.Search$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make filename
 IF Display.Volume THEN
    ASCIZ.File = "\*.*" + CHR$(0)
 ELSE
    ASCIZ.File = Directory.Search$ + Filename.Search$ + CHR$(0)
 END IF

 ' store dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile)
 InregsX.DX = VARPTR(DTAfile)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' find first filename
 IF Display.Volume THEN
    InregsX.AX = &H4E00
    InregsX.CX = &H08
    InregsX.DS = VARSEG(ASCIZ.File)
    InregsX.DX = VARPTR(ASCIZ.File)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    IF Windows.Detected THEN
       InregsX.AX = &H714E
       InregsX.CX = &H37
       InregsX.SI = &H1
       InregsX.Ds = VARSEG(ASCIZ.File)
       InregsX.DX = VARPTR(ASCIZ.File)
       InregsX.ES = VARSEG(FileWDTA)
       InregsX.DI = VARPTR(FileWDTA)
       CALL InterruptX(&H21, InregsX, OutregsX)
       Wfile.Handle = OutregsX.AX
    ELSE
       InregsX.AX = &H4E00
       InregsX.CX = &H37
       InregsX.DS = VARSEG(ASCIZ.File)
       InregsX.DX = VARPTR(ASCIZ.File)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF

 ' check find first error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    EXIT SUB
 END IF

 ' search filenames
 DO
    ' store filename
    IF Display.Volume THEN
       Filename$ = DTAfile.ASCIZfilename
    ELSE
       IF Windows.Detected THEN
          Filename$ = FileWDTA.ASCIZfull
       ELSE
          Filename$ = DTAfile.ASCIZfilename
       END IF
    END IF
    Filename$ = LEFT$(Filename$, INSTR(Filename$, CHR$(0)) - 1)

    ' check filename
    IF Filename$ <> "." AND Filename$ <> ".." THEN
       ' store file size
       IF Windows.Detected THEN
          File.Size = False
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeHigh, 4, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeHigh, 3, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeHigh, 2, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeHigh, 1, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeLow, 4, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeLow, 3, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeLow, 2, 1))
          File.Size = File.Size * &H100 + ASC(MID$(FileWDTA.FileSizeLow, 1, 1))
       ELSE
          File.Size = False
          File.Size = File.Size + ASC(MID$(DTAfile.FileSize, 4, 1))
          File.Size = File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
          File.Size = File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
          File.Size = File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 1, 1))
       END IF

       ' display filename
       IF Windows.Detected THEN
          TempWDTA = FileWDTA
       ELSE
          TempDTA = DTAfile
       END IF
       CALL DisplayFiles(Directory.Search$, Filename$)
    END IF

    ' check to quit
    IF Quit.Searching THEN
       EXIT DO
    END IF

    IF Windows.Detected THEN
       ' find next long filename
       InregsX.AX = &H714F
       InregsX.BX = Wfile.Handle
       InregsX.SI = &H1
       InregsX.ES = VARSEG(FileWDTA)
       InregsX.DI = VARPTR(FileWDTA)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' find next filename
       InregsX.AX = &H4F00
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' check find first error
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       EXIT DO
    END IF
 LOOP
 ' check windows dos
 IF Windows.Detected THEN
    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

' critical error trap
Error.Routine:
 Data.Error = ERR
 IF Display.Errors THEN
    Error.Level = True
    OutregsX.Flags = &H1
    RESUME NEXT
 END IF
 SELECT CASE Data.Error
 CASE 53
    Temp.Outpt$ = "File not found."
 CASE 61
    Temp.Outpt$ = "Disk full."
 CASE 70
    Temp.Outpt$ = "Permission denied."
 CASE 71
    Temp.Outpt$ = "Disk not ready."
 CASE ELSE
    Temp.Outpt$ = "Untrapped error" + STR$(Data.Error) + "."
 END SELECT
 COLOR Green, Black
 IF Double.Line = False THEN
    IF Wide.List THEN
       Wide.List = False
       PRINT
    END IF
 END IF
 PRINT Temp.Outpt$
 Prompt$ = "Press R to retry, Q to quit, C to continue:"
 CALL MorePrompt(Prompt$, "rqc", Outpt$)
 SELECT CASE Outpt$
 CASE "r"
    RESUME
 CASE "q"
    Error.Level = True
    RESUME End.Whereis
 CASE "c"
    OutregsX.Flags = &H1
    RESUME NEXT
 END SELECT
 END 0

SUB MorePrompt (Input.String$, Input.Mask$, Output.String$)
 COLOR White, Black
 PRINT Input.String$ + " ";
 Input.Char$ = NUL
 DO
    LOCATE , , 1
    Input.Char$ = INKEY$
    IF LEN(Input.Char$) THEN
       Input.Char$ = LCASE$(Input.Char$)
       IF INSTR(Input.Mask$, Input.Char$) THEN
	  PRINT Input.Char$
	  Output.String$ = Input.Char$
	  EXIT DO
       END IF
    END IF
 LOOP
END SUB

' displays carry flag error
SUB DisplayError (Temp$)
 ' check carry flag error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    ' check display errors flag
    IF Display.Errors = False THEN
       ' display error
       COLOR Red, Black
       PRINT Temp$
    END IF
 END IF
END SUB

' routine displays directory or filename last access date in dos
SUB ReadAccessDate(Search.Directory$, Search.Filename$, SearchType%)
 ' declare some variables
 DIM DTAfile2 AS DTAtype2
 DIM FCBfile AS FCBtype
 DIM Current.DTA.Seg AS INTEGER
 DIM Current.DTA.Off AS INTEGER
 DIM ASCIZ2 AS STRING * 260

 ' reset file last access date
 File.Access.Date = 0

 ' check for dos 7.00
 IF Windows.DOS = False THEN
    EXIT SUB
 END IF

 ' store current dta
 InregsX.AX = &H2F00
 CALL InterruptX(&H21, InregsX, OutregsX)
 Current.DTA.Seg = OutregsX.ES
 Current.DTA.Off = OutregsX.BX

 ' store directory search dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(DTAfile2)
 InregsX.DX = VARPTR(DTAfile2)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' make directory
 Directory$ = Search.Directory$
 IF RIGHT$(Directory$, 1) = "\" THEN
    Directory$ = LEFT$(Directory$, LEN(Directory$) - 1)
 END IF
 IF Directory$ = "" THEN
    Directory$ = "\"
 END IF
 ASCIZ2 = Directory$ + CHR$(0)

 ' change to directory
 InregsX.AX = &H3B00
 InregsX.DS = VARSEG(ASCIZ2)
 InregsX.DX = VARPTR(ASCIZ2)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check carry flag error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' check display type
    IF SearchType% = False THEN
       ' store directory name
       FCBfile.ExtendedFCB = CHR$(255)
       FCBfile.FileAttribute = CHR$(&H37)
       FCBfile.Filename = "."
       FCBfile.Extension = ""
       FCBfile.DriveNumber = CHR$(ASC(Drive.Search) - 64)
    ELSE
       ' make filename
       Imbedded = INSTR(Search.Filename$, ".")
       IF Imbedded THEN
          Filename$ = LEFT$(Search.Filename$, Imbedded - 1)
          Extension$ = MID$(Search.Filename$, Imbedded + 1)
       ELSE
          Filename$ = Search.Filename$
          Extension$ = ""
       END IF

       ' store filename
       FCBfile.ExtendedFCB = CHR$(255)
       FCBfile.FileAttribute = CHR$(&H27)
       FCBfile.Filename = Filename$
       FCBfile.Extension = Extension$
       FCBfile.DriveNumber = CHR$(ASC(Drive.Search) - 64)
    END IF

    ' find first fcb
    InregsX.AX=&H1100
    InregsX.DS=VARSEG(FCBfile)
    InregsX.DX=VARPTR(FCBfile)
    CALL InterruptX(&H21,InregsX,OutregsX)

    ' check fcb error
    IF (OutregsX.AX AND &HFF) = &H0 THEN
       ' read extended date\time
       File.Access.Date = ASC(MID$(DTAfile2.LastAccessDate, 2, 1))
       File.Access.Date = File.Access.Date * &H100 + ASC(MID$(DTAfile2.LastAccessDate, 1, 1))
    END IF
 END IF

 ' restore current dta
 InregsX.AX = &H1A00
 InregsX.DS = Current.DTA.Seg
 InregsX.DX = Current.DTA.Off
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check access date
 IF File.Access.Date > 0 THEN
    Year! = INT(File.Access.Date / 512)
    Month! = INT((File.Access.Date AND &H1E0) / 32)
    Day! = INT(File.Access.Date AND &H1F)
    Year! = Year! + 1980

    File.Date$ = RIGHT$(STR$(Month! + 100), 2) + "-"
    File.Date$ = File.Date$ + RIGHT$(STR$(Day! + 100), 2) + "-"
    File.Date$ = File.Date$ + MID$(STR$(Year!), 2)
    COLOR Cyan, Black
    PRINT "("+ File.Date$ + ") ";
 END IF
END SUB
