REM file: Treedel.bas - Public Domain DOS Utility
REM Version 1.0a created 05/22/1995
REM Version 1.1a created 08/30/1995
REM Version 1.2a created 02/20/1998

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

' declare subroutines
DECLARE SUB TreeDirectories (D$)
DECLARE SUB Directories (D$, X%)
DECLARE SUB DeleteDOSFiles (D$, X%)
DECLARE SUB DeleteWINFiles (D$)
DECLARE SUB DeleteDirectory (D$)

' 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'
REM $INCLUDE: 'fcb.bi'

' declare functions
DECLARE FUNCTION ParseLine (S$)

' initialize filename buffers
DIM ASCIZ AS STRING * 260
COMMON SHARED ASCIZ.Sub AS STRING * 260, ASCIZ.Tree 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

' 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 Inregs AS RegType, Outregs AS RegType

' declare work variables
COMMON SHARED Continuous.Display AS INTEGER, Display.Errors AS INTEGER
COMMON SHARED Prompt.Delete AS INTEGER, Quit.Searching AS INTEGER
COMMON SHARED Display.Lowercase AS INTEGER, Short.Display AS INTEGER
COMMON SHARED SubWDTA AS WDTAtype, TreeWDTA AS WDTAtype
COMMON SHARED Windows.Detected AS INTEGER

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

' 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
Inregs.AX = &H1900
CALL Interrupt(&H21, Inregs, Outregs)
Current.Drive = Outregs.AX AND &HFF

' 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
Endif

' 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 = UCASE$(ENVIRON$("XDIR"))
END IF

' get switches from command line
Continuous.Display = ParseLine ("/C")
Short.Display = ParseLine ("/D")
Prompt.Delete = ParseLine ("/P")
Display.Lowercase = ParseLine ("/U")
Display.Errors = ParseLine ("/Z")

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

' set searching work variables
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 "Treedel v1.2a: Directory delete 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 entire command
   Command.Work = 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.Number = ASC(UCASE$(LEFT$(Command.Work, 1))) - 65
         Command.Work = MID$(Command.Work, 3)
      ELSE
	 Drive.Number = Current.Drive
      END IF

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

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

      ' check error flag
      IF (OutregsX.Flags AND &H1) = &H0 THEN
         ' store current directory
         Directory.Search$ = LEFT$(ASCIZ, INSTR(ASCIZ, CHR$(0)) - 1)
         IF LEFT$(Command.Work, 1) = "\" THEN
            Directory.Search$ = Command.Work
         ELSE
            Directory.Search$ = Directory.Search$ + "\" + Command.Work
         END IF
         IF LEFT$(Directory.Search$, 1) <> "\" THEN
            Directory.Search$ = "\" + Directory.Search$
         END IF
         Command.Work = NUL

         ' change to drive
         InregsX.AX = &HE00
         InregsX.DX = Drive.Number
         CALL InterruptX(&H21, InregsX, OutregsX)

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

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

         ' check error flag
         IF (OutregsX.Flags AND &H1) = &H0 THEN
            ' 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)

            ' check to display
            IF Continuous.Display = False THEN
               IF Display.Lowercase THEN
                  Prompt$ = LCASE$(CHR$(Drive.Number + 65) + ":" + Directory.Search$)
               ELSE
                  IF Windows.Detected THEN
                     Prompt$ = CHR$(Drive.Number + 65) + ":" + Directory.Search$
                  ELSE
                     Prompt$ = UCASE$(CHR$(Drive.Number + 65) + ":" + Directory.Search$)
                  END IF
               END IF
               COLOR Yellow, Black
               PRINT "Searching: " + Prompt$
            END IF

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

            ' call routine to search for directories
            CALL TreeDirectories(Directory.Search$)

            ' 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

            ' check carry flag error
            IF (OutregsX.Flags AND &H1) = &H1 THEN
               Current.Directory = "\" + CHR$(0)
               ' 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
         END IF
      END IF

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

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

   ' check search filename
   IF Standard.Input$ = NUL THEN
      EXIT DO
   END IF
LOOP
End.Delete:
' restore basic dta
InregsX.AX = &H1A00
InregsX.DS = BASIC.DTA.SEG
InregsX.DX = BASIC.DTA.OFF
CALL InterruptX(&H21, InregsX, OutregsX)

' restore current drive
Inregs.AX = &HE00
Inregs.DX = Current.Drive
CALL Interrupt(&H21, Inregs, Outregs)

' display end program
IF Continuous.Display = False THEN
   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 "Treedel v1.2a: Directory delete utility; "
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Treedel [d:]\path\ [/cdpuz]"
 PRINT "Where:"
 PRINT "   /c  continuous display"
 PRINT "   /d  short file display"
 PRINT "   /p  don't prompt before deleting"
 PRINT "   /u  display lowercase"
 PRINT "   /z  supress error messages"
 COLOR Plain, Black
 END

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

' subroutine to delete a directory
SUB DeleteDirectory (Directory$)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260

 ' store directory filename
 ASCIZ = LEFT$(Directory$, LEN(Directory$) - 1) + CHR$(0)

 ' check windows dos
 IF Windows.Detected THEN
    ' delete directory
    InregsX.AX = &H713A
    InregsX.DS = VARSEG(ASCIZ)
    InregsX.DX = VARPTR(ASCIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' delete directory
    InregsX.AX = &H3A00
    InregsX.DS = VARSEG(ASCIZ)
    InregsX.DX = VARPTR(ASCIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

' subroutine to delete files in a directory in windows
SUB DeleteWINFiles (Directory$)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260

 ' make filename
 ASCIZ = Directory$ + "*.*" + CHR$(0)
 InregsX.AX = &H7141
 InregsX.DS = VARSEG(ASCIZ)
 InregsX.DX = VARPTR(ASCIZ)
 InregsX.SI = &H1
 InregsX.CX = &H27
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

' subroutine to delete files in a directory in dos
SUB DeleteDOSFiles (Directory$, Failed%)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260
 DIM FCBfile AS FCBtype

 ' make filename
 ASCIZ = LEFT$(Directory$, LEN(Directory$) - 1) + CHR$(0)

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

 ' display any errors
 CALL DisplayError ("Error changing directory.")

 ' check error flag
 Failed% = 0
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Failed% = -1
 ELSE
    ' store fcb
    FCBfile.ExtendedFCB = CHR$(&HFF)
    FCBfile.FileAttribute = CHR$(&H27)
    FCBfile.DriveNumber = CHR$(Drive.Number + 1)
    FCBfile.Filename = "????????"
    FCBfile.Extension = "???"

    ' delete filenames
    OutregsX.Flags = &H0
    InregsX.AX = &H1300
    InregsX.DS = VARSEG(FCBfile)
    InregsX.DX = VARPTR(FCBfile)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' check fcb error flag
    IF (OutregsX.AX AND &HFF) = &HFF THEN
       ' check display errors flag
       IF Display.Errors = False THEN
          COLOR Red, Black
          CALL DisplayError ("Error deleting files.")
       END IF
    END IF
 END IF

 ' change to root directory
 ASCIZ = "\" + CHR$(0)
 InregsX.AX = &H3B00
 InregsX.DS = VARSEG(ASCIZ)
 InregsX.DX = VARPTR(ASCIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

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

 ' make directory filename
 ASCIZ.Tree = Directory.Search$ + CHR$(0)
 GOSUB Restore.TDTA

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

 ' check findirst error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    IF OutregsX.AX <> &H12 THEN
       CALL DisplayError ("Error accessing directory.")
    END IF
    EXIT SUB
 END IF

 ' loop directories
 DO
    ' check directory attribute
    IF Windows.Detected THEN
       Attribute = ASC(TreeWDTA.FileAttr)
    ELSE
       Attribute = ASC(DTAfile.FileAttr)
    END IF
    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 directory
           FOR Imbedded = LEN(Directory.Search$) TO 1 STEP -1
              IF MID$(Directory.Search$, Imbedded, 1) = "\" THEN
                 Directory$ = LEFT$(Directory.Search$, Imbedded) + Directory$
                 EXIT FOR
              END IF
           NEXT
           IF RIGHT$(Directory$, 1) <> "\" THEN
              Directory$ = Directory$ + "\"
           END IF

           ' check to prompt
           IF Prompt.Delete = False THEN
              IF Display.Lowercase THEN
                 Prompt$ = LCASE$(CHR$(Drive.Number + 65) + ":" + Directory$)
              ELSE
                 IF Windows.Detected THEN
                    Prompt$ = CHR$(Drive.Number + 65) + ":" + Directory$
                 ELSE
                    Prompt$ = UCASE$(CHR$(Drive.Number + 65) + ":" + Directory$)
                 END IF
              END IF
              Prompt$ = "Delete subdirectories in " + Prompt$
              Prompt2$ = ""
              IF (Attribute AND &H20) = &H20 THEN
                 Prompt2$ = Prompt2$ + "/a"
              END IF
              IF (Attribute AND &H1) = &H1 THEN
                 Prompt2$ = Prompt2$ + "/o"
              END IF
              IF (Attribute AND &H2) = &H2 THEN
                 Prompt2$ = Prompt2$ + "/h"
              END IF
              IF (Attribute AND &H4) = &H4 THEN
                 Prompt2$ = Prompt2$ + "/s"
              END IF
              IF LEN(Prompt2$) THEN
                 Prompt$ = Prompt$ + "{" + MID$(Prompt2$, 2) + "}"
              END IF
              Prompt$ = Prompt$ + "(y/n/q)?"
              CALL MorePrompt(Prompt$, "ynq", Outpt$)
              SELECT CASE Outpt$
              CASE "n"
                 EXIT DO
              CASE "q"
                 Quit.Searching = True
                 EXIT DO
              END SELECT
           END IF

           ' store directory filename
           Temp.Dir$ = LEFT$(Directory$, LEN(Directory$) - 1)

           ' display directory
           IF Short.Display = False THEN
              Temp.Dir$ = CHR$(Drive.Number + 65) + ":" + Temp.Dir$
           END IF
           IF Display.Lowercase THEN
              Temp.Dir$ = LCASE$(Temp.Dir$)
           ELSE
              IF Windows.Detected = False THEN
                 Temp.Dir$ = UCASE$(Temp.Dir$)
              END IF
           END IF
           COLOR Yellow, Black
           IF Continuous.Display = False THEN
              PRINT "Deleting: " + Temp.Dir$
           ELSE
              PRINT Temp.Dir$
           END IF

           ' routine to delete directories
           CALL Directories(Directory$, Failed%)
           GOSUB Restore.TDTA
	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

 ' 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.TDTA:
 ' 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 access subdirectories
SUB Directories (Directory.Search$, Failed%)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 ASCIZ.Sub = Directory.Search$ + "*.*" + CHR$(0)
 GOSUB Restore.DTA

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

 ' check findirst error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    IF OutregsX.AX <> &H12 THEN
       CALL DisplayError ("Error accessing directory.")
    END IF
    EXIT SUB
 END IF

 ' delete filenames
 IF Windows.Detected THEN
    CALL DeleteWINFiles(Directory.Search$)
 ELSE
    CALL DeleteDOSFiles(Directory.Search$, Failed%)
    IF Failed% THEN
       EXIT SUB
    END IF
 END IF
 GOSUB Restore.DTA

 ' recurse subdirectories
 DO
    ' check directory attribute
    IF Windows.Detected THEN
       Attribute = ASC(SubWDTA.FileAttr)
    ELSE
       Attribute = ASC(DTAfile.FileAttr)
    END IF
    IF (Attribute AND &H10) = &H10 THEN

	' store directory name
        IF Windows.Detected THEN
           Directory$ = SubWDTA.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$ + "\"

	   ' recursively search subdirectories
           CALL Directories(Next.Directory$, Failed%)
	   GOSUB Restore.DTA
           IF Failed% THEN
              EXIT SUB
           END IF
	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(SubWDTA)
       InregsX.DI = VARPTR(SubWDTA)
       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

 ' delete directory
 CALL DeleteDirectory(Directory.Search$)

 ' 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

' 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 57
    Temp.Outpt$ = "Media error."
 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 White, Black
 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.Delete
 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
