REM file: Generic Uninstal Utility.
REM version: 1.0a PD 07/09/2000
REM version: 1.1a PD 08/22/2000
REM version: 1.2a PD 01/05/2005
REM version: 1.3a PD 12/10/2005
REM version: 1.4a PD 08/31/2006

' declare directory delete subroutines
DECLARE SUB TreeDirectories(D$)
DECLARE SUB Directories(D$)
DECLARE SUB DeleteDirectory(D$)
DECLARE SUB DeleteDOSFiles(D$)

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

' declare filename editing subroutine
DECLARE SUB Edit.Autoexec()

' declare temporary filename delete subroutine
DECLARE SUB Delete.Temp.Files()

' declare config filename read subroutine
DECLARE SUB Read.Config()

' default integer variables
DEFINT A-Z

' runtime array loading
REM $DYNAMIC

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

' define constant values
CONST MaxAutoexec = 10
CONST MaxTemp = 10

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

' declare interrupt structure
Type RegTypeX
 AX AS Integer
 BX AS Integer
 CX AS Integer
 DX AS Integer
 BP AS Integer
 SI AS Integer
 DI AS Integer
 Flags AS Integer
 DS AS Integer
 ES AS Integer
End Type

Declare Sub InterruptX(N AS Integer,I AS RegTypeX,O AS RegTypeX)

' declare Data Transfer Area structure
TYPE DTAtype
 Drive AS STRING * 1
 SearchTemplate AS STRING * 11
 SearchAttr AS STRING * 1
 EntryCount AS STRING * 2
 ClusterNumber AS STRING * 2
 Reserved AS STRING * 4
 FileBits AS STRING * 1
 FileTime AS STRING * 2
 FileDate AS STRING * 2
 FileSize AS STRING * 4
 ASCIIZfilename AS STRING * 13
END TYPE

' declare Windows Data Transfer Area structure
TYPE WDTAtype
 FileBits AS STRING * 4
 CreateTime AS STRING * 8
 AccessTime AS STRING * 8
 ModTime AS STRING * 8
 FileSizeHigh AS STRING * 4
 FileSizeLow AS STRING * 4
 Reserved AS STRING * 8
 ASCIIZfull AS STRING * 260
 ASCIIZshort AS STRING * 14
END TYPE

' declare File Control Block structure
TYPE FCBtype
 ExtendedFCB AS String * 1 ' ffh
 Reserved1 As String * 5
 FileAttribute As String * 1
 DriveNumber As String * 1 ' 0=default, 1=a, etc.
 Filename As String * 8
 Extension As String * 3
 BlockNumber As String * 2
 RecordSize As String * 2
 FileSize As String * 4
 FileDate As String * 2
 FileTime As String * 2
 Reserved2 As String * 8
 BlockRecord As String * 1
 RecordNumber As String * 4
END TYPE

' declare functions
DECLARE FUNCTION ParseLine (S$)

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

' initialize directory variables
COMMON SHARED Current.Directory AS STRING * 260
COMMON SHARED Batch.Path AS STRING ' name to remove from autoexec.bat path
COMMON SHARED Ram.Temp AS STRING ' pathname for temporary ram drive files

' initialize filename buffers
DIM ASCIIZ AS STRING * 260
COMMON SHARED ASCIIZ.Sub AS STRING * 260
COMMON SHARED ASCIIZ.Tree AS STRING * 260

' initialize long filename buffers
COMMON SHARED delWDTA AS WDTAtype
COMMON SHARED fileWDTA AS WDTAtype
COMMON SHARED SubWDTA AS WDTAtype
COMMON SHARED TreeWDTA AS WDTAtype

' initialize autoexec.bat array of environment variables to remove
COMMON SHARED Autoexec.Array() AS STRING * 128

' initialize temporary filename array to remove from ram drive
COMMON SHARED Filename.Array() AS STRING * 128

' 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 command line work variables
COMMON SHARED Autoexec.Prompt AS INTEGER
COMMON SHARED Command.Line AS STRING
COMMON SHARED Continuous.Display AS INTEGER
COMMON SHARED Display.Errors AS INTEGER
COMMON SHARED Edit.Prompt AS INTEGER
COMMON SHARED Delete.Directory AS INTEGER
COMMON SHARED Delete.Temp AS INTEGER
COMMON SHARED Delete.Prompt AS INTEGER
COMMON SHARED Display.Prompt AS INTEGER
COMMON SHARED Ram.Drive AS STRING * 1
COMMON SHARED Config.Filename AS STRING
COMMON SHARED Last.Switch AS INTEGER

' declare work variables
COMMON SHARED Windows.Detected AS INTEGER

' increase stack size
STACK STACK

' declare standard error trap
ON ERROR GOTO Error.Routine

' allocate array of environment variables
REDIM Autoexec.Array(1 TO MaxAutoexec) AS STRING * 128

' allocate array of temporary filenames
REDIM Filename.Array(1 TO MaxTemp) AS STRING * 128

' 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

' 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
Command.Line = RTRIM$(Command.Line)

' get ram drive switch from command line
Ram.Drive = NUL
Drive = INSTR(UCASE$(Command.Line), "/S:")
IF Drive THEN
   Last.Switch = Imbedded - 1
   Ram.Drive = MID$(Command.Line, Drive + 3, 1)
   Ram.Drive = UCASE$(Ram.Drive)
   IF Ram.Drive >= "A" AND Ram.Drive <= "Z" THEN
      Command.Line = LEFT$(Command.Line, Drive - 1) + MID$(Command.Line, Drive + 4)
   ELSE
      GOTO Boot.Error
   END IF
END IF

' assign autoexec array of environment variables
FOR Element = 1 TO MaxAutoexec
   Autoexec.Array(Element) = NUL
NEXT

' assign lines to remove
Autoexec.Array(1) = "SET UNINSTAL1="
Autoexec.Array(2) = "SET UNINSTAL2="
Autoexec.Array(3) = "SET UNINSTAL3="
Autoexec.Array(4) = "SET UNINSTAL4="
Autoexec.Array(5) = "SET UNINSTAL5="

' assign filename array
FOR Element = 1 TO MaxTemp
   Filename.Array(Element) = NUL
NEXT

' assign filenames
Filename.Array(1) = "ALTWORK?.DAT"
Filename.Array(2) = "MESSWRK?.DAT"
Filename.Array(3) = "TEMPWRK?.DAT"

' get temporary environment drive variable
Temp.Drive$ = "TEMP"
Ram.Temp = ENVIRON$(Temp.Drive$)

' get config filename from command line
Config.Filename = NUL
Switch = INSTR(UCASE$(Command.Line), "/F:" + CHR$(34))
Var = False
IF Switch THEN
   FOR Temp = Switch + 4 TO LEN(Command.Line)
      IF MID$(Command.Line, Temp, 1) = CHR$(34) THEN
         Var = True
         Last.Switch = Switch - 1
         EXIT FOR
      END IF
      Config.Filename = Config.Filename + MID$(Command.Line, Temp, 1)
   NEXT
   IF Var = False THEN
      GOTO Boot.Error
   END IF
   IF Config.Filename = Nul THEN
      GOTO Boot.Error
   END IF
   Command.Line = LEFT$(Command.Line, Switch - 1) + MID$(Command.Line, Temp + 1)
   CALL Read.Config
END IF

' get switches from command line
Edit.Prompt = ParseLine ("/A")
Continuous.Display = ParseLine ("/C")
Delete.Temp = ParseLine ("/D")
Display.Prompt = ParseLine ("/P")
AutoExec.Prompt = ParseLine ("/Q")
Delete.Prompt = ParseLine ("/R")
Delete.Directory = ParseLine ("/X")
Display.Errors = ParseLine ("/Z")
Var = ParseLine("/_")

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

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

' recheck command line
Temp.Path$ = "UNINSTAL"
IF Command.Line = NUL THEN
   Environ.Path$ = ENVIRON$(Temp.Path$)
   IF Environ.Path$ <> NUL THEN
      Command.Line = Environ.Path$
   END IF
END IF

' make header
IF Continuous.Display = False THEN
   COLOR White, Black
   PRINT "Generic Uninstal Utility v1.4a: "
END IF

' check command line
IF LEFT$(Command.Line, 1) = CHR$(34) THEN
   Command.Line = MID$(Command.Line, 2)
END IF
IF RIGHT$(Command.Line, 1) = CHR$(34) THEN
   Command.Line = LEFT$(Command.Line, LEN(Command.Line) - 1)
END IF

' store current drive
IF MID$(Command.Line, 2, 1) = ":" THEN
   Drive.Number = ASC(UCASE$(LEFT$(Command.Line, 1))) - 65
   Command.Line = MID$(Command.Line, 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(ASCIIZ)
   InregsX.SI = VARPTR(ASCIIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
ELSE
   ' get current directory
   InregsX.AX = &H4700
   InregsX.DX = Drive.Number + 1
   InregsX.DS = VARSEG(ASCIIZ)
   InregsX.SI = VARPTR(ASCIIZ)
   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$(ASCIIZ, INSTR(ASCIIZ, CHR$(0)) - 1)
   IF Command.Line <> NUL THEN
      IF LEFT$(Command.Line, 1) <> "\" THEN
         Directory.Search$ = Directory.Search$ + "\" + Command.Line
      ELSE
         Directory.Search$ = Command.Line
      END IF
   END IF
   IF LEFT$(Directory.Search$, 1) <> "\" THEN
      Directory.Search$ = "\" + Directory.Search$
   END IF
   IF RIGHT$(Directory.Search$, 1) = "\" THEN
      Directory.Search$ = LEFT$(Directory.Search$, LEN(Directory.Search$) - 1)
   END IF

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

      Prompt$ = CHR$(Drive.Number + 65) + ":" + Directory.Search$
      Prompt$ = UCASE$(Prompt$)
      IF Continuous.Display = False THEN
         COLOR Yellow, Black
         PRINT "Searching: " + Prompt$
      END IF
      IF Batch.Path = NUL THEN
         Batch.Path = Prompt$
      END IF

      ' check to delete directory
      IF Delete.Directory = False THEN
         ' call routine to search for directories
         CALL TreeDirectories(Directory.Search$)
      END IF

      ' 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
IF Edit.Prompt = False THEN
   CALL Edit.Autoexec
END IF
IF Delete.Temp = False THEN
   CALL Delete.Temp.Files
END IF

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)

' 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 "Generic Uninstal Utility v1.4a:"
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Uninstal [d:\path\] [/acdfpqrsxz]"
 PRINT "Where:"
 PRINT "   d:\path\ optionally specifies pathname"
 PRINT "   /a  ignores editing Autoexec.bat"
 PRINT "   /c  continuous display"
 PRINT "   /d  ignores deleting temp files"
 PRINT "   /f:" + CHR$(34) + "<filename.ext>" + CHR$(34) + " config filename"
 PRINT "   /p  bypass delete prompt"
 PRINT "   /q  don't prompt to edit autoexec.bat"
 PRINT "   /r  don't prompt to delete temp files"
 PRINT "   /s:d  specify Ram drive letter"
 PRINT "   /x  don't remove directory"
 PRINT "   /z  suppress error messages"
 COLOR Plain, Black
 END

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

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

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

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

' subroutine to delete files in a directory in dos
SUB DeleteDOSFiles (Directory$)
 ' declare subroutine variables
 DIM ASCIIZ AS STRING * 260
 DIM ASCIIZ1 AS STRING * 260
 DIM ASCIIZ2 AS STRING * 261
 DIM FCBfile AS FCBtype

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

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

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

    ' ambiguate path
    ASCIIZ1 = LEFT$(Directory$, LEN(Directory$) - 1) + CHR$(0)
    InregsX.AX = &H7160
    InregsX.CX = &H8001
    InregsX.DS = VARSEG(ASCIIZ1)
    InregsX.SI = VARPTR(ASCIIZ1)
    InregsX.ES = VARSEG(ASCIIZ2)
    InregsX.DI = VARPTR(ASCIIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)
    ASCIIZ = ASCIIZ2

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

 ' check error flag
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' 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
          CALL DisplayError ("Error deleting files.")
       END IF
    END IF
 END IF

 ' change to root directory
 ASCIIZ = "\" + CHR$(0)
 InregsX.AX = &H3B00
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 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
 ASCIIZ.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(ASCIIZ.Tree)
    InregsX.DX = VARPTR(ASCIIZ.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(ASCIIZ.Tree)
    InregsX.DX = VARPTR(ASCIIZ.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.FileBits)
    ELSE
       Attribute = ASC(DTAfile.FileBits)
    END IF
    IF (Attribute AND &H10) = &H10 THEN

       ' store directory name
       IF Windows.Detected THEN
          Directory$ = TreeWDTA.ASCIIZfull
       ELSE
          Directory$ = DTAfile.ASCIIZfilename
       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
          Delete.Flag = False
          IF Windows.Detected THEN
             Prompt$ = CHR$(Drive.Number + 65) + ":" + Directory$
          ELSE
             Prompt$ = UCASE$(CHR$(Drive.Number + 65) + ":" + Directory$)
          END IF
          IF Display.Prompt = False THEN
             Prompt$ = "Delete subdirectories in " + Prompt$ + "(y/n)?"
             CALL MorePrompt(Prompt$, "yn", Outpt$)
             SELECT CASE Outpt$
             CASE "n"
                Delete.Flag = True
             END SELECT
          END IF

          ' check to continue deleting
          IF Delete.Flag = False THEN
             ' store directory filename
             Temp.Dir$ = LEFT$(Directory$, LEN(Directory$) - 1)

             ' display directory
             Temp.Dir$ = CHR$(Drive.Number + 65) + ":" + Temp.Dir$
             IF Windows.Detected = False THEN
                Temp.Dir$ = UCASE$(Temp.Dir$)
             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$)
             GOSUB Restore.TDTA
          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(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

 ' delete directory
 CALL DeleteDirectory(Directory$)
 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$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 ASCIIZ.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(ASCIIZ.Sub)
    InregsX.DX = VARPTR(ASCIIZ.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(ASCIIZ.Sub)
    InregsX.DX = VARPTR(ASCIIZ.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
 CALL DeleteDOSFiles(Directory.Search$)
 GOSUB Restore.DTA

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

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

 ' 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

 ' delete directory
 CALL DeleteDirectory(Directory.Search$)
 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 edit autoexec.bat
SUB Edit.Autoexec
 IF Autoexec.Prompt = False THEN
    Prompt$ = "Edit Autoexec.bat(y/n)"
    CALL MorePrompt(Prompt$, "yn", Outpt$)
    SELECT CASE Outpt$
    CASE "n"
       EXIT SUB
    END SELECT
 END IF
 CLOSE
 Filename1$ = "C:\AUTOEXEC.BAT"
 Filename2$ = "C:\AUTOEXEC.BKP"
 OPEN Filename1$ FOR INPUT AS #1
 OPEN Filename2$ FOR OUTPUT AS #2
 IF Continuous.Display = False THEN
    COLOR Yellow, Black
    PRINT "Making backup of Autoexec.bat"
 END IF
 WHILE NOT EOF(1)
    LINE INPUT #1, File.Line$
    PRINT #2, File.Line$
 WEND
 IF Continuous.Display = False THEN
    COLOR Yellow, Black
    PRINT "Editing Autoexec.bat"
 END IF
 CLOSE
 OPEN Filename1$ FOR OUTPUT AS #1
 OPEN Filename2$ FOR INPUT AS #2
 WHILE NOT EOF(2)
    LINE INPUT #2, File.Line1$
    File.Line2$ = UCASE$(File.Line1$)
    Copy.Line = True
    IF LEFT$(File.Line2$,5)="PATH " OR _
    LEFT$(File.Line2$,5)="PATH=" OR _
    LEFT$(File.Line2$,9)="SET PATH=" THEN
       Imbedded = INSTR(File.Line2$, Batch.Path)
       IF Imbedded THEN
          IF Continuous.Display = False THEN
             COLOR Yellow, Black
             PRINT "Removing "; Batch.Path; " from Autoexec.bat PATH="
          END IF
          File.Line1$ = LEFT$(File.Line1$, Imbedded - 1) + _
          MID$(File.Line1$, Imbedded + LEN(Batch.Path) + 1)
       END IF
    END IF
    FOR Element = 1 TO MaxAutoexec
       Edit.Line$ = Autoexec.Array(Element)
       Edit.Line$ = RTRIM$(Edit.Line$)
       IF LEN(Edit.Line$) THEN
          IF LEFT$(File.Line2$, LEN(Edit.Line$)) = Edit.Line$ THEN
             IF Continuous.Display = False THEN
                COLOR Yellow, Black
                PRINT "Removing "; Edit.Line$; " from Autoexec.bat SET="
             END IF
             Copy.Line = False
             EXIT FOR
          END IF
       ELSE
          EXIT FOR
       END IF
    NEXT
    IF Copy.Line THEN
       PRINT #1, File.Line1$
    END IF
 WEND
END SUB

' subroutine to remove temporary files from ram drive
SUB Delete.Temp.Files
 IF Delete.Prompt = False THEN
    Prompt$ = "Remove temporary files from Ram drive(y/n)?"
    CALL MorePrompt(Prompt$, "yn", Outpt$)
    SELECT CASE Outpt$
    CASE "n"
       EXIT SUB
    END SELECT
 END IF
 IF RTRIM$(Ram.Drive) <> NUL THEN
    Ram.Temp = Ram.Drive + ":\"
 END IF
 IF Ram.Temp = NUL THEN
    IF Delete.Prompt = False THEN
       Prompt$ = "Enter Ram drive letter(a-z)?"
       CALL MorePrompt(Prompt$, "abcdefghijklmnopqrstuvwxyz", Outpt$)
       IF Outpt$ >= "a" AND Outpt$ <= "z" THEN
          Ram.Temp = UCASE$(Outpt$) + ":\"
       END IF
    END IF
 END IF
 IF Ram.Temp = NUL THEN
    OutregsX.Flags = &H1
    CALL DisplayError ("Error accessing ram drive.")
    EXIT SUB
 END IF
 IF RIGHT$(Ram.Temp, 1) <> "\" THEN
    Ram.Temp = Ram.Temp + "\"
 END IF
 IF Continuous.Display = False THEN
    COLOR Yellow, Black
    PRINT "Deleting files:"
 END IF
 FOR Element = 1 TO MaxTemp
    Filename$ = Filename.Array(Element)
    Filename$ = RTRIM$(Filename$)
    IF LEN(Filename$) THEN
       CALL Filenames(Ram.Temp, Filename$)
    ELSE
       EXIT FOR
    END IF
 NEXT
END SUB

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

 ' check display flag
 IF Continuous.Display = False THEN
    COLOR Yellow, Black
    PRINT Directory.Search$ + Filename.Search$
 END IF

 ' make filename
 ASCIIZ = 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(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    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(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    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.ASCIIZfull
    ELSE
       Filename$ = DTAfile.ASCIIZfilename
    END IF
    Filename$ = LEFT$(Filename$, INSTR(Filename$, CHR$(0)) - 1)

    ' check filename
    IF Filename$ <> "." AND Filename$ <> ".." THEN
       ' delete filename
       CALL DeleteFiles(Directory.Search$, Filename$)
       GOSUB Restore.fDTA
    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

' subroutine to delete a filename
SUB DeleteFiles (Directory$, Filename$)
 ' declare subroutine variables
 DIM ASCIIZ AS STRING * 260
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

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

 ' 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(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(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

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

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

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

 ' 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 read config file
SUB Read.Config
 ON LOCAL ERROR GOTO Config.Error
 ' read config file
 CLOSE
 OPEN Config.Filename FOR INPUT AS #1
 DO
    LINE INPUT #1, Var$
    IF LEFT$(Var$, 1) <> ";" THEN
       Batch.Path = Var$
       EXIT DO
    END IF
 LOOP
 FOR Temp = 1 TO 5
    DO
       LINE INPUT #1, Temp$
       IF LEFT$(Temp$, 1) <> ";" THEN
          EXIT DO
       END IF
    LOOP
    IF Temp$ <> NUL THEN
       Temp$ = "SET " + Temp$ + "="
       Autoexec.Array(Temp) = Temp$
    END IF
 NEXT
 DO
    LINE INPUT #1, Temp$
    IF LEFT$(Temp$, 1) <> ";" THEN
       EXIT DO
    END IF
 LOOP
 IF LEN(Temp$) = 1 THEN
    Temp$ = UCASE$(Temp$)
    IF Temp$ >= "A" AND Temp$ <= "Z" THEN
       Ram.Drive = Temp$
    END IF
 END IF
 FOR Temp = 1 TO 3
    DO
       LINE INPUT #1, Temp$
       IF LEFT$(Temp$, 1) <> ";" THEN
          EXIT DO
       END IF
    LOOP
    IF Temp$ <> NUL THEN
       Filename.Array(Temp) = Temp$
    END IF
 NEXT
 EXIT SUB
Config.Error:
 COLOR White, Black
 PRINT "Error reading config file: " + Config.Filename
 COLOR Plain, Black
 END
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
