REM file: Copyit.bas - Public Domain DOS Utility. Module 1 of 2. v2.0a.

' get standard include declarations
REM $INCLUDE: 'COPYIT.INC'

' declare external procedures
DECLARE SUB SetInt
DECLARE SUB RestInt

' backwards compatible for bc 7.1
REM $INCLUDE: 'BC7.INC'

' declare dos command work variables
REDIM DOS.Command(1 TO 10) AS STRING * 128
REDIM Parameters(1 TO 12) AS STRING * 260

' declare filename array
REDIM Skip.Filenames(1 TO 10) AS STRING * 12
REDIM Excluded.Files(1 TO 10) AS STRING * 12

' declare work arrays
REDIM Convert.Ascii(1 To 10, 1 To 2) AS INTEGER
REDIM Strip.Ascii(1 To 10) AS INTEGER

' declare destination work arrays
REDIM Destinate.Directory(1 TO 10) AS STRING * 260
REDIM Destinate.Filename(1 TO 10) AS STRING * 260

' increase stack size
STACK STACK

' install new interrupt service routine
CALL SetInt

' declare standard error trap
ON ERROR GOTO Error.Routine

' get command line switches
CALL ReadSwitches

' store skip file list
Skip.Filenames(1) = "WIN386.SWP"

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

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

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

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

' store default directory
Default.Dir = Directory.ASCIZ
Imbedded = INSTR(Default.Dir, CHR$(0))
Default.Dir = LEFT$(Default.Dir, Imbedded - 1)
IF Default.Dir = NUL THEN
   Default.Dir = "\"
ELSE
   Default.Dir = "\" + Default.Dir
END IF

' reset number of /d and /f switches
IF Number.Dest.Dirs = False THEN
   Number.Dest.Dirs = 1
   Destinate.Directory(1) = Default.Dir
END IF
IF Number.Dest.Files = False THEN
   Number.Dest.Files = 1
   Destinate.Filename(1) = NUL
END IF

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

' reset open file flag
Output.File.Open = False

' store time copying began
Start.Time = TIMER

' 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

   ' search standard input
   DO

      ' read character
      InregsX.AX = &H600
      InregsX.DX = &H0FF
      CALL InterruptX(&H21, InregsX, OutregsX)

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

      ' store input
      Redirected.Input = True
      Char$ = CHR$(OutregsX.AX AND &HFF)

      ' check input character
      SELECT CASE ASC(Char$)
      CASE 10, 26
      CASE 13
	 EXIT DO
      CASE ELSE
	 Standard.Input = Standard.Input + Char$
      END SELECT
   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
         Var$ = Inkey$ ' quits here
         CALL SetInt ' reset Control-Break
         IF Var$ = 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 original command line
   Command.Line = Command.Line.Redirect

   ' search filespecs
   File.List = False
   DO

      ' check control break
      IF BreakIS THEN
         EXIT DO
      END IF

      ' process work filename
      IF LEFT$(Command.Line, 1) = "@" THEN
	 ' reset copy type flag
	 File.List = True

	 ' make input filename
	 Command.Line = MID$(Command.Line, 2)
	 CALL MakeFilename

	 ' process work filename
	 Data.Error = False

	 ' restore basic dta
	 GOSUB Restore.Basic.Dta

	 ' open redirected filename list
	 CLOSE #1
	 OPEN Command.Work FOR INPUT SHARED AS #1

	 ' check error flag
	 IF Data.Error THEN
	    IF Display.Errors = False THEN
               IF Wide.List THEN
                  Wide.List = False
                  PRINT
               END IF
               COLOR Red, Black
	       PRINT "Error opening list file " + Command.Work
	    END IF
	 ELSE
	    ' process input filenames
	    DO UNTIL EOF(1)

               ' check control break
               IF BreakIS THEN
                  EXIT DO
               END IF

	       ' read next filename from input file
	       LINE INPUT #1, Command.Work

               ' check input
               IF RTRIM$(Command.Work) <> NUL THEN

                  ' check control break
                  IF BreakIS THEN
                     EXIT DO
                  END IF

                  ' process search filename
                  CALL ProcessCommand(Quit%)

                  ' restore basic dta
                  GOSUB Restore.Basic.Dta

                  ' check overwrite flag
                  IF Quit% THEN
                     Error.Level = 1
                     GOTO End.Copy
                  END IF
               END IF
	    LOOP
	 END IF
      ELSE
	 ' process command line filenames
	 IF File.List THEN
	    IF Command.Line = NUL THEN
	       EXIT DO
	    END IF
	 END IF

         ' check control break
         IF BreakIS THEN
            EXIT DO
         END IF

	 ' make input filename
	 CALL MakeFilename

         ' check control break
         IF BreakIS THEN
            EXIT DO
         END IF

	 ' process search filename
	 CALL ProcessCommand(Quit%)

         ' check control break
         IF BreakIS THEN
            EXIT DO
         END IF

	 ' check overwrite flag
	 IF Quit% THEN
	    Error.Level = 1
	    GOTO End.Copy
	 END IF

	 ' process command line filenames
	 IF Command.Line = NUL THEN
	    EXIT DO
	 END IF
      END IF

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

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

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

' program end label
End.Copy:

' check append files
IF Append.Files THEN
   CALL CloseFile
END IF

' restore basic dta
GOSUB Restore.Basic.Dta

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

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

' calculate time elapsed during file copying
Stop.Time = TIMER
Time.Elapsed = Stop.Time - Start.Time

' check timing overflowed through midnight
IF Time.Elapsed < 0 THEN
   Time.Elapsed = Time.Elapsed + 86400!
END IF

' display end program
IF Continuous.Display = False THEN
   IF Wide.List THEN
      Wide.List = False
      PRINT
   END IF
   Total$ = "Files copied" + STR$(Files.Counted)
   IF Double.Line THEN
      Total$ = Total$ + " Total bytes copied "
      Total$ = Total$ + Format$(Total.Bytes, "#,##0;;0")
   END IF
   COLOR Yellow, Black
   PRINT Total$
   Total$ = "Directories copied" + STR$(Dirs.Counted)
   IF Double.Line THEN
      Total$ = Total$ + " directories deleted" + STR$(Total.Deleted)
   END IF
   PRINT Total$
   IF Rate.Mode THEN
      Total.Bytes = Total.Bytes / Time.Elapsed
      IF Total.Bytes > 0 THEN
         IF Total.Bytes < 1024 THEN
            Total$ = "Transfer rate " + Format$(Total.Bytes,"#,##0.0;;")
            Total$ = Total$ + " B/second."
         ELSE
            Total.Bytes = Total.Bytes / 1024
            Total$ = "Transfer rate " + Format$(Total.Bytes,"#,##0.0;;")
            Total$ = Total$ + " KB/second."
         END IF
         PRINT Total$
      END IF
   END IF
   Prompt$ = "Press <enter> to exit to DOS:"
   CALL MorePrompt(Prompt$, CHR$(13), Outpt$, CHR$(13))
END IF
COLOR Plain, Black

' restore key trapping
CALL RestInt

' return error level to dos
SELECT CASE Error.Level
CASE 1
   END 8
CASE -1
   END 4
CASE 0
   IF Files.Counted = 0# THEN
      END 2
   ELSE
      END 0
   END IF
END SELECT
END 2

Restore.Basic.Dta:
 InregsX.AX = &H1A00
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' make header
Header:
 IF Header.Flag THEN
    RETURN
 END IF
 Header.Flag = True
 IF Continuous.Display = False THEN
    COLOR White, Black
    PRINT "Copyit "+Version$+": File copy utility; "
 END IF
 RETURN

' critical error trap
Error.Routine:
 ' clear dots
 FOR Count = 1 TO Dot.Count
    PRINT CHR$(29);" ";CHR$(29);
 NEXT
 Dot.Count = False
 ' clear percent display
 IF Percent.Display THEN
    IF Percent.Flag THEN
       FOR Imbedded = 1 TO 4
          PRINT CHR$(29);" ";CHR$(29);
       NEXT
    END IF
 END IF
 ' clear progress bar
 IF Progress.Bar THEN
    FOR Imbedded = 1 TO Current.Progress
       PRINT CHR$(29);" ";CHR$(29);
    NEXT
 END IF
 ' process error
 Data.Error = ERR
 IF Display.Errors THEN
    Error.Level = True
    OutregsX.Flags = &H1
    RESUME NEXT
 END IF
 ' prompt error
 IF Wide.List THEN
    Wide.List = False
    PRINT
 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$, "c")
 IF BreakIS THEN
    Outpt$ = "q"
 END IF
 SELECT CASE Outpt$
 CASE "r"
    RESUME
 CASE "q"
    Error.Level = True
    RESUME End.Copy
 CASE "c"
    OutregsX.Flags = &H1
    RESUME NEXT
 END SELECT
 COLOR Plain, Black
 ' restore key trapping
 CALL RestInt
 END 0

' routine to copy a filename
SUB CopyFiles(Source.Directory$, Source.Filename$)
 ' declare subroutine variables
 DIM Attribute AS INTEGER
 DIM Handle AS INTEGER
 DIM Number.Bytes AS INTEGER
 DIM Sum.Bytes AS DOUBLE
 DIM DTAfile AS DTAtype

 ' restore dta
 GOSUB Copy.DTA

 ' make filename
 ASCIZ.Copy = Source.Directory$ + Source.Filename$ + CHR$(0)

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

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

 ' store file attribute
 Attribute = OutregsX.CX
 Source.Attribute = Attribute

 ' check file attribute
 IF Display.Attribute THEN

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

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

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

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

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

 ' reset destination file attribute
 IF Set.Dest.Readonly THEN
    Attribute = Attribute OR 1
 END IF
 IF Set.Dest.Hidden THEN
    Attribute = Attribute OR 2
 END IF
 IF Set.Dest.System THEN
    Attribute = Attribute OR 4
 END IF
 IF Set.Dest.Archive THEN
    Attribute = Attribute OR &H20
 END IF
 IF Set.Dest.Any THEN
    Attribute = Attribute OR &H27
 END IF
 IF Clear.Dest.Readonly THEN
    Attribute = Attribute AND NOT 1
 END IF
 IF Clear.Dest.Hidden THEN
    Attribute = Attribute AND NOT 2
 END IF
 IF Clear.Dest.System THEN
    Attribute = Attribute AND NOT 4
 END IF
 IF Clear.Dest.Archive THEN
    Attribute = Attribute AND NOT &H20
 END IF
 IF Clear.Dest.Any THEN
    Attribute = Attribute AND NOT &H27
 END IF

 ' reset source file attribute
 IF Set.Source.Readonly THEN
    Source.Attribute = Source.Attribute OR 1
 END IF
 IF Set.Source.Hidden THEN
    Source.Attribute = Source.Attribute OR 2
 END IF
 IF Set.Source.System THEN
    Source.Attribute = Source.Attribute OR 4
 END IF
 IF Set.Source.Archive THEN
    Source.Attribute = Source.Attribute OR &H20
 END IF
 IF Set.Source.Any THEN
    Source.Attribute = Source.Attribute OR &H27
 END IF
 IF Clear.Source.Readonly THEN
    Source.Attribute = Source.Attribute AND NOT 1
 END IF
 IF Clear.Source.Hidden THEN
    Source.Attribute = Source.Attribute AND NOT 2
 END IF
 IF Clear.Source.System THEN
    Source.Attribute = Source.Attribute AND NOT 4
 END IF
 IF Clear.Source.Archive THEN
    Source.Attribute = Source.Attribute AND NOT &H20
 END IF
 IF Clear.Source.Any THEN
    Source.Attribute = Source.Attribute AND NOT &H27
 END IF

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

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

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

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

 YearTemp! = INT(File.Work.Date / 512)
 MonthTemp! = INT((File.Work.Date AND &H1E0) / 32)
 DayTemp! = INT(File.Work.Date AND &H1F)
 YearTemp! = YearTemp! + 1980

 File.Date$ = RIGHT$(STR$(MonthTemp! + 100), 2) + "-"
 File.Date$ = File.Date$ + RIGHT$(STR$(DayTemp! + 100), 2) + "-"
 File.Date$ = File.Date$ + MID$(STR$(YearTmp!), 2)

 File.Time$ = RIGHT$(STR$(HourTemp! + 100), 2) + ":"
 File.Time$ = File.Time$ + RIGHT$(STR$(MinuteTemp! + 100), 2) + ":"
 File.Time$ = File.Time$ + RIGHT$(STR$(SecondsTemp! + 100), 2)

 ' display file copying
 Copy.File$ = Source.Directory$ + Source.Filename$
 IF Append.Files THEN
    Copy.Dest$ = Dest.Dir
 ELSE
    IF Copy.Directory = False THEN
       Copy.Dest$ = Dest.Dir + Source.Filename$
    ELSE
       Copy.Dest$ = Copy.Target + Source.Filename$
    END IF
    Write.ASCIZ = Copy.Dest$ + CHR$(0)
 END IF

 ' check skip files
 FOR Skip.File = 1 TO 10
    Filename$ = Skip.Filenames(Skip.File)
    IF RTRIM$(UCASE$(Filename$)) = RTRIM$(UCASE$(Source.Filename$)) THEN
       EXIT SUB
    END IF
 NEXT

 ' check excluded files
 FOR File.Number = 1 TO Number.Excluded
    Filename1$ = RTRIM$(Excluded.Files(File.Number))
    Filename2$ = RTRIM$(UCASE$(Source.Filename$))
    CALL CheckExcluded(Filename1$, Filename2$, Exclude.File%)
    IF Exclude.File% THEN
       EXIT SUB
    END IF
 NEXT

 ' check if output file open already
 IF Output.File.Open = False THEN

    ' check if appending files
    IF Append.Files THEN

       ' store output file info
       ASCIZfile = Dest.Dir + CHR$(0)

       ' check if files similar
       CALL ReadFileSynch

       ' restore current dta
       GOSUB Copy.DTA

       ' check files
       CALL CheckSynched(Synched%)
       IF Synched% THEN
          EXIT SUB
       END IF
    END IF
 END IF

 ' check append files
 IF Append.Files = False THEN

    ' store output file info
    ASCIZfile = Write.ASCIZ

    ' check if files similar
    CALL ReadFileSynch

    ' restore current dta
    GOSUB Copy.DTA

    ' check files
    CALL CheckSynched(Synched%)
    IF Synched% THEN
       EXIT SUB
    END IF
 END IF

 ' check if file copying onto itself
 CALL FilenameCopying(Copy.File$, Copy.Dest$)

 ' check copying flag
 IF Filename.Exists THEN
    EXIT SUB
 END IF

 ' check windows dos
 IF Windows.Detected THEN
    CALL ExtendedFile(0) ' read extended filename date\time
 END IF

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

 ' store file handle
 Handle = OutregsX.AX

 ' display any error
 IF Debug.Mode THEN
    IF OutregsX.AX > 0 THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H opening input filename.")
    END IF
 END IF

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

 ' test read from file
 InregsX.AX = &H3F00
 InregsX.BX = Handle
 InregsX.CX = &H0001
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' display any error
 IF Debug.Mode THEN
    IF OutregsX.AX > 0 THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading input filename.")
    END IF
 END IF

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

 ' reset pointer to start of file
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = &H0
 InregsX.DX = &H0
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check windows dos
 IF Windows.Detected = False THEN
    ' get date\time
    InregsX.AX = &H5700
    InregsX.BX = Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
    Source.File.Time = OutregsX.CX
    Source.File.Date = OutregsX.DX
 END IF

 ' reset delete file flag
 Copy.Error = False

 ' check if output file open already
 IF Output.File.Open = False THEN

    ' reset open file flag
    Output.File.Open = True

    ' check if appending files
    IF Append.Files THEN

       ' store output file info
       ASCIZfile = Dest.Dir + CHR$(0)
       File.Attribute = Attribute

       ' check to overwrite
       IF Overwrite.Prompt = False THEN
          Copy.File$ = Directory.Search$ + RTRIM$(Filename.Search)
	  Copy.Dest$ = Dest.Dir
	  CALL PromptOverwrite(Copy.File$, Copy.Dest$, Response%)
	  IF Response% = False THEN
             GOTO End.Copy.File
	  END IF
       END IF

       ' open output file
       CALL OpenFile(OpenError%)

       ' restore current dta
       GOSUB Copy.DTA

       ' check error opening output file
       IF OpenError% THEN
	  EXIT SUB
       END IF
    END IF
 END IF

 ' check append files
 IF Append.Files = False THEN

    ' store output file info
    ASCIZfile = Write.ASCIZ
    File.Attribute = Attribute

    ' check to overwrite
    IF Overwrite.Prompt = False THEN
       CALL PromptOverwrite(Copy.File$, Copy.Dest$, Response%)
       IF Response% = False THEN
	  GOTO End.Copy.File
       END IF
    END IF

    ' open output file
    CALL OpenFile(OpenError%)

    ' restore current dta
    GOSUB Copy.DTA

    ' check error opening output file
    IF OpenError% THEN
       GOTO End.Copy.File
    END IF
 END IF

 ' reset error flag
 Error.Type = False
 Percent.Flag = False
 Current.Progress = False

 ' copy file
 Count.Forward = True
 Dot.Count = False

 ' file input loop
 DO

    ' read from file
    InregsX.AX = &H3F00
    InregsX.BX = Handle
    InregsX.CX = &H1000
    InregsX.DS = VARSEG(Buffer)
    InregsX.DX = VARPTR(Buffer)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' show disk activity
    IF Continuous.Display = False AND Dot.Mode = False THEN
       IF Count.Forward THEN
          PRINT ".";
          Dot.Count = Dot.Count + 1
          IF Dot.Count = 8 THEN
             Count.Forward = False
          END IF
       ELSE
          PRINT CHR$(29);" ";CHR$(29);
          Dot.Count = Dot.Count - 1
          IF Dot.Count = False THEN
             Count.Forward = True
          END IF
       END IF
    END IF

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

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

    ' check binary copy
    IF Copy.Ascii = False THEN
       Ascii.Exit = False
    ELSE
       Imbedded = INSTR(Buffer, CHR$(26))
       IF Imbedded THEN
	  Number.Bytes = Imbedded + 1
	  Ascii.Exit = True
       END IF
    END IF

    ' check ascii filters
    FOR Count = 1 TO Ascii.Filters
       Ascii.From = Convert.Ascii(Count, 1)
       Ascii.To = Convert.Ascii(Count, 2)
       Next.Imbedded = 1
       DO
	  Imbedded = INSTR(Next.Imbedded, Buffer, CHR$(Ascii.From))
	  IF Imbedded THEN
	     Next.Imbedded = Imbedded + 1
	     MID$(Buffer, Imbedded, 1) = CHR$(Ascii.To)
	  ELSE
	     EXIT DO
	  END IF
	  IF Next.Imbedded >= Number.Bytes THEN
	     EXIT DO
	  END IF
       LOOP
    NEXT

    ' check ascii strips
    FOR Count = 1 TO Ascii.Strips
       DO
	  Imbedded = INSTR(Buffer, CHR$(Strip.Ascii(Count)))
	  IF Imbedded > False AND Imbedded <= Number.Bytes THEN
	     Buffer = LEFT$(Buffer, Imbedded - 1) + MID$(Buffer, Imbedded + 1)
	     Number.Bytes = Number.Bytes - 1
	  ELSE
	     EXIT DO
	  END IF
       LOOP
    NEXT

    ' check number of bytes to copy
    IF Number.Bytes <= False THEN
       EXIT DO
    END IF

    ' append to file
    InregsX.AX = &H4000
    InregsX.BX = Append.Handle
    InregsX.CX = Number.Bytes
    InregsX.DS = VARSEG(Buffer)
    InregsX.DX = VARPTR(Buffer)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' increment bytes copied
    Sum.Bytes = Sum.Bytes + Number.Bytes

    ' check binary copy
    IF Ascii.Exit THEN
       EXIT DO
    END IF

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

    ' check number of bytes actually written
    IF OutregsX.AX < Number.Bytes THEN
       Error.Type = 3
       OutregsX.Flags = &H1
       EXIT DO
    END IF

    ' check to display percent copied
    IF Percent.Display THEN
       Percent = (Sum.Bytes / File.Size) * 100!
       Percent = INT(Percent)
       IF Percent.Flag THEN
          FOR Imbedded = 1 TO 4
             PRINT CHR$(29);" ";CHR$(29);
          NEXT
       END IF
       Percent.Flag = True
       PRINT RIGHT$(STR$(Percent + 1000!), 3) + "%";
    END IF

    ' check to display progress bar
    IF Progress.Bar THEN
       Percent = (Sum.Bytes / File.Size) * 100!
       Percent = INT(Percent / 10)
       DO WHILE Percent > Current.Progress
          PRINT "#";
          Current.Progress = Current.Progress + 1
       LOOP
    END IF

    IF BreakIS THEN
       Error.Type = 4
       OutregsX.Flags = &H1
       EXIT DO
    END IF
 LOOP

 ' clear percent display
 IF Percent.Display THEN
    IF Percent.Flag THEN
       FOR Imbedded = 1 TO 4
          PRINT CHR$(29);" ";CHR$(29);
       NEXT
    END IF
 END IF

 ' clear progress bar
 IF Progress.Bar THEN
    FOR Imbedded = 1 TO Current.Progress
       PRINT CHR$(29);" ";CHR$(29);
    NEXT
 END IF

 ' clear dots
 FOR Count = 1 TO Dot.Count
    PRINT CHR$(29);" ";CHR$(29);
 NEXT
 Dot.Count = False
 Count.Forward = True

 ' check copying file error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' update counter
    Files.Counted = Files.Counted + 1#
 END IF

 ' check copying file error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    IF Wide.List THEN
       Wide.List = False
       PRINT
    END IF
    Copy.Error = True
    IF Display.Errors = False THEN
       COLOR Red, Black
       SELECT CASE Error.Type
       CASE False
	  PRINT "Error copying files."
       CASE 1
	  PRINT "Error reading from input file."
       CASE 2
	  PRINT "Error writing to output file."
       CASE 3
	  PRINT "Disk full error writing to output file."
          Disk.Full = True
	  Error.Level = True
	  Quit.Searching = True
       CASE 4
          Copy.Error = False
          Quit.Searching = True
       CASE ELSE
	  PRINT "Error copying files."
       END SELECT
    END IF
 END IF

 ' close output file
 IF Append.Files = False THEN
    CALL CloseFile
 END IF

End.Copy.File:
 ' close input file
 InregsX.AX = &H3E00
 InregsX.BX = Handle
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' display any errors
 IF Debug.Mode THEN
    IF OutregsX.AX > 0 THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H closing input filename.")
    END IF
 END IF

 ' check disk full flag
 IF Disk.Full THEN
    ' delete destination file
    CALL DeleteDestFile(False, Source.Filename$)
    EXIT SUB
 END IF

 ' check copy error flag
 IF Copy.Error THEN
    ' delete destination file
    CALL DeleteDestFile(True, Source.Filename$)
    EXIT SUB
 END IF

 ' check to change source attribute
 IF Set.Source.Attribute THEN
    ' check windows dos
    IF Windows.Detected THEN
       ' reset source attribute
       InregsX.AX = &H7143
       InregsX.BX = &H1
       InregsX.CX = Source.Attribute
       InregsX.DS = VARSEG(ASCIZ.Copy)
       InregsX.DX = VARPTR(ASCIZ.Copy)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' reset source attribute
       InregsX.AX = &H4301
       InregsX.CX = Source.Attribute
       InregsX.DS = VARSEG(ASCIZ.Copy)
       InregsX.DX = VARPTR(ASCIZ.Copy)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF

 ' increment bytes copied
 Total.Bytes = Total.Bytes + File.Size

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

    ' display file size
    COLOR Red, Black
    Total$ = Format$(File.Size, "#,##0;;0")
    Total$ = Total$ + SPACE$(20 - LEN(Total$))
    PRINT Total$;

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

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

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

    ' check for system file
    IF (Attribute AND 4) = 4 THEN
       PRINT " System";
    END IF
    Wide.List = False
    PRINT
 END IF

 ' check to delete source file
 IF Delete.Copied THEN
    CALL DeleteSourceFile(Source.Directory$, Source.Filename$)
 END IF
 EXIT SUB

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

' routine to access directories in windows
SUB WinDirectories(Directory.Search$, Directory.Target$)
 ' declare subroutine variables
 DIM Wfile.Handle AS INTEGER
 DIM Attribute AS INTEGER

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

 ' 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(CopyWDTA)
 InregsX.DI = VARPTR(CopyWDTA)
 CALL InterruptX(&H21, InregsX, OutregsX)
 Wfile.Handle = OutregsX.AX

 ' check findirst error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' store source copied directory filename
    Copy.Target = Directory.Target$

    ' check to copy directory structure
    Directory.Exists = False
    IF Copy.Directory THEN
       Dir.Copy.Name = Directory.Search$
       Dir.Copy.Target = Directory.Target$
       CALL CopyDirectory
    END IF

    ' check directory exists
    IF Directory.Exists = False THEN

       ' search directory names
       CALL Filenames(Directory.Search$)

       ' check to recurse directories
       IF Recurse.Directories THEN

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

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

             ' check directory attribute
             Attribute = ASC(CopyWDTA.Fileattr)
             IF (Attribute AND &H10) = &H10 THEN
                ' store directory name
                Next.Search = CopyWDTA.ASCIZfull
                Next.Search = LEFT$(Next.Search, INSTR(Next.Search, CHR$(0)) - 1)
                ' check directory name
                IF Next.Search <> "." AND Next.Search <> ".." THEN
                   ' check recursion levels
                   Recursion = True
                   IF Nested.Recurse > False THEN
                      Nested.Levels = Nested.Levels + 1
                      IF Nested.Levels >= Nested.Recurse THEN
                         Recursion = False
                      END IF
                   END IF

                   ' recursively search subdirectories
                   IF Recursion THEN
                      CALL WinDirectories(Directory.Search$ + Next.Search + "\", _
                      Directory.Target$ + Next.Search + "\")
                   END IF
                   IF Nested.Recurse > False THEN
                      Nested.Levels = Nested.Levels - 1
                   END IF
                END IF
             END IF

             ' find next long filename
             InregsX.AX = &H714F
             InregsX.BX = Wfile.Handle
             InregsX.SI = &H1
             InregsX.ES = VARSEG(CopyWDTA)
             InregsX.DI = VARPTR(CopyWDTA)
             CALL InterruptX(&H21, InregsX, OutregsX)

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

 ' check to delete subdirectory
 IF Recurse.Error = False THEN
    IF Delete.Directory THEN
       CALL DeleteSubdirectory(Directory.Search$)
    END IF
 END IF
END SUB

' routine to access directories in dos
SUB DosDirectories(Directory.Search$, Directory.Target$)
 ' declare subroutine variables
 DIM Wfile.Handle AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Attribute AS INTEGER

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

 ' find first directory
 InregsX.AX = &H4E00
 InregsX.CX = &H37
 InregsX.DS = VARSEG(ASCIZ.Sub)
 InregsX.DX = VARPTR(ASCIZ.Sub)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check findirst error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' store source copied directory filename
    Copy.Target = Directory.Target$

    ' check to copy directory structure
    Directory.Exists = False
    IF Copy.Directory THEN
       Dir.Copy.Name = Directory.Search$
       Dir.Copy.Target = Directory.Target$
       CALL CopyDirectory
       GOSUB Restore.DTA
    END IF

    ' check directory exists
    IF Directory.Exists = False THEN

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

       ' check to recurse directories
       IF Recurse.Directories THEN
          ' recursive subdirectories
          DO
             ' check control-break
             IF BreakIS THEN
                Quit.Searching = True
             END IF

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

             ' check directory attribute
             Attribute = ASC(DTAfile.Fileattr)
             IF (Attribute AND &H10) = &H10 THEN
                ' store directory name
                Next.Search = DTAfile.ASCIZfilename
                Next.Search = LEFT$(Next.Search, INSTR(Next.Search, CHR$(0)) - 1)

                ' check directory name
                IF Next.Search <> "." AND Next.Search <> ".." THEN
                   ' check recursion levels
                   Recursion = True
                   IF Nested.Recurse > False THEN
                      Nested.Levels = Nested.Levels + 1
                      IF Nested.Levels >= Nested.Recurse THEN
                         Recursion = False
                      END IF
                   END IF

                   ' recursively search subdirectories
                   IF Recursion THEN
                      CALL DosDirectories(Directory.Search$ + Next.Search + "\", _
                      Directory.Target$ + Next.Search + "\")
                      GOSUB Restore.DTA
                   END IF
                   IF Nested.Recurse > False THEN
                      Nested.Levels = Nested.Levels - 1
                   END IF
                END IF
             END IF

             ' find next directory
             InregsX.AX = &H4F00
             CALL InterruptX(&H21, InregsX, OutregsX)

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

 ' check to delete subdirectory
 IF Recurse.Error = False THEN
    IF Delete.Directory THEN
       CALL DeleteSubdirectory(Directory.Search$)
    END IF
 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

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

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

 ' restore dta
 GOSUB File.DTA

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

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

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

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

       ' display filename
       IF Windows.Detected = False THEN
          TempDTA = DTAfile
       END IF
       CALL CopyFiles(Directory.Search$, Filename$)
    END IF

    ' restore filenames dta
    GOSUB File.DTA

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

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

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

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

' routine to open output file
SUB OpenFile(OpenError%)
 ' reset file open error flags
 Disk.Full = False
 OpenError% = False

 ' reset file create flag
 File.Create = False

 ' check to append to destination
 IF Append.Dest THEN

    ' check windows dos
    IF Windows.Detected THEN
       ' open file for append
       InregsX.AX = &H716C
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0011
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       InregsX.DI = &H1
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' open file for append
       InregsX.AX = &H6C00
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0011
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' check file append error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       File.Create = True
    END IF
 END IF

 ' check file create error flag
 IF File.Create = False THEN

    ' check windows dos
    IF Windows.Detected THEN
       ' create/truncate file
       InregsX.AX = &H716C
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0012
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       InregsX.DI = &H1
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create/truncate file
       InregsX.AX = &H6C00
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0012
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' check file append error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       File.Create = True
    END IF
 END IF

 ' check file create error flag
 IF File.Create = False THEN

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

    ' check windows dos
    IF Windows.Detected THEN
       ' create/truncate file
       InregsX.AX = &H716C
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0012
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       InregsX.DI = &H1
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create/truncate file
       InregsX.AX = &H6C00
       InregsX.BX = &H0041
       InregsX.CX = File.Attribute
       InregsX.DX = &H0012
       InregsX.DS = VARSEG(ASCIZfile)
       InregsX.SI = VARPTR(ASCIZfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF

 ' store file handle
 Append.Handle = OutregsX.AX

 ' display any error
 IF Debug.Mode THEN
    IF OutregsX.AX > 0 THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H opening output filename.")
    END IF
 END IF

 ' check open file error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    OpenError% = True
    EXIT SUB
 END IF

 ' check to append to destination
 IF Append.Dest THEN
    ' reset pointer to eof
    InregsX.AX = &H4202
    InregsX.BX = Append.Handle
    InregsX.CX = &H0
    InregsX.DX = &H0
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' display any error
    IF Debug.Mode THEN
       IF OutregsX.AX > 0 THEN
          CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H appending output filename.")
       END IF
    END IF

    ' check open file error
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       OpenError% = True
    END IF
 END IF
END SUB

' routine to close output file
SUB CloseFile
 ' check output file
 IF Output.File.Open = False THEN
    EXIT SUB
 END IF

 ' check windows dos
 IF Windows.Detected = False THEN
    CALL ExtendedFile(-2) ' set filename date\time
 END IF

 ' close file
 InregsX.AX = &H3E00
 InregsX.BX = Append.Handle
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' display any errors
 IF Debug.Mode THEN
    IF OutregsX.AX > 0 THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H closing output filename.")
    END IF
 END IF

 ' check error flag
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' check windows dos
    IF Windows.Detected THEN
       CALL ExtendedFile(-1) ' set extended filename date\time
    END IF
 END IF
END SUB

' routine to read output file info
SUB ReadFileSynch
 ' declare subroutine variables
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' restore readfile 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 = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIZfile)
    InregsX.DX = VARPTR(ASCIZfile)
    InregsX.ES = VARSEG(SDTAfile)
    InregsX.DI = VARPTR(SDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    ' find first filename
    InregsX.AX = &H4E00
    InregsX.CX = &H27
    InregsX.DS = VARSEG(ASCIZfile)
    InregsX.DX = VARPTR(ASCIZfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store file size
 IF Windows.Detected THEN
    Dest.File.Size = False
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeHigh, 4, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeHigh, 3, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeHigh, 2, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeHigh, 1, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeLow, 4, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeLow, 3, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeLow, 2, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(SDTAfile.FileSizeLow, 1, 1))
 ELSE
    Dest.File.Size = False
    Dest.File.Size = Dest.File.Size + ASC(MID$(DTAfile.FileSize, 4, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
    Dest.File.Size = Dest.File.Size * &H100 + ASC(MID$(DTAfile.FileSize, 1, 1))
 END IF

 ' store file date and time
 IF Windows.Detected THEN
    IF Create.Time THEN
       Dest.File.Time = ASC(MID$(SDTAfile.CreateTime, 2, 1))
       Dest.File.Time = Dest.File.Time * &H100 + ASC(MID$(SDTAfile.CreateTime, 1, 1))
       Dest.File.Date = ASC(MID$(SDTAfile.CreateTime, 4, 1))
       Dest.File.Date = Dest.File.Date * &H100 + ASC(MID$(SDTAfile.CreateTime, 3, 1))
    ELSE
       IF Access.Time THEN
          Dest.File.Time = ASC(MID$(SDTAfile.AccessTime, 2, 1))
          Dest.File.Time = Dest.File.Time * &H100 + ASC(MID$(SDTAfile.AccessTime, 1, 1))
          Dest.File.Date = ASC(MID$(SDTAfile.AccessTime, 4, 1))
          Dest.File.Date = Dest.File.Date * &H100 + ASC(MID$(SDTAfile.AccessTime, 3, 1))
       ELSE
          IF Modify.Time THEN
             Dest.File.Time = ASC(MID$(SDTAfile.ModTime, 2, 1))
             Dest.File.Time = Dest.File.Time * &H100 + ASC(MID$(SDTAfile.ModTime, 1, 1))
             Dest.File.Date = ASC(MID$(SDTAfile.ModTime, 4, 1))
             Dest.File.Date = Dest.File.Date * &H100 + ASC(MID$(SDTAfile.ModTime, 3, 1))
          END IF
       END IF
    END IF
 ELSE
    Dest.File.Time = ASC(MID$(DTAfile.FileTime, 2, 1))
    Dest.File.Time = Dest.File.Time * &H100 + ASC(MID$(DTAfile.FileTime, 1, 1))
    Dest.File.Date = ASC(MID$(DTAfile.FileDate, 2, 1))
    Dest.File.Date = Dest.File.Date * &H100 + ASC(MID$(DTAfile.FileDate, 1, 1))
 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
END SUB

' routine to preprocess filenames in stdin/command line list
SUB ProcessCommand(Quit%)
 ' declare work variables
 DIM Attribute AS INTEGER
 DIM Current.DTA.SEG AS INTEGER
 DIM Current.DTA.OFF AS INTEGER
 DIM ProcessDTA AS DTAtype
 DIM Wfile.Handle AS INTEGER

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

 ' restore prompt dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(ProcessDTA)
 InregsX.DX = VARPTR(ProcessDTA)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' reset quit flag
 Quit% = False

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

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

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

 ' store current directory
 Directory.Search$ = NUL
 Temp.Search$ = LEFT$(ASCIZ.Command, INSTR(ASCIZ.Command, CHR$(0)) - 1)
 IF LEN(Command.Work) THEN
    Imbedded = INSTR(Command.Work, "\")
    Imbedded.Char = Imbedded
    WHILE Imbedded
       Imbedded.Char = Imbedded
       Imbedded = INSTR(Imbedded + 1, Command.Work, "\")
    WEND
    IF Imbedded.Char THEN
       Directory.Search$ = LEFT$(Command.Work, Imbedded.Char)
       Command.Work = MID$(Command.Work, Imbedded.Char + 1)
    END IF
 END IF
 IF Directory.Search$ = NUL THEN
    IF Temp.Search$ = "\" THEN
       Directory.Search$ = "\"
    ELSE
       Directory.Search$ = "\" + Temp.Search$
    END IF
 END IF
 IF RIGHT$(Directory.Search$, 1) <> "\" THEN
    Directory.Search$ = Directory.Search$ + "\"
 END IF
   
 ' get filename spec
 Filename.Search = Command.Work
 IF RTRIM$(Filename.Search) = NUL THEN
    Filename.Search = "*.*"
 END IF
 Command.Work = NUL
   
 ' change to drive
 InregsX.AX = &HE00
 InregsX.DX = ASC(UCASE$(Drive.Search)) - 65
 CALL InterruptX(&H21, InregsX, OutregsX)

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

 ' check search filename
 Target.Dir$ = NUL
 Imbedded1 = INSTR(Filename.Search, "?")
 Imbedded2 = INSTR(Filename.Search, "*")
 IF Imbedded1 = False AND Imbedded2 = False THEN

    ' check search directory
    ASCIZ.Command = Directory.Search$ + RTRIM$(Filename.Search) + CHR$(0)

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

    ' check specified directory/filename
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' check source is directory or filename
       IF Windows.Detected THEN
          Attribute = ASC(ProWDTA.Fileattr)
       ELSE
          Attribute = ASC(ProcessDTA.Fileattr)
       END IF
       IF (Attribute AND &H10) = &H10 THEN
          Directory.Search$ = Directory.Search$ + RTRIM$(Filename.Search) + "\"
          Target.Dir$ = Rtrim$(Filename.Search)
          Filename.Search = "*.*"
       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
 END IF

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

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

 ' process destination directories
 FOR Count1 = 1 TO Number.Dest.Dirs
    ' store directory
    Next.Dest.Dir$ = Destinate.Directory(Count1)
    Next.Dest.Dir$ = RTRIM$(Next.Dest.Dir$)

    ' check destination drive
    IF MID$(Next.Dest.Dir$, 2, 1) = ":" THEN
       Drive.Number = ASC(UCASE$(MID$(Next.Dest.Dir$, 1, 1)))
       Next.Dest.Dir$ = MID$(Next.Dest.Dir$, 3)
    ELSE
       Drive.Number = ASC(UCASE$(Current.Drive))
    END IF

    ' store destination directory
    GOSUB Store.Dest.Dir

    ' recheck destination directory
    IF LEFT$(Next.Dest.Dir$, 1) <> "\" THEN
       Next.Dest.Dir$ = Default.Dest + Next.Dest.Dir$
    END IF   
    IF RIGHT$(Next.Dest.Dir$, 1) <> "\" THEN
       Next.Dest.Dir$ = Next.Dest.Dir$ + "\"
    END IF

    ' store destination drive
    Next.Dest.Dir$ = CHR$(Drive.Number) + ":" + Next.Dest.Dir$

    ' process destination filenames
    FOR Count2 = 1 TO Number.Dest.Files

       ' store filename
       Dest.File = Destinate.Filename(Count2)
       Dest.File = RTRIM$(Dest.File)

       ' reset work variable
       Append.Files = False

       ' check destination filename
       Dest.Dir = Next.Dest.Dir$
       IF Dest.File <> NUL THEN
          Dest.Dir = Dest.Dir + Dest.File
          Append.Files = True
       END IF

       ' store target directory for directory structure copy
       Directory.Target$ = Dest.Dir
       IF Zero.Nest = False THEN
          IF LEN(Target.Dir$) THEN
             Directory.Target$ = Directory.Target$ + Target.Dir$ + "\"
          END IF
       END IF

       ' construct target directory
       IF Copy.Directory THEN
          CALL CreateDirectory(Directory.Target$)
       END IF

       ' call routine to search for files
       Recurse.Error = False

       ' check windows recursive copying
       IF Windows.Detected THEN
          CALL WinDirectories(Directory.Search$, Directory.Target$)
       ELSE
          CALL DosDirectories(Directory.Search$, Directory.Target$)
       END IF

       ' close output file
       IF Append.Files THEN
          CALL CloseFile
          Output.File.Open = False
       END IF

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

       ' check quit flag
       IF Quit.Searching THEN
          Quit% = True
          EXIT FOR
       END IF
    NEXT

    ' restore destination directory
    GOSUB Restore.Dest.Dir

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

    ' check quit flag
    IF Quit.Searching THEN
       EXIT FOR
    END IF
 NEXT
 EXIT SUB

Store.Dest.Dir:
 ' change to drive
 InregsX.AX = &HE00
 InregsX.DX = Drive.Number - 65
 CALL InterruptX(&H21, InregsX, OutregsX)

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

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

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

 ' store directory
 Imbedded = INSTR(DefaultASCIZ, CHR$(0))
 Default.Dest = LEFT$(DefaultASCIZ, Imbedded - 1)
 IF Default.Dest = NUL THEN
    Default.Dest = "\"
 END IF
 IF Default.Dest <> "\" THEN
    Default.Dest = "\" + Default.Dest
 END IF
 IF RIGHT$(Default.Dest, 1) <> "\" THEN
    Default.Dest = Default.Dest + "\"
 END IF

 ' change to drive
 InregsX.AX = &HE00
 InregsX.DX = ASC(UCASE$(Drive.Search)) - 65
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

Restore.Dest.Dir:
 ' store drive number
 Drive.Number = ASC(UCASE$(LEFT$(Dest.Dir, 1)))

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

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

 ' store directory
 IF LEFT$(DefaultASCIZ, 1) <> "\" THEN
    DefaultASCIZ = "\" + DefaultASCIZ
 END IF

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

 ' check default directory
 IF (OutregsX.Flags AND &H1) = &H1 THEN

    ' reset directory
    DefaultASCIZ = "\" + CHR$(0)

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

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

 ' change to drive
 InregsX.AX = &HE00
 InregsX.DX = ASC(UCASE$(Drive.Search)) - 65
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN
END SUB

' routine to prompt before overwriting files
SUB PromptOverwrite(Source.File$, Destination.File$, Response%)
 ' declare work variables
 DIM PromptASCIZ AS STRING * 260
 DIM Current.DTA.SEG AS INTEGER
 DIM Current.DTA.OFF AS INTEGER
 DIM PromptDTA AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' reset continue searching flag
 Response% = True

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

 ' restore prompt dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(PromptDTA)
 InregsX.DX = VARPTR(PromptDTA)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' make filename
 PromptASCIZ = Destination.File$ + CHR$(0)

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

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

 ' check find first error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' make drive
    Start.Dir = Dest.Dir
    IF MID$(Start.Dir, 2, 1) = ":" THEN
       Drive.Number = ASC(UCASE$(LEFT$(Start.Dir, 1)))
       Start.Dir = MID$(Start.Dir, 3)
    ELSE
       Drive.Number = ASC(UCASE$(Drive.Search))
    END IF

    ' make directory
    To.File$ = Destination.File$
    IF MID$(To.File$, 2, 1) = ":" THEN
       To.File$ = MID$(To.File$, 3)
    END IF

    ' make directory
    From.File$ = Source.File$
    IF MID$(From.File$, 2, 1) = ":" THEN
       From.File$ = MID$(From.File$, 3)
    END IF

    ' check overwrite flag
    IF Dont.Overwrite THEN
       ' display file
       Response% = False

       ' make filename
       Outpt$ = Drive.Search + ":" + RTRIM$(From.File$)
       Outpt$ = UCASE$(Outpt$)
       IF Display.Lowercase THEN
	  Outpt$ = LCASE$(Outpt$)
       END IF

       ' check continuous display
       IF Continuous.Display = False THEN
	  Outpt$ = "Not copying file " + Outpt$
       END IF

       ' display filename
       IF Wide.List THEN
          Wide.List = False
          PRINT
       END IF
       COLOR White, Black
       PRINT Outpt$
    ELSE
       ' check continuous copying
       IF Continue.Searching = False THEN

	  ' make filename
	  Prompt$ = CHR$(Drive.Number) + ":" + RTRIM$(To.File$)
	  Prompt$ = UCASE$(Prompt$)
	  IF Display.Lowercase THEN
	     Prompt$ = LCASE$(Prompt$)
	  END IF

	  ' make prompt
	  Prompt$ = "File " + Prompt$ + " exists. "
	  IF Append.Dest THEN
	     Prompt$ = Prompt$ + "Append"
	  ELSE
	     Prompt$ = Prompt$ + "Overwrite"
	  END IF
	  Prompt$ = Prompt$ + "(y/n/q/c)?"
          CALL MorePrompt(Prompt$, "ynqc", Outpt$, "y")
          IF BreakIS THEN
             Outpt$ = "q"
          END IF
	  SELECT CASE Outpt$
	  CASE "c"
	     Response% = True
	     Continue.Searching = True
	  CASE "q"
	     Response% = False
	     Quit.Searching = True
	  CASE "y"
	     Response% = True
	  CASE "n"
	     Response% = False
	  END SELECT
       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

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

' routine to create/verify directory
SUB CopyDirectory
 ' check for copying directory onto itself
 CALL DirectoryCopying
 IF Directory.Exists THEN
    Recurse.Error = True
    IF Display.Errors = False THEN
       IF Wide.List THEN
          Wide.List = False
          PRINT
       END IF
       COLOR White, Black
       IF Directory.Exists = -1 THEN
          PRINT "Directory cannot be copied onto itself."
       ELSE
          IF Directory.Exists = -2 THEN
             PRINT "Directory cannot be recursively copied onto itself."
          END IF
       END IF
    END IF
    EXIT SUB
 END IF

 ' make directory
 Filename$ = RTRIM$(Dir.Copy.Target)
 Filename$ = LEFT$(Filename$, LEN(Filename$) - 1)

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

    ' make directory name
    ASCIZ.Dir = Filename$ + CHR$(0)

    ' check windows dos
    IF Windows.Detected THEN
       ' create directory
       InregsX.AX = &H7139
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create directory
       InregsX.AX = &H3900
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' check carry flag
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       ' error code path not found
       IF OutregsX.AX = &H3 THEN
          EXIT SUB
       END IF
    END IF

    ' increment directory counter
    Dirs.Counted = Dirs.Counted + 1!

    ' check to display creating directory
    IF Continuous.Display = False THEN
       ' create filenames
       Old.File$ = Filename$
       New.File$ = Filename$
       CALL MakeFile(Old.File$, New.File$)

       ' display directry copied
       COLOR Yellow, Black

       ' check carry flag
       IF (OutregsX.Flags AND &H1) = &H0 THEN
          IF Debug.Mode THEN
             IF Wide.List THEN
                Wide.List = False
                PRINT
             END IF
             PRINT "Creating: " + New.File$
          END IF
       ELSE
          ' error code access denied
          IF OutregsX.AX = &H5 THEN
             ' maximum length of directory
             IF Windows.Detected THEN
                IF LEN(New.File$) > 246 THEN
                   EXIT SUB
                END IF
             ELSE
                IF LEN(New.File$) > 66 THEN
                   EXIT SUB
                END IF
             END IF
             IF Debug.Mode THEN
                IF Wide.List THEN
                   Wide.List = False
                   PRINT
                END IF
                PRINT "Verifying: " + New.File$
             END IF
             EXIT SUB
          END IF
       END IF
    END IF

    ' make directory name
    Directory$ = RTRIM$(Dir.Copy.Name)
    Directory$ = LEFT$(Directory$, LEN(Directory$) - 1)
    ASCIZ.Dir = Directory$ + CHR$(0)

    ' check windows dos
    IF Windows.Detected THEN
       ' store directory date\time
       CALL ExtendedFile(1) ' get extended directory date\time

       ' get directory attributes
       InregsX.AX = &H7143
       InregsX.BX = &H0
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' get directory attributes
       InregsX.AX = &H4300
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' store source directory attribute
    Dir.Attribute = OutregsX.CX

    ' remove directory attribute to change directory attribute
    Dir.Attribute = Dir.Attribute AND NOT &H10

    ' make directory name
    ASCIZ.Dir = Filename$ + CHR$(0)

    ' check windows dos
    IF Windows.Detected THEN
       ' copy directory date\time
       CALL ExtendedFile(2) ' set extended directory date\time

       ' copy directory attribute
       InregsX.AX = &H7143
       InregsX.BX = &H1
       InregsX.CX = Dir.Attribute
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' copy directory attribute
       InregsX.AX = &H4301
       InregsX.CX = Dir.Attribute
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF
END SUB

' routine to create directory structure
SUB CreateDirectory(Directory.Path$)
 ' store current date\time
 Current.Month = VAL(MID$(DATE$, 1, 2))
 Current.Day = VAL(MID$(DATE$, 4, 2))
 Current.Year = VAL(MID$(DATE$, 7, 4))
 Current.Hour = VAL(MID$(TIME$, 1, 2))
 Current.Minute = VAL(MID$(TIME$, 4, 2))
 Current.Second = VAL(MID$(TIME$, 7, 2))
 Current.Millisecond = CINT(ABS(TIMER - CSNG(Current.Hour * 3600 + Current.Minute * 60 + Current.Second)) * 100)

 ' get current drive
 InregsX.AX = &H1900
 CALL InterruptX(&H21, InregsX, OutregsX)
 Current.Create = OutregsX.AX AND &HFF

 ' make directory
 Directory$ = RTRIM$(Directory.Path$)
 Drive.Create = ASC(LEFT$(Directory$, 1))
 Directory$ = MID$(Directory$, 3)

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

 ' construct path
 Next.Dir = INSTR(Directory$, "\")
 DO
    IF Next.Dir = False THEN
       EXIT DO
    END IF
    Filename$ = LEFT$(Directory$, Next.Dir - 1)
    Next.Dir = INSTR(Next.Dir + 1, Directory$, "\")

    ' make directory name
    ASCIZ.Dir = Filename$ + CHR$(0)

    ' check windows dos
    IF Windows.Detected THEN
       ' create directory
       InregsX.AX = &H7139
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create directory
       InregsX.AX = &H3900
       InregsX.DS = VARSEG(ASCIZ.Dir)
       InregsX.DX = VARPTR(ASCIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' check carry flag
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' check windows dos
       IF Windows.Detected THEN
          Directory.LastWrite.Date = VAL("&H" + HEX$((Current.Year - 1980) * 512))
          Directory.LastWrite.Date = Directory.LastWrite.Date + Current.Month * 32 + Current.Day
          Directory.LastWrite.Time = VAL("&H" + HEX$(Current.Hour * 2048))
          Directory.LastWrite.Time = Directory.LastWrite.Time + Current.Minute * 32 + INT(Current.Second / 2)

          Directory.LastAccess.Date = VAL("&H" + HEX$((Current.Year - 1980) * 512))
          Directory.LastAccess.Date = Directory.LastAccess.Date + Current.Month * 32 + Current.Day

          Directory.Creation.Date = VAL("&H" + HEX$((Current.Year - 1980) * 512))
          Directory.Creation.Date = Directory.Creation.Date + Current.Month * 32 + Current.Day
          Directory.Creation.Time = VAL("&H" + HEX$(Current.Hour * 2048))
          Directory.Creation.Time = Directory.Creation.Time + Current.Minute * 32 + INT(Current.Second / 2)
          Directory.Creation.Milli = Current.Millisecond

          ' copy directory date\time
          CALL ExtendedFile(2) ' set extended directory date\time
       ELSE
          Source.File.Time = VAL("&H" + HEX$(Current.Hour * 2048))
          Source.File.Time = Source.File.Time + Current.Minute * 32 + INT(Current.Second / 2)
          Source.File.Date = VAL("&H" + HEX$((Current.Year - 1980) * 512))
          Source.File.Date = Source.File.Date + Current.Month * 32 + Current.Day

          ' copy directory date\time
          CALL ExtendedFile(-2) ' set filename date\time
       END IF
    END IF
 LOOP

 ' restore drive
 InregsX.AX = &HE00
 InregsX.DX = Current.Create
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

' subroutine to delete destination filename
SUB DeleteDestFile(Delete.Prompt, Filename$)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260

 ' close output file
 IF Append.Files THEN
    CALL CloseFile
    Output.File.Open = False
 END IF

 ' make drive
 Start.Dir = Dest.Dir
 IF MID$(Start.Dir, 2, 1) = ":" THEN
    Drive.Number = ASC(UCASE$(LEFT$(Start.Dir, 1)))
    Start.Dir = MID$(Start.Dir, 3)
 ELSE
    Drive.Number = ASC(UCASE$(Drive.Search))
 END IF
 
 ' display file being deleted
 IF Dest.File = NUL THEN
    Source.File$ = CHR$(Drive.Number) + ":" + Start.Dir + Filename$
 ELSE
    Source.File$ = CHR$(Drive.Number) + ":" + Start.Dir
 END IF

 ' check prompt to delete
 IF Delete.Prompt THEN
    Prompt$ = NUL
    IF Display.Errors THEN
       Prompt$ = "Error copying file. "
    END IF
    Prompt$ = Prompt$ + "Delete partially copied destination file: " + Source.File$ + "(y/n)?"
    CALL MorePrompt(Prompt$, "yn", Respond$, "y")
    IF BreakIS THEN
       Respond$ = "n"
    END IF
    SELECT CASE Respond$
    CASE "n"
       EXIT SUB
    END SELECT
 END IF

 ' check to display file
 IF Continuous.Display = False THEN
    IF Wide.List THEN
       Wide.List = False
       PRINT
    END IF
    COLOR White, Black
    PRINT "Deleting file: " + Source.File$
 END IF

 ' check windows dos
 ASCIZ = Source.File$ + CHR$(0)
 IF Windows.Detected THEN
    InregsX.AX = &H7141
    InregsX.DS = VARSEG(ASCIZ)
    InregsX.DX = VARPTR(ASCIZ)
    InregsX.SI = &H1
    InregsX.CX = &H27
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    InregsX.AX = &H4100
    InregsX.DS = VARSEG(ASCIZ)
    InregsX.DX = VARPTR(ASCIZ)
    InregsX.CX = &H27
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' display any errors
 CALL DisplayError("Error deleting file(1).")
END SUB

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

 ' make filename
 ASCIZ = "\" + CHR$(0)

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

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

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

 ' check delete error flag
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    Total.Deleted = Total.Deleted + 1
 END IF
END SUB

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

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

 ' check windows dos
 IF Windows.Detected THEN
    ' 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)

    ' display any errors
    CALL DisplayError("Error accessing drive(7).")
 ELSE
    ' 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 accessing drive(8).")

    ' check carry flag error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' store fcb
       Drive.Number = ASC(UCASE$(Drive.Search))
       FCBfile.ExtendedFCB = CHR$(&HFF)
       FCBfile.FileAttribute = CHR$(&H27)
       FCBfile.DriveNumber = CHR$(Drive.Number - 64)
       FCBfile.Filename = "????????"
       FCBfile.Extension = "???"

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

' subroutine to delete source filename
SUB DeleteSourceFile(Directory$, Filename$)
 ' declare subroutine variables
 DIM ASCIZ AS STRING * 260

 ' make filename
 Source.File$ = Directory$ + Filename$

 ' reset delete flag
 Response = False

 ' check continue flag
 IF Continue.Deleting THEN
    Response = True
 END IF

 ' check delete prompting
 IF Prompt.Delete1 THEN
    Response = True
 END IF

 ' prompt to delete
 IF Response = False THEN
    Outpt$ = Source.File$
    IF Display.Lowercase THEN
       Outpt$ = LCASE$(Outpt$)
    ELSE
       IF Windows.Detected = False THEN
          Outpt$ = UCASE$(Outpt$)
       END IF
    END IF
    Outpt$ = Drive.Search + ":" + Outpt$
    Prompt$ = "Delete copied file: " + Outpt$ + "(y/n/q/c)?"
    CALL MorePrompt(Prompt$, "ynqc", Respond$, "y")
    IF BreakIS THEN
       Respond$ = "q"
    END IF
    SELECT CASE Respond$
    CASE "c"
       Response = True
       Continue.Deleting = True
    CASE "q"
       Response = False
       Quit.Searching = True
    CASE "y"
       Response = True
    CASE "n"
       Response = False
    END SELECT
 END IF

 ' check delete flag
 IF Response = True THEN
    ' display file being deleted
    IF Continuous.Display = False THEN
       IF Wide.List THEN
          Wide.List = False
          PRINT
       END IF
       COLOR White, Black
       Prompt$ = "Deleting file: " + Outpt$
       PRINT Prompt$
    END IF

    ' check windows dos
    ASCIZ = Source.File$ + CHR$(0)
    IF Windows.Detected THEN
       InregsX.AX = &H7141
       InregsX.DS = VARSEG(ASCIZ)
       InregsX.DX = VARPTR(ASCIZ)
       InregsX.SI = &H1
       InregsX.CX = &H27
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       InregsX.AX = &H4100
       InregsX.DS = VARSEG(ASCIZ)
       InregsX.DX = VARPTR(ASCIZ)
       InregsX.CX = &H27
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF

    ' display any errors
    CALL DisplayError("Error deleting file(2).")
 END IF
END SUB

' subroutine to delete subdirectory
SUB DeleteSubdirectory(Source.Directory$)
 ' declare work variables
 DIM PromptASCIZ AS STRING * 260
 DIM Current.DTA.SEG AS INTEGER
 DIM Current.DTA.OFF AS INTEGER
 DIM PromptDTA AS DTAtype

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

 ' restore prompt dta
 InregsX.AX = &H1A00
 InregsX.DS = VARSEG(PromptDTA)
 InregsX.DX = VARPTR(PromptDTA)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' make directory name
 Directory$ = Source.Directory$
 Temp.Dir$ = Directory$
 CALL MakeFile(Directory$, Temp.Dir$)

 ' reset delete flag
 Response = False

 ' check to continue
 IF Continue.Subdelete THEN
    Response = True
 END IF

 ' check for prompting
 IF Prompt.Delete2 THEN
    Response = True
 END IF

 ' make directory name
 Outpt$ = Directory$
 IF Display.Lowercase THEN
    Outpt$ = LCASE$(Outpt$)
 ELSE
    IF Windows.Detected = False THEN
       Outpt$ = UCASE$(Outpt$)
    END IF
 END IF

 ' check delete flag
 IF Response = False THEN
    ' prompt to delete
    Prompt$ = "Delete copied directory: " + Outpt$ + "(y/n/q/c)?"
    CALL MorePrompt(Prompt$, "ynqc", Respond$, "y")
    IF BreakIS THEN
       Respond$ = "q"
    END IF
    SELECT CASE Respond$
    CASE "c"
       Response = True
       Continue.Subdelete = True
    CASE "q"
       Response = False
       Quit.Searching = True
    CASE "y"
       Response = True
    CASE "n"
       Response = False
    END SELECT
 END IF

 ' check delete flag
 IF Response THEN
    IF Continuous.Display = False THEN
       IF Wide.List THEN
          Wide.List = False
          PRINT
       END IF
       COLOR White, Black
       PRINT "Deleting directory: " + Outpt$
    END IF
    CALL TreeDirectories(Directory$)
 END IF

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

' subroutine to delete subdirectories
SUB TreeDirectories(Directory.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)

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

 ' check findirst error
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    ' delete filenames
    CALL DeleteFiles(Directory.Search$)

    ' restore dta
    GOSUB Tree.DTA

    ' recurse subdirectories
    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 next search directory
	      Next.Directory$ = Directory.Search$ + Directory$ + "\"

	      ' recursively search subdirectories
	      CALL TreeDirectories(Next.Directory$)

              ' restore dta
	      GOSUB Tree.DTA
	   END IF
       END IF

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

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

 ' check windows dos
 IF Windows.Detected THEN
    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' delete directory
 CALL DeleteDirectory(Directory.Search$)
 EXIT SUB

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

' verifies duplicate filenames
SUB FilenameCopying(From$, To$)
 ' declare subroutine variables
 DIM ASCIZ1 AS STRING * 260
 DIM ASCIZ2 AS STRING * 261

 ' reset flag
 Filename.Exists = False

 ' make filenames
 From.File$ = From$
 To.File$ = To$
 CALL MakeFile(From.File$, To.File$)

 ' check filename length
 IF Windows.Detected THEN
    IF LEN(To.File$) > 259 THEN
       Filename.Exists = True
       EXIT SUB
    END IF
 ELSE
    IF LEN(To.File$) > 79 THEN
       Filename.Exists = True
       EXIT SUB
    END IF
 END IF

 ' check to run DOS commands
 FOR Count = 1 TO Number.Commands
    Next.Command$ = RTRIM$(DOS.Command(Count))
    IF LEN(Next.Command$) THEN
       CALL ExecuteCommand(Next.Command$, From.File$)
    END IF
 NEXT

 ' store filenames
 From.Filename$ = From.File$
 To.Filename$ = To.File$

 ' display filename
 GOSUB Display.File

 ' determine if files exist
 IF UCASE$(From.Filename$) = UCASE$(To.Filename$) THEN
    Filename.Exists = True
    IF Continuous.Display = False THEN
       IF Wide.List THEN
          Wide.List = False
          PRINT
       END IF
       COLOR White, Black
       PRINT "File in list cannot be copied onto itself."
    END IF
 END IF
 EXIT SUB

Display.File:
 ' display filename
 IF Windows.Detected AND Short.Display THEN
    ' get windows short filename
    ASCIZ1 = From.File$ + CHR$(0)
    InregsX.AX = &H7160
    InregsX.CX = &H8001
    InregsX.DS = VARSEG(ASCIZ1)
    InregsX.SI = VARPTR(ASCIZ1)
    InregsX.ES = VARSEG(ASCIZ2)
    InregsX.DI = VARPTR(ASCIZ2)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' check carry flag error
    IF (OutregsX.Flags AND &H1) = &H1 THEN
       From.File$ = NUL
    ELSE
       ' store windows short filename
       From.File$ = ASCIZ2
       Imbedded = INSTR(From.File$, CHR$(0))
       From.File$ = LEFT$(From.File$, Imbedded - 1)
    END IF

    ' convert to lowercase
    IF Display.Lowercase THEN
       From.File$ = LCASE$(From.File$)
    END IF
 END IF

 ' check to truncate path
 IF Display.Path THEN
    FOR Imbedded = LEN(From.File$) TO 1 STEP -1
       IF MID$(From.File$, Imbedded, 1) = "\" THEN
          From.File$ = LEFT$(From.File$, 2) + MID$(From.File$, Imbedded + 1)
          EXIT FOR
       ENDIF
    NEXT
    FOR Imbedded = LEN(To.File$) TO 1 STEP -1
       IF MID$(To.File$, Imbedded, 1) = "\" THEN
          To.File$ = LEFT$(To.File$, 2) + MID$(To.File$, Imbedded + 1)
          EXIT FOR
       ENDIF
    NEXT
 END IF

 ' check to truncate drive letter
 IF Drive.Letter THEN
    From.File$ = MID$(From.File$, 3)
    To.File$ = MID$(To.File$, 3)
 END IF

 ' display filename being copied
 Outpt$ = From.File$
 Outpt$ = RTRIM$(Outpt$)

 ' check filename
 IF Outpt$ <> NUL THEN
    IF Display.Wide AND Display.Path THEN
       COLOR Yellow, Black
       PRINT Outpt$ + SPACE$(15 - LEN(Outpt$));
       Wide.List = Wide.List + 1
       IF Wide.List = 5 THEN
          Wide.List = False
          PRINT
       END IF
    ELSE
       IF Short.Display = False THEN
          Outpt$ = Outpt$ + " - " + To.File$
          IF Append.Dest THEN
             Outpt$ = "Appending: " + Outpt$
          ELSE
             Outpt$ = "Copying: " + Outpt$
          END IF
       END IF
       COLOR Yellow, Black
       PRINT Outpt$
    END IF
 END IF
 RETURN
END SUB

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

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

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

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

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

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

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

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

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

 ' restore key trapping
 CALL RestInt

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

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

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

 ' reset key trapping
 CALL SetInt

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

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

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

' routine preserves destination file date\time.
SUB ExtendedFile(TimeType%)
 SELECT CASE TimeType%
 CASE -2
    ' set date\time
    InregsX.AX = &H5701
    InregsX.BX = Append.Handle
    IF Override.Time THEN
       InregsX.CX = Override.Time
    ELSE
       InregsX.CX = Source.File.Time
    END IF
    IF Override.Date THEN
       InregsX.DX = Override.Date
    ELSE
       InregsX.DX = Source.File.Date
    END IF
    CALL InterruptX(&H21, InregsX, OutregsX)
 CASE -1
    ' set extended date\time
    InregsX.AX = &H7143
    InregsX.BX = &H03
    InregsX.DS = VARSEG(ASCIZfile)
    InregsX.DX = VARPTR(ASCIZfile)
    InregsX.CX = Windows.LastWrite.Time
    InregsX.DI = Windows.LastWrite.Date
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting filename last modified date\time.")
    END IF

    IF Windows.Dos = False THEN
       EXIT SUB
    ENDIF

    InregsX.AX = &H7143
    InregsX.BX = &H05
    InregsX.DS = VARSEG(ASCIZfile)
    InregsX.DX = VARPTR(ASCIZfile)
    InregsX.DI = Windows.LastAccess.Date
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting filename last access date.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H07
    InregsX.DS = VARSEG(ASCIZfile)
    InregsX.DX = VARPTR(ASCIZfile)
    InregsX.CX = Windows.Creation.Time
    InregsX.DI = Windows.Creation.Date
    InregsX.SI = Windows.Creation.Milli
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting filename creation date\time.")
    END IF
 CASE 0
    ' get extended date\time
    InregsX.AX = &H7143
    InregsX.BX = &H04
    InregsX.DS = VARSEG(ASCIZ.Copy)
    InregsX.DX = VARPTR(ASCIZ.Copy)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Windows.LastWrite.Time = OutregsX.CX
    Windows.LastWrite.Date = OutregsX.DI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading filename last modified date\time.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H06
    InregsX.DS = VARSEG(ASCIZ.Copy)
    InregsX.DX = VARPTR(ASCIZ.Copy)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Windows.LastAccess.Date = OutregsX.DI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading filename last access date.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H08
    InregsX.DS = VARSEG(ASCIZ.Copy)
    InregsX.DX = VARPTR(ASCIZ.Copy)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Windows.Creation.Time = OutregsX.CX
    Windows.Creation.Date = OutregsX.DI
    Windows.Creation.Milli = OutregsX.SI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading filename creation date\time.")
    END IF

    IF Override.Time THEN
       IF Create.Time THEN
          Windows.Creation.Time = Override.Time
          Windows.Creation.Time = Millisecond
       END IF
       IF Modify.Time THEN
          Windows.LastWrite.Time = Override.Time
       END IF
    END IF
    IF Override.Date THEN
       IF Create.Time THEN
          Windows.Creation.Date = Override.Date
       END IF
       IF Access.Time THEN
          Windows.LastAccess.Date = Override.Date
       END IF
       IF Modify.Time THEN
          Windows.LastWrite.Date = Override.Date
       END IF
    END IF
 CASE 1
    ' get directory extended date\time
    InregsX.AX = &H7143
    InregsX.BX = &H04
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Directory.LastWrite.Time = OutregsX.CX
    Directory.LastWrite.Date = OutregsX.DI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading directory last modified date\time.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H06
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Directory.LastAccess.Date = OutregsX.DI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading directory last access date.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H08
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Directory.Creation.Time = OutregsX.CX
    Directory.Creation.Date = OutregsX.DI
    Directory.Creation.Milli = OutregsX.SI

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading directory creation date\time.")
    END IF

 CASE 2
    ' check directory
    IF MID$(ASCIZ.dir, 2, 2) = ":" + CHR$(0) THEN
       EXIT SUB
    END IF

    ' set directory extended date\time
    InregsX.AX = &H7143
    InregsX.BX = &H03
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    InregsX.CX = Directory.LastWrite.Time
    InregsX.DI = Directory.LastWrite.Date
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting directory last modified date\time.")
    END IF

    IF Windows.Dos = False THEN
       EXIT SUB
    ENDIF

    InregsX.AX = &H7143
    InregsX.BX = &H05
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    InregsX.DI = Directory.LastAccess.Date
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting directory last access date.")
    END IF

    InregsX.AX = &H7143
    InregsX.BX = &H07
    InregsX.DS = VARSEG(ASCIZ.dir)
    InregsX.DX = VARPTR(ASCIZ.dir)
    InregsX.CX = Directory.Creation.Time
    InregsX.DI = Directory.Creation.Date
    InregsX.SI = Directory.Creation.Milli
    CALL InterruptX(&H21, InregsX, OutregsX)

    IF Debug.Mode THEN
       ' display any errors
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H setting directory creation date\time.")
    END IF
 END SELECT
END SUB
