REM file: Typea.bas - Public Domain DOS Utility for ANSI files
REM Version 1.0a created 01/15/1996
REM Version 1.1a created 03/06/2001

' 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: 'wdta.bi'

' declare subroutines
DECLARE SUB Directories(D$,F$)
DECLARE SUB Filenames(D$,F$)
DECLARE SUB DisplayFiles(D$,F$)
DECLARE SUB Reset.Cursor()

' declare functions
DECLARE FUNCTION ParseLine (S$)
DECLARE FUNCTION BreakIS()
DECLARE FUNCTION ClearBreak()
DECLARE FUNCTION KeyIS()

' initialize filename buffer
DIM ASCIIZ AS STRING * 260

' initialize drive variables
COMMON SHARED Drive.Number AS INTEGER, Current.Drive AS INTEGER

' initialize directory variables
COMMON SHARED Current.Directory AS STRING * 260, Denied AS INTEGER

' declare program dta
DIM BASIC.DTA.SEG AS INTEGER, BASIC.DTA.OFF AS INTEGER

' declare registers
COMMON SHARED InregsX AS RegTypeX, OutregsX AS RegTypeX
COMMON SHARED InregsX2 AS RegtypeX, InregsX3 AS RegtypeX

' declare work variables
COMMON SHARED Lines.Counted AS INTEGER, More.Display AS INTEGER
COMMON SHARED Quit.Searching AS INTEGER, Windows.Detected AS INTEGER
COMMON SHARED Video.Page 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

' declare recurse work variables
COMMON SHARED Recurse.Directories AS INTEGER

' declare windows wdta type
COMMON SHARED FDTAfile AS WDTAtype, WDTAfile AS WDTAtype

' declare display work variables
COMMON SHARED Display.Hidden AS INTEGER, Display.System AS INTEGER
COMMON SHARED Display.Readonly AS INTEGER, Display.Archive AS INTEGER
COMMON SHARED No.Display.Hidden AS INTEGER, No.Display.System AS INTEGER
COMMON SHARED No.Display.Readonly AS INTEGER, No.Display.Archive AS INTEGER
COMMON SHARED No.Display.Any AS INTEGER, Display.Any AS INTEGER
COMMON SHARED Display.File AS INTEGER

' declare file date and time work variables
COMMON SHARED File.Work.Date AS SINGLE, File.Work.Time AS SINGLE

' declare command line work variables
COMMON SHARED Command.Line AS STRING, Command.Line.Redirect AS STRING
COMMON SHARED Command.Work AS STRING, Control.Break AS INTEGER
COMMON SHARED Redirected.Input AS INTEGER, Pipe.Buffer AS STRING * 1
COMMON SHARED Last.Switch AS INTEGER

' declare external procedures
DECLARE SUB SetInt
DECLARE SUB RestInt

' backwards compatible for bc 7.1
REM $INCLUDE: 'bc7.inc'

' increase stack size
STACK STACK

' install new interrupt service routine
CALL SetInt

' 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$))
    Last.Switch = Imbedded - 1
    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$))
       Last.Switch = Imbedded - 1
       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 = (OutregsX.AX AND &HFF) + 65

' check windows
Windows.Detected = True
If Load.Windows = False Then
   InregsX.AX = &H160A
   CALL InterruptX(&H2F, InregsX, OutregsX)
   IF OutregsX.AX > 0 THEN
      InregsX.AX = &H4A33
      CALL InterruptX(&H2F, InregsX, OutregsX)
      IF OutregsX.AX = 0 THEN
         Windows.Detected = 0 ' DOS 7.00
      END IF
   END IF
END IF
IF INSTR(COMMAND$, "/_") THEN
   Windows.Detected = True
END IF

' 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
IF Command.Line = NUL THEN
   Command.Line = ENVIRON$("TYPEA")
END IF
Command.Line = RTRIM$(Command.Line)

' get switches from command line
No.Display.Archive = ParseLine ("//A")
No.Display.Hidden = ParseLine ("//H")
No.Display.Readonly = ParseLine ("//O")
No.Display.System = ParseLine ("//S")
No.Display.Any = ParseLine ("//X")
Display.Archive = ParseLine ("/A")
Display.Hidden = ParseLine ("/H")
Display.Readonly = ParseLine ("/O")
Display.System = ParseLine ("/S")
Display.Any = ParseLine ("/X")
Recurse.Directories = ParseLine ("/R")
Control.Break = ParseLine ("/~")
Var = ParseLine("/_")

' 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
   Last.Switch = Imbedded - 1
   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
   Last.Switch = Imbedded - 1
   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

' recheck command line
IF INSTR(Command.Line, "/") THEN
   GOTO Boot.Error
END IF
Command.Line = RTRIM$(Command.Line)
IF Last.Switch THEN
   IF LEN(Command.Line) > Last.Switch THEN
      GOTO Boot.Error
   END IF
END IF

' initialize work variables
Lines.Counted = False
More.Display = False
Quit.Searching = False

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

' check findfirst error
IF (OutregsX.Flags AND &H1) = &H1 THEN
   GOTO End.Typea
END IF

' store directory
IF LEN(Current.Directory) = False THEN
   Current.Directory = "\"
END IF
IF LEFT$(Current.Directory, 1) <> "\" THEN
   Current.Directory = "\" + Current.Directory
END IF
Current.Directory = Current.Directory + CHR$(0)

' store video page
InregsX.AX = &HF00
CALL InterruptX(&H10, InregsX, OutregsX)
Video.Page = OutregsX.BX

' reposition cursor
LOCATE ,,1
CALL Reset.Cursor

' remove blanks from command line
Command.Line = RTRIM$(Command.Line)
Command.Line = LTRIM$(Command.Line)
Command.Line.Redirect = Command.Line

' check break flag override
IF Control.Break THEN
   Var = ClearBreak
END IF

' search through all input filenames
Redirected.Input = False

' check pipe length
InregsX.AX = &H4202 ' eof
InregsX.BX = 0 ' stdin
InregsX.CX = 0
InregsX.DX = 0
Call InterruptX(&H21, InregsX, OutregsX)
If OutregsX.AX > 0 Then
   Pipe.Redirect = True
   InregsX.AX = &H4200
   InregsX.BX = 0 ' stdin
   InregsX.CX = 0
   InregsX.DX = 0
   Call InterruptX(&H21, InregsX, OutregsX)
Endif
If Pipe.Redirect = False Then
   DEF SEG = &H40
   X = PEEK(&H71)
   DEF SEG
   IF X = 64 THEN
      DEF SEG = &H40
      POKE &H71, 0
      DEF SEG
   END IF
Endif
DO
   ' check control break
   IF BreakIS THEN
      EXIT DO
   END IF

   ' get standard input
   Standard.Input$ = NUL

   If Pipe.Redirect Then
      DO
         ' read from device
         InregsX.AX = &H3F00
         InregsX.BX = 0 ' stdin
         InregsX.CX = 1 ' char
         InregsX.DS = VARSEG(Pipe.Buffer)
         InregsX.DX = VARPTR(Pipe.Buffer)
         Call InterruptX(&H21, InregsX, OutregsX)
         If (OutregsX.Flags AND &H1) = &H1 Then
            Exit Do
         Endif
         If (OutregsX.Flags AND &H1) = &H0 Then
            If OutregsX.AX = 0 Then
               Exit Do
            Endif

            ' store input
            Redirected.Input = True

            ' store character
            Char$ = Pipe.Buffer

            ' check input character
            SELECT CASE ASC(Char$)
            CASE 10, 26
            CASE 13
               EXIT DO
            CASE ELSE
               Standard.Input$ = Standard.Input$ + Char$
            END SELECT
         END IF
      LOOP
   END IF

   ' clear break flag
   IF Redirected.Input = False THEN
      IF Cleared = False THEN
         Cleared = True
         Var = ClearBreak
      END IF
   END IF

   ' check control break
   IF BreakIS THEN
      EXIT DO
   END IF

   ' check nul filename input
   IF Redirected.Input = False THEN
      IF Standard.Input$ = NUL THEN
         CALL RestInt ' restore Control-Break
         X$ = Inkey$ ' quits here
         CALL SetInt ' reset Control-Break
         IF X$ = CHR$(0) + CHR$(0) THEN
            EXIT DO
         END IF
      END IF
   END IF

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

   ' store entire command
   Command.Work = Command.Line.Redirect

   ' filename processing loop
   DO
      ' check control break
      IF BreakIS THEN
         EXIT DO
      END IF

      ' 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
         Search.Drive = ASC(UCASE$(LEFT$(Command.Work, 1)))
         Command.Work = MID$(Command.Work, 3)
      ELSE
	 Search.Drive = Current.Drive
      END IF
      Drive.Number = Search.Drive - 64

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

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

         ' store current directory
         Directory.Search$ = LEFT$(ASCIIZ, INSTR(ASCIIZ, CHR$(0)) - 1)
         IF LEFT$(Directory.Search$, 1) <> "\" THEN
            Directory.Search$ = "\" + Directory.Search$
         END IF
         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 = Drive.Number - 1
         CALL InterruptX(&H21, InregsX, OutregsX)
   
         ' call routine to search for files
         CALL Directories(Directory.Search$, Filename.Search$)

         ' restore current drive
         InregsX.AX = &HE00
         InregsX.DX = Current.Drive - 65 ' 0=a, 1=b, ..
         CALL InterruptX(&H21, InregsX, OutregsX)

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

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

      ' check quit searching
      IF Quit.Searching THEN
	 EXIT DO
      END IF
   LOOP

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

   ' check quit searching flag
   IF Quit.Searching THEN
      EXIT DO
   END IF
LOOP

End.Typea:

' 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 = Current.Drive - 65
CALL InterruptX(&H21, InregsX, OutregsX)

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

' restore key trapping
CALL RestInt
COLOR Plain, Black
END

' display program usage
Boot.Usage:
 ' restore key trapping
 CALL RestInt
 Var$=Inkey$
 ' make header
 COLOR White, Black
 PRINT "Typea v1.1a: ANSI file content display utility; "
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Typea [d:\path\filename.ext][//ahosx][/drt]"
 PRINT "Where:"
 PRINT "   /d  is range of file dates in form mm/dd/yyyy-mm/dd/yyyy"
 PRINT "   /r  recurse directories"
 PRINT "   /t  is range of file times in form hh:mm:ss-hh:mm:ss"
 PRINT "   type files with attributes"
 PRINT "     // prefix to not type files with,"
 PRINT "     / prefix to type files only with,"
 PRINT "       a  archive, h  hidden, o  read-only, s  system, x  none"
 COLOR Plain, Black
 END

Boot.Error:
 CALL RestInt
 Var$=Inkey$
 COLOR White, Black
 PRINT "Command line error. Type Typea /? 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 ASCIIZ AS STRING * 260
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 ASCIIZ = Directory.Search$ + "*.*" + CHR$(0)
 GOSUB Restore.DTA1

 ' check windows dos
 IF Windows.Detected THEN
    ' find first long filename
    InregsX.AX = &H714E
    InregsX.CX = &H37
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(WDTAfile)
    InregsX.DI = VARPTR(WDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    ' find first directory
    InregsX.AX = &H4E00
    InregsX.CX = &H37
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

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

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

 ' check to recurse directories
 IF Recurse.Directories THEN

    ' recurse directories
    DO
       ' check control-break
       IF BreakIS THEN
          Quit.Searching = True
       END IF

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

       ' check directory attribute
       IF Windows.Detected THEN
          Attribute = ASC(WDTAfile.FileBits)
       ELSE
          Attribute = ASC(DTAfile.FileBits)
       END IF
       IF (Attribute AND &H10) = &H10 THEN

          ' store directory name
          IF Windows.Detected THEN
             Directory$ = WDTAfile.ASCIIZfull
          ELSE
             Directory$ = DTAfile.ASCIIZfilename
          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$ + "\"

             ' recursively search subdirectories
             CALL Directories(Next.Directory$, Filename.Search$)
             GOSUB Restore.DTA1
          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(WDTAfile)
          InregsX.DI = VARPTR(WDTAfile)
          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.DTA1:
 ' 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 file
SUB DisplayFiles (Directory$, Filename$)
 ' declare subroutine variables
 DIM ASCIIZ AS STRING * 260
 DIM Attribute AS INTEGER
 DIM Handle AS INTEGER
 DIM Buffer AS STRING * 1024

 ' make filename
 ASCIIZ = Directory$ + Filename$ + CHR$(0)

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

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

    ' store file attribute
    Attribute = OutregsX.CX

    ' check file attribute
    Display.File = True

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

    ' check for read-only file
    IF Display.Readonly THEN
       IF (Attribute AND 1) <> 1 THEN
          Display.File = False
       END IF
    END IF

    ' check for hidden file
    IF Display.Hidden THEN
       IF (Attribute AND 2) <> 2 THEN
          Display.File = False
       END IF
    END IF

    ' check for system file
    IF Display.System THEN
       IF (Attribute AND 4) <> 4 THEN
          Display.File = False
       END IF
    END IF

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

    ' check for no attributes
    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 &H20) = False THEN
                   Display.File = False
                END IF
             END IF
          END IF
       END IF
    END IF

    ' check for read-only file
    IF No.Display.Readonly THEN
       IF (Attribute AND 1) = 1 THEN
          Display.File = False
       END IF
    END IF

    ' check for hidden file
    IF No.Display.Hidden THEN
       IF (Attribute AND 2) = 2 THEN
          Display.File = False
       END IF
    END IF

    ' check for system file
    IF No.Display.System THEN
       IF (Attribute AND 4) = 4 THEN
          Display.File = False
       END IF
    END IF

    ' check for archive file
    IF No.Display.Archive THEN
       IF (Attribute AND &H20) = &H20 THEN
          Display.File = False
       END IF
    END IF

    ' verify display file
    IF Display.File = False THEN
       EXIT SUB
    END IF

    ' check windows dos
    Denied = False
    IF Windows.Detected THEN
       ' open file for input
       InregsX.AX = &H716C
       InregsX.BX = &H0044
       InregsX.CX = &H27
       InregsX.DX = &H01
       InregsX.DS = VARSEG(ASCIIZ)
       InregsX.SI = VARPTR(ASCIIZ)
       InregsX.DI = &H1
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' open file for input
       InregsX.AX = &H6C00
       InregsX.BX = &H0044
       InregsX.CX = &H27
       InregsX.DX = &H01
       InregsX.DS = VARSEG(ASCIIZ)
       InregsX.SI = VARPTR(ASCIIZ)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

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

       ' check share flag
       IF Denied THEN
          EXIT SUB
       END IF

       ' store file handle
       Handle = OutregsX.AX

       ' get date\time
       InregsX.AX = &H5700
       InregsX.BX = Handle
       CALL InterruptX(&H21, InregsX, OutregsX)

       ' store date\time
       IF OutregsX.CX < False THEN
          File.Work.Time = CSNG(OutregsX.CX + 65536)
       ELSE
          File.Work.Time = CSNG(OutregsX.CX)
       END IF
       IF OutregsX.DX < False THEN
          File.Work.Date = CSNG(OutregsX.DX + 65536)
       ELSE
          File.Work.Date = CSNG(OutregsX.DX)
       END IF

       ' check date\time range
       IF Search.From.Date OR Search.To.Date THEN
          IF File.Work.Date < Search.From.Date THEN
             GOTO Close.File
          END IF
          IF File.Work.Date > Search.To.Date THEN
             GOTO Close.File
          END IF
       END IF
       IF Search.From.Time OR Search.To.Time THEN
          IF File.Work.Time < Search.From.Time THEN
             GOTO Close.File
          END IF
          IF File.Work.Time > Search.To.Time THEN
             GOTO Close.File
          END IF
       END IF

       ' display file
       DO
          InregsX.AX = &H3F00
          InregsX.BX = Handle
          InregsX.CX = &H0400
          InregsX.DS = VARSEG(Buffer)
          InregsX.DX = VARPTR(Buffer)
          CALL InterruptX(&H21, InregsX, OutregsX)

          ' check error flag
          IF (OutregsX.Flags AND &H1) = &H1 THEN
             EXIT DO
          END IF

          ' check number of bytes to display
          Number.Bytes = OutregsX.AX
          IF Number.Bytes = False THEN
             EXIT DO
          END IF

          ' display buffer
          GOSUB Print.Buffer

          ' check to quit
          IF Quit.Searching THEN
             EXIT DO
          END IF
       LOOP
       GOTO Close.File
    END IF
 END IF
 EXIT SUB

 ' close file
Close.File:
 InregsX.AX = &H3E00
 InregsX.BX = Handle
 CALL InterruptX(&H21, InregsX, OutregsX)
 EXIT SUB

Print.Buffer:
 FOR Length = 1 TO Number.Bytes
    Char = ASC(MID$(Buffer, Length, 1))
    SELECT CASE Char
    CASE 0, 26
    CASE ELSE
       InregsX.AX = &H600
       InregsX.DX = Char
       CALL InterruptX(&H21, InregsX, OutregsX)
    END SELECT
    ' reposition cursor during ansi writes
    IF Char = 10 THEN
       CALL Reset.Cursor
       IF BreakIS THEN
          Quit.Searching = True
          EXIT FOR
       END IF
    END IF
 NEXT
 RETURN
END SUB

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

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

 ' restore dta
 GOSUB Restore.DTA2

 ' check windows dos
 IF Windows.Detected THEN
    ' find first long filename
    InregsX.AX = &H714E
    InregsX.CX = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(FDTAfile)
    InregsX.DI = VARPTR(FDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    ' find first filename
    InregsX.AX = &H4E00
    InregsX.CX = &H27
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 DO
    ' check findfirst error
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       EXIT DO
    END IF

    ' store filename
    IF Windows.Detected THEN
       Filename$ = FDTAfile.ASCIIZfull
    ELSE
       Filename$ = DTAfile.ASCIIZfilename
    END IF
    Filename$ = LEFT$(Filename$, INSTR(Filename$, CHR$(0)) - 1)

    ' check filename
    IF Filename$ <> "." AND Filename$ <> ".." THEN

       ' display file
       CALL DisplayFiles(Directory.Search$, Filename$)
       GOSUB Restore.DTA2
    END IF

    ' check control-break
    IF BreakIS THEN
       Quit.Searching = True
    END IF

    ' check quit searching
    IF Quit.Searching THEN
       EXIT DO
    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(FDTAfile)
       InregsX.DI = VARPTR(FDTAfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' find next filename
       InregsX.AX = &H4F00
       CALL InterruptX(&H21, InregsX, OutregsX)
    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
 EXIT SUB

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

' routine to position cursor
SUB Reset.Cursor
 InregsX.AX = &H0300
 InregsX.BX = Video.Page
 CALL InterruptX(&H10, InregsX, OutregsX)
 Cursor.Row = (OutregsX.DX And &HFF00) / 256 + 1
 Cursor.Column = (OutregsX.DX And &HFF) + 1
 LOCATE Cursor.Row, Cursor.Column, 1
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
    Denied = True
    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
 PRINT Temp.Outpt$
 Prompt$ = "Press R to retry, Q to quit, C to continue:"
 CALL MorePrompt(Prompt$, "rqc", Outpt$)
 IF BreakIS THEN
    Outpt$ = "q"
 END IF
 SELECT CASE Outpt$
 CASE "r"
    RESUME
 CASE "q"
    Error.Level = True
    RESUME End.Typea
 CASE "c"
    OutregsX.Flags = &H1
    RESUME NEXT
 END SELECT

 ' restore key trapping
 CALL RestInt
 COLOR Plain, Black
 END 0

SUB MorePrompt (Input.String$, Input.Mask$, Output.String$)
 COLOR White, Black
 PRINT Input.String$ + " ";
 Input.Char$ = NUL
 DO
    LOCATE , , 1
    InregsX2 = InregsX
    DO
       IF BreakIS THEN
          EXIT DO
       END IF
       IF KeyIS THEN
          IF OutregsX.AX <> 0 THEN
             InregsX.AX = &H0000
             CALL InterruptX(&H16, InregsX, OutregsX)
             Input.Char$=CHR$(OutregsX.AX AND &HFF)
             EXIT DO
          END IF
       END IF
    LOOP
    InregsX = InregsX2
    IF BreakIS THEN
       EXIT DO
    END IF
    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

' clears Control-Break flag
FUNCTION ClearBreak
 DEF SEG = &H40
 POKE &H71, &H0
 DEF SEG
 ClearBreak = True
END FUNCTION

' checks Control-Break flag
FUNCTION BreakIS
 STATIC Var AS INTEGER
 IF Redirected.Input THEN
    DEF SEG = &H40
    X = PEEK(&H71)
    DEF SEG
    IF X = 64 THEN
       Var = True
    END IF
 END IF
 IF KeyIS THEN
    IF OutregsX.AX = 0 THEN
       Var = True
       DEF SEG = &H40
       POKE &H71, 64
       DEF SEG
    END IF
 END IF
 IF Var THEN
    Continuous.Display = True
 END IF
 BreakIS = Var
END FUNCTION

' checks keyboard buffer
FUNCTION KeyIS
 InregsX3 = InregsX
 InregsX.AX = &H0100
 CALL InterruptX(&H16, InregsX, OutregsX)
 InregsX = InregsX3
 IF (OutregsX.Flags AND &H40) = &H40 THEN
    KeyIS = False
 ELSE
    KeyIS = True
 END IF
END FUNCTION
