REM file: Delete.bas - Public Domain DOS Utility
REM Version 1.0a created 05/22/1995
REM Version 1.1a created 04/23/1996
REM Version 1.2a created 03/26/2001

' declare subroutines
DECLARE SUB Directories (D$, F$)
DECLARE SUB Filenames (D$, F$)
DECLARE SUB DeleteFiles (D$, F$)

' 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 Green = 10
CONST Plain = 7
CONST Red = 12
CONST Yellow = 14
CONST White = 15

' get include files
REM $INCLUDE: 'qbx.bi'
REM $INCLUDE: 'dta.bi'
REM $INCLUDE: 'wdta.bi'

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

' initialize filename buffer
DIM ASCIZ 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

' declare work variables
COMMON SHARED Continuous.Display AS INTEGER, Files.Deleted AS DOUBLE
COMMON SHARED Total.Bytes AS DOUBLE, File.Size AS DOUBLE
COMMON SHARED Prompt.Delete AS INTEGER, Continuous.Delete AS INTEGER

' declare search work variables
COMMON SHARED File.Work.Date AS SINGLE, File.Work.Time AS SINGLE
COMMON SHARED Search.Size.From AS DOUBLE, Search.Size.To AS DOUBLE
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 Search.File.Size, Creation.Time AS INTEGER
COMMON SHARED Access.Time AS INTEGER, Modified.Time AS INTEGER

' declare recurse work variables
COMMON SHARED Recurse.Directories AS INTEGER, Redirected.Input AS INTEGER
COMMON SHARED Quit.Deleting AS INTEGER

' declare delete work variables
COMMON SHARED Delete.Hidden AS INTEGER, Delete.System AS INTEGER
COMMON SHARED Delete.Readonly AS INTEGER, Delete.Archive AS INTEGER
COMMON SHARED Dont.Delete.Hidden AS INTEGER, Dont.Delete.System AS INTEGER
COMMON SHARED Dont.Delete.Readonly AS INTEGER, Dont.Delete.Archive AS INTEGER
COMMON SHARED Dont.Delete.Any AS INTEGER, Delete.Any AS INTEGER
COMMON SHARED Delete.File AS INTEGER, Display.Lowercase AS INTEGER
COMMON SHARED Display.Errors AS INTEGER

' declare windows dta
COMMON SHARED fileWDTA AS WDTAtype
COMMON SHARED dirWDTA AS WDTAtype
COMMON SHARED delWDTA AS WDTAtype

' declare dos type
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, Control.Break 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$))
    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 = OutregsX.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 windows dos
IF Windows.Detected THEN
   ' get current directory
   InregsX.AX = &H7147
   InregsX.DX = Current.Drive + 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 = Current.Drive + 1
   InregsX.DS = VARSEG(Current.Directory)
   InregsX.SI = VARPTR(Current.Directory)
   CALL InterruptX(&H21, InregsX, OutregsX)
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)

' 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$("DELETE"))
END IF

' get attribute bit switches
Delete.Archive = ParseLine ("+A")
Delete.Hidden = ParseLine ("+H")
Delete.Readonly = ParseLine ("+O")
Delete.System = ParseLine ("+S")
Delete.Any = ParseLine ("+X")
Dont.Delete.Archive = ParseLine ("/A")
Dont.Delete.Hidden = ParseLine ("/H")
Dont.Delete.Readonly = ParseLine ("/O")
Dont.Delete.System = ParseLine ("/S")
Dont.Delete.Any = ParseLine ("/X")

' get switches from command line
Continuous.Display = ParseLine ("/C")
Prompt.Delete = ParseLine ("/P")
Recurse.Directories = ParseLine ("/R")
Display.Lowercase = ParseLine ("/U")
Display.Errors = ParseLine ("/Z")
Control.Break = 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
   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
         Modofied.Time = True
      END IF
   END IF
END IF

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

' initialize some variables
Continuous.Delete = False
Files.Deleted = FalseD
Total.Bytes = FalseD
Quit.Deleting = False

' store 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
DO
   ' check control break
   IF BreakIS THEN
      EXIT DO
   END IF

   ' get standard input
   Standard.Input$ = NUL
   InregsX.AX = &HB00
   CALL InterruptX(&H21, InregsX, OutregsX)
   DO WHILE (OutregsX.AX AND &HFF) = &HFF
      Redirected.Input = True
      InregsX.AX = &H800
      CALL InterruptX(&H21, InregsX, OutregsX)
      Char$ = CHR$(OutregsX.AX AND &HFF)
      SELECT CASE ASC(Char$)
      CASE 10, 26
      CASE 13
	 EXIT DO
      CASE ELSE
	 Standard.Input$ = Standard.Input$ + Char$
      END SELECT
      InregsX.AX = &HB00
      CALL InterruptX(&H21, InregsX, OutregsX)
   LOOP

   ' 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

   ' display header
   GOSUB Header

   ' 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 flag 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 = Drive.Number
         CALL InterruptX(&H21, InregsX, OutregsX)
   
         ' display any errors
         CALL DisplayError ("Error accessing drive.")

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

            ' display search filename
            IF Continuous.Display = False THEN
               Prompt$ = Directory.Search$ + Filename.Search$
               IF Display.Lowercase THEN
                  Prompt$ = LCASE$(Prompt$)
               ELSE
                  IF Windows.Detected = False THEN
                     Prompt$ =  UCASE$(Prompt$)
                  END IF
               END IF
               COLOR Yellow, Black
               Prompt$ = CHR$(Drive.Number + 65) + ":" + Prompt$
               Prompt$ = "Searching: " + Prompt$
               PRINT Prompt$
            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

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

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

   ' check quit searching
   IF Quit.Deleting 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
InregsX.AX = &HE00
InregsX.DX = Current.Drive
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

' display end program
IF Continuous.Display = False THEN
   COLOR Yellow, Black
   Total$ = "Files deleted "
   Total$ = Total$ + Format$(Files.Deleted, "#,##0;;0")
   Total$ = Total$ + " Total bytes deleted "
   Total$ = Total$ + Format$(Total.Bytes, "#,##0;;0")
   PRINT Total$
   Prompt$ = "Press <enter> to exit to DOS:"
   CALL MorePrompt(Prompt$, CHR$(13), Outpt$)
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 "Delete v1.2a: File delete utility; "
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Delete [d:\path\filename.ext][+/ahosx][/cdfprtuz]"
 PRINT "Where:"
 PRINT "   /c  continuous display   /p  do not prompt before deleting"
 PRINT "   /r  recurse directories  /u  display files in lowercase"
 PRINT "   /z  suppress error messages"
 PRINT "   display ranges: (/1  creation, /2  last access  /3  modify time)"
 PRINT "     /d  range of file dates in form mm/dd/yyyy-mm/dd/yyyy"
 PRINT "     /t  range of file times in form hh:mm:ss-hh:mm:ss"
 PRINT "     /f  range of file size xxx-xxx in kilobytes"
 PRINT "   delete file attributes:"
 PRINT "     + prefix for files with, / prefix for files without,"
 PRINT "       a  archive, h  hidden, o  read-only, s  system, x  none"
 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:
 CALL RestInt
 Var$=Inkey$
 COLOR White, Black
 PRINT "Command line error. Type Delete /? for help."
 COLOR Plain, Black
 END

' make header
Header:
 IF Header.Flag THEN
    RETURN
 END IF
 Header.Flag = True
 IF Continuous.Display = False THEN
    COLOR White, Black
    PRINT "Delete v1.2a: File delete utility; "
 END IF
 RETURN
   
' subroutine to delete a filename
SUB DeleteFiles (Directory$, Filename$)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make filename
 ASCIZ = Directory$ + Filename$ + CHR$(0)
 GOSUB Restore.dDTA

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

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

 ' store file attribute
 Attribute = OutregsX.CX

 ' reset delete flag
 Delete.File = True

 ' check for read-only file
 IF (Attribute AND &H1) = &H1 THEN
    IF Dont.Delete.Readonly THEN
       EXIT SUB
    END IF
 ELSE
    IF Delete.Readonly THEN
       EXIT SUB
    END IF
 END IF

 ' check for hidden file
 IF (Attribute AND &H2) = &H2 THEN
    IF Dont.Delete.Hidden THEN
       EXIT SUB
    END IF
 ELSE
    IF Delete.Hidden THEN
       EXIT SUB
    END IF
 END IF

 ' check for system file
 IF (Attribute AND &H4) = &H4 THEN
    IF Dont.Delete.System THEN
       EXIT SUB
    END IF
 ELSE
    IF Delete.System THEN
       EXIT SUB
    END IF
 END IF

 ' check for archive file
 IF (Attribute AND &H20) = &H20 THEN
    IF Dont.Delete.Archive THEN
       EXIT SUB
    END IF
 ELSE
    IF Delete.Archive THEN
       EXIT SUB
    END IF
 END IF

 ' check all bits
 IF Delete.Any THEN
    IF (Attribute AND &H1) = &H1 THEN
       EXIT SUB
    END IF
    IF (Attribute AND &H2) = &H2 THEN
       EXIT SUB
    END IF
    IF (Attribute AND &H4) = &H4 THEN
       EXIT SUB
    END IF
    IF (Attribute AND &H20) = &H20 THEN
       EXIT SUB
    END IF
 END IF
 IF Dont.Delete.Any THEN
    IF (Attribute AND &H1) = False THEN
       IF (Attribute AND &H2) = False THEN
          IF (Attribute AND &H4) = False THEN
             IF (Attribute AND &H20) = False THEN
                EXIT SUB
             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

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

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

 ' store file date and time
 IF Windows.Detected THEN
    IF Creation.Time THEN
       File.Work.Time = ASC(MID$(delWDTA.CreateTime, 2, 1))
       File.Work.Time = File.Work.Time * &H100 + ASC(MID$(delWDTA.CreateTime, 1, 1))
       File.Work.Date = ASC(MID$(delWDTA.CreateTime, 4, 1))
       File.Work.Date = File.Work.Date * &H100 + ASC(MID$(delWDTA.CreateTime, 3, 1))
    ELSE
       IF Access.Time THEN
          File.Work.Time = ASC(MID$(delWDTA.AccessTime, 2, 1))
          File.Work.Time = File.Work.Time * &H100 + ASC(MID$(delWDTA.AccessTime, 1, 1))
          File.Work.Date = ASC(MID$(delWDTA.AccessTime, 4, 1))
          File.Work.Date = File.Work.Date * &H100 + ASC(MID$(delWDTA.AccessTime, 3, 1))
       ELSE
          IF Modified.Time THEN
             File.Work.Time = ASC(MID$(delWDTA.ModTime, 2, 1))
             File.Work.Time = File.Work.Time * &H100 + ASC(MID$(delWDTA.ModTime, 1, 1))
             File.Work.Date = ASC(MID$(delWDTA.ModTime, 4, 1))
             File.Work.Date = File.Work.Date * &H100 + ASC(MID$(delWDTA.ModTime, 3, 1))
          END IF
       END IF
    END IF
 ELSE
    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

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

 ' check delete flag
 IF Delete.File THEN
    ' check delete type
    IF Continuous.Delete = False THEN
       ' check for prompt to delete
       IF Prompt.Delete THEN
          ' display filename
          Outpt$ = Directory$ + Filename$
          IF Display.Lowercase THEN
             Outpt$ = LCASE$(Outpt$)
          ELSE
             IF Windows.Detected = False THEN
                Outpt$ = UCASE$(Outpt$)
             END IF
          END IF
          COLOR Yellow, Black
          PRINT Outpt$
       ELSE
          ' prompt to delete
          Prompt$ = Directory$ + Filename$
          IF Display.Lowercase THEN
             Prompt$ = LCASE$(Prompt$)
          ELSE
             IF Windows.Detected = False THEN
                Prompt$ = UCASE$(Prompt$)
             END IF
          END IF
          Prompt$ = CHR$(Drive.Number + 65) + ":" + 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$ = "Delete file " + Prompt$ + "(y/n/q/c)?"
          CALL MorePrompt(Prompt$, "ynqc", Outpt$)
          IF BreakIS THEN
             Outpt$ = "q"
          END IF
          SELECT CASE Outpt$
          CASE "n"
             Delete.File = False
          CASE "q"
             Delete.File = False
             Quit.Deleting = True
          CASE "c"
             Continuous.Delete = True
          END SELECT
       END IF
    END IF

    ' check delete file flag
    IF Delete.File THEN
       ' check windows dos
       IF Windows.Detected THEN
          ' change filename attribute
          InregsX.AX = &H7143
          InregsX.BX = &H1
          InregsX.CX = &H0
          InregsX.DS = VARSEG(ASCIZ)
          InregsX.DX = VARPTR(ASCIZ)
          CALL InterruptX(&H21, InregsX, OutregsX)
          ' delete long filename
          InregsX.AX = &H7141
          InregsX.DS = VARSEG(ASCIZ)
          InregsX.DX = VARPTR(ASCIZ)
          InregsX.SI = &H0
          InregsX.CX = &H0
          CALL InterruptX(&H21, InregsX, OutregsX)
       ELSE
          ' set file attributes
          InregsX.AX = &H4301
          InregsX.DS = VARSEG(ASCIZ)
          InregsX.DX = VARPTR(ASCIZ)
          InregsX.CX = &H0
          CALL InterruptX(&H21, InregsX, OutregsX)
          ' delete filename
          InregsX.AX = &H4100
          InregsX.DS = VARSEG(ASCIZ)
          InregsX.DX = VARPTR(ASCIZ)
          InregsX.CX = &H0
          CALL InterruptX(&H21, InregsX, OutregsX)
       END IF

       ' display any errors
       CALL DisplayError("Error "+HEX$(OutregsX.AX)+"H deleting filename.")

       ' check error flag
       IF (OutregsX.Flags AND &H1) = &H0 THEN
          ' check display type
          IF Prompt.Delete = False THEN
             IF Continuous.Display = False THEN
                Prompt$ = Directory$ + Filename$
                IF Display.Lowercase THEN
                   Prompt$ = LCASE$(Prompt$)
                ELSE
                   IF Windows.Detected = False THEN
                      Prompt$ = UCASE$(Prompt$)
                   END IF
                END IF
                COLOR Yellow, Black
                Prompt$ = CHR$(Drive.Number + 65) + ":" + Prompt$
                Prompt$ = "Deleting: " + Prompt$
                PRINT Prompt$
             END IF
          END IF
          ' update counters
          Files.Deleted = Files.Deleted + 1#
          Total.Bytes = Total.Bytes + File.Size
       END IF
    END IF
 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.dDTA:
 ' 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 directories
SUB Directories (Directory.Search$, Filename.Search$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM ASCIZ AS STRING * 260
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 ASCIZ = 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)
    InregsX.DX = VARPTR(ASCIZ)
    InregsX.ES = VARSEG(dirWDTA)
    InregsX.DI = VARPTR(dirWDTA)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    ' find first directory
    InregsX.AX = &H4E00
    InregsX.CX = &H37
    InregsX.DS = VARSEG(ASCIZ)
    InregsX.DX = VARPTR(ASCIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' check flag error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    EXIT SUB
 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 control-break
       IF BreakIS THEN
          Quit.Deleting = True
       END IF

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

       ' check directory attribute
       IF Windows.Detected THEN
          Attribute = ASC(dirWDTA.FileAttr)
       ELSE
          Attribute = ASC(DTAfile.FileAttr)
       ENDIF
       IF (Attribute AND &H10) = &H10 THEN
          ' store directory name
          IF Windows.Detected THEN
             Directory$ = dirWDTA.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$, Filename.Search$)
             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(dirWDTA)
          InregsX.DI = VARPTR(dirWDTA)
          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 delete filenames in directory
SUB Filenames (Directory.Search$, Filename.Search$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM ASCIZ AS STRING * 260
 DIM Wfile.Handle AS INTEGER

 ' make filename
 ASCIZ = Directory.Search$ + Filename.Search$ + CHR$(0)
 GOSUB Restore.fDTA

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

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

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

 ' recurse filename
 DO

    ' store filename
    IF Windows.Detected THEN
       Filename$ = fileWDTA.ASCIZfull
    ELSE
       Filename$ = DTAfile.ASCIZfilename
    END IF
    Filename$ = LEFT$(Filename$, INSTR(Filename$, CHR$(0)) - 1)

    ' 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

    ' check filename
    IF Filename$ <> "." AND Filename$ <> ".." THEN
       ' delete filename
       CALL DeleteFiles(Directory.Search$, Filename$)
       GOSUB Restore.fDTA
    END IF

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

    ' check to quit
    IF Quit.Deleting 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(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 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.fDTA:
 ' 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 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.Delete
 CASE "c"
    OutregsX.Flags = &H1
    RESUME NEXT
 END SELECT
 COLOR Plain, Black
 ' restore key trapping
 CALL RestInt
 END 0

SUB MorePrompt (Input.String$, Input.Mask$, Output.String$)
 COLOR White, Black
 PRINT Input.String$ + " ";
 Input.Char$ = NUL
 DO
    LOCATE , , 1
    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
    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 KeyIS THEN
    IF OutregsX.AX = 0 THEN
       Var = True
    END IF
 END IF
 IF Var THEN
    Continuous.Display = True
 END IF
 BreakIS = Var
END FUNCTION

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

' 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
