REM file: Copyit.bas - Public Domain DOS Utility. Module 1 of 2. v4.3a. 2007.

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

' declare external procedures
DECLARE SUB SetInt
DECLARE SUB RestInt

' backwards compatible for bc 7.1
'  (edit out REMs in include file for BC7)
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 * 348

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

' 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
REDIM Destinate.Netpaths(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

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

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

' check windows/dos (fixed v4.0a 08/31/2006)
Windows.Detected = True
If Load.Windows = False Then
   InregsX.AX = &H160A
   CALL InterruptX(&H2F, InregsX, OutregsX)
   IF OutregsX.AX > 0 THEN
      InregsX.AX = &H4A33
      CALL InterruptX(&H2F, InregsX, OutregsX)
      IF OutregsX.AX = 0 THEN
         Windows.Detected = 0 ' DOS 7.00
      END IF
   END IF
END IF
InregsX.AX = &H4A33
CALL InterruptX(&H2F, InregsX, OutregsX)
IF OutregsX.AX = False THEN
   Windows.DOS = True
END IF
IF Load.DOS THEN
   Windows.Detected = False
END IF

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

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

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

' store default server name
Default.Net = Nul
InregsX.AX = &H5E00
InregsX.DS = VARSEG(Default.Net)
InregsX.DX = VARPTR(Default.Net)
CALL interruptX(&H21, InregsX, OutregsX)
' check error flag
IF (OutregsX.Flags AND &H1) = &H0 THEN
   ' read CH
   VarS = (OutregsX.CX AND &HFF00) / 256
   IF VarS > 0 THEN ' valid name
      ' store default server name
      Imbedded = INSTR(Default.Net, CHR$(0))
      IF Imbedded THEN
         Default.Net = LEFT$(Default.Net, Imbedded - 1)
      END IF
      Default.Net = "\\" + RTRIM$(Default.Net) +"\"
   END IF
END IF

' reset number of destination filenames
IF Number.Dest.Files = False THEN
   Number.Dest.Files = 1
   Destinate.Filename(1) = Nul
END IF

' reset number of destination directories
IF Number.Dest.Dirs = False OR Zero.Dirs THEN
   Zero.Dirs = True
   Number.Dest.Dirs = 1
   IF Number.Dest.Nets = False THEN
      Destinate.Directory(1) = Default.Dir
   ELSE
      Destinate.Directory(1) = "\"
   END IF
END IF

' reset and check netpaths
IF Zero.Nets THEN
   Var2$ = RTRIM$(Default.Net)
   IF LEN(Var2$) THEN
      Var2$ = Var2$ + Lcase$(Current.Drive) + "\"
      Number.Dest.Nets = 1
      Destinate.Netpaths(1) = Var2$
   END IF
END IF
IF Number.Dest.Nets = False THEN
   Number.Dest.Nets = 1
   Destinate.Netpaths(1) = Nul
END IF
FOR Count0 = 1 TO Number.Dest.Nets
   Next.Dest.Net$ = Destinate.Netpaths(Count0)
   Next.Dest.Net$ = RTRIM$(Next.Dest.Net$)
   IF Next.Dest.Net$ <> Nul THEN
      ' check netpath
      IF RIGHT$(Next.Dest.Net$, 1) = "\" THEN
         Next.Dest.Net$ = LEFT$(Next.Dest.Net$, LEN(Next.Dest.Net$) - 1)
      END IF
      IF LEFT$(Next.Dest.Net$, 2) <> "\\" THEN
         Next.Dest.Net$ = "\" + Next.Dest.Net$
      END IF
      IF LEFT$(Next.Dest.Net$, 2) <> "\\" THEN
         Next.Dest.Net$ = "\" + Next.Dest.Net$
      END IF
      Destinate.Netpaths(Count0) = Next.Dest.Net$
   END IF
NEXT

' check /d omitted or specified w/o parameters
IF Zero.Dirs THEN
   ' compare /g\\serv\c\ to default.net
   IF Number.Dest.Nets = 1 THEN
      Var1$ = RTRIM$(Destinate.Netpaths(1))
      IF LEN(Var1$) THEN
         Var1$ = UCASE$(Var1$) + "\"
         Var2$ = RTRIM$(Default.Net)
         IF LEN(Var2$) THEN
            Var2$ = UCASE$(Var2$) + Current.Drive + "\"
            IF Var1$ = Var2$ THEN
               Destinate.Directory(1) = Default.Dir
            END IF
         END IF
      END IF
   END IF
END IF

' 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

' check pipe length
InregsX.AX = &H4202 ' eof
InregsX.BX = 0 ' stdin
InregsX.CX = 0
InregsX.DX = 0
Call InterruptX(&H21, InregsX, OutregsX)

IF OutregsX.AX > 0 THEN
   Pipe.Redirect = True
   InregsX.AX = &H4200
   InregsX.BX = 0 ' stdin
   InregsX.CX = 0
   InregsX.DX = 0
   Call InterruptX(&H21, InregsX, OutregsX)
END IF

IF Pipe.Redirect = False THEN
   DEF SEG = &H40
   X = PEEK(&H71)
   DEF SEG
   IF X = 64 THEN
      DEF SEG = &H40
      POKE &H71, 0
      DEF SEG
   END IF
END IF

DO
   ' check control break
   IF BreakIS THEN
      EXIT DO
   END IF

   ' read standard input
   Standard.Input = Nul

   ' search standard input
   If Pipe.Redirect Then
      DO

         ' read from device
         InregsX.AX = &H3F00
         InregsX.BX = 0 ' stdin
         InregsX.CX = 1 ' char
         InregsX.DS = VARSEG(Pipe.Buffer)
         InregsX.DX = VARPTR(Pipe.Buffer)
         Call InterruptX(&H21, InregsX, OutregsX)
         IF (OutregsX.Flags AND &H1) = &H1 THEN
            EXIT DO
         END IF
         IF (OutregsX.Flags AND &H1) = &H0 THEN
            IF OutregsX.AX = 0 THEN
               EXIT DO
            END IF

            ' store input
            Redirected.Input = True

            ' store character
            Char$ = Pipe.Buffer

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

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

   ' check control break
   IF BreakIS THEN
      EXIT DO
   END IF

   ' check nul filename input
   IF Redirected.Input = False THEN
      IF Standard.Input = Nul THEN
         CALL RestInt ' restore Control-Break
         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
   DO

      ' check control break
      IF BreakIS THEN
         EXIT DO
      END IF

      ' store input filename
      Stored.Command.Line$ = Command.Line

      ' create filenames
      CALL MakeFilename

      ' locate filelist character
      Flag = False
      Command.Out2$ = Nul
      Command.Out$ = Command.Work
      IF MID$(Command.Out$, 2, 1) = ":" THEN
         Command.Out2$ = LEFT$(Command.Out$, 2)
         Command.Out$ = MID$(Command.Out$, 3)
      END IF
      IF LEFT$(Command.Out$, 1) = "@" THEN
         Flag = True
         Command.Out$ = MID$(Command.Out$, 2)
      ELSE
         FOR Var = LEN(Command.Out$) TO 1 STEP -1
            IF MID$(Command.Out$, Var, 1) = "\" THEN
               Char$ = MID$(Command.Out$, Var + 1)
               IF LEFT$(Char$, 1) = "@" THEN
                  Flag = True
                  Command.Out$ = LEFT$(Command.Out$, Var) + MID$(Char$, 2)
               END IF
               EXIT FOR
            END IF
         NEXT
      END IF
      IF LEN(Command.Out2$) THEN
         Command.Out$ = Command.Out2$ + Command.Out$
      END IF

      ' process work filename containing filelist
      IF Flag AND Enable.Redirect THEN

	 ' process work filename
         Command.Out$ = Ambiguate$(Command.Out$)
         Var$ = Nul
         IF Command.Out$ <> Nul THEN
            CLOSE #1
            Data.Error = False
            Error.Flag = True
            Var$ = DIR$(Command.Out$)
            Error.Flag = False
            IF Data.Error THEN
               Var$ = Nul
            END IF
         END IF

         ' check filelist exists
         IF Var$ <> Nul THEN

            ' concatenate work filename
            Var$ = Command.Work ' command line filename
            FOR Var = LEN(Var$) TO 1 STEP -1
               IF MID$(Var$, Var, 1) = "\" THEN
                  Var$ = MID$(Var$, Var + 1)
                  EXIT FOR
               END IF
            NEXT
            IF MID$(Var$, 2, 1) = ":" THEN
               Var$ = MID$(Var$, 3)
            END IF
            Filename2$ = Var$
            Var$ = MID$(Var$, 2)
            Var2$ = Command.Out$ ' ambiguated filename
            FOR Var = LEN(Var2$) TO 1 STEP -1
               IF MID$(Var2$, Var, 1) = "\" THEN
                  Var2$ = LEFT$(Var2$, Var)
                  Var$ = Var2$ + Var$
                  EXIT FOR
               END IF
            NEXT

            ' check filename
            CALL CheckFile(Filename2$,Flag,True)
            IF Flag = False THEN
               Var$ = Nul
            END IF

            ' prompt for filelist
            IF Disable.Prompts = False THEN
               IF Var$ <> Nul THEN
                  ' prompt to use filelist
                  Prompt$ = "Use '" + Lcase$(Var$) + "' as filelist(y/n/q)?"
                  CALL MorePrompt(Prompt$, "ynq", Outpt$, "n")
                  IF BreakIS THEN
                     Outpt$ = "q"
                  END IF
                  SELECT CASE Outpt$
                  CASE "q"
                     Quit.Searching = True
                     GOTO Next.Filename2
                  CASE "n"
                     Var$ = Nul
                  END SELECT
               END IF
            END IF
         END IF

         ' check filelist exists
         IF Var$ = Nul THEN

            ' restore original command line
            Command.Line = Stored.Command.Line$

            ' create filenames
            CALL MakeFilename

            ' continue processing filename
            GOTO Next.Filename1
         END IF

         ' open filename list
         Data.Error = False
         Error.Flag = True
         OPEN Command.Out$ FOR INPUT SHARED AS #1
         Error.Flag = False

	 ' check error flag
	 IF Data.Error THEN

            ' display filename error
            OutregsX.Flags = &H1
            CALL DisplayError("Error opening listfile.")
	 ELSE

	    ' process input filenames
	    DO UNTIL EOF(1)

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

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

                  ' process filename
                  Max.Recurse = 0
                  CALL ProcessFilename(Quit)

                  ' check control break
                  IF BreakIS THEN
                     EXIT DO
                  END IF

                  ' check overwrite flag
                  IF Quit OR Quit.Searching THEN
                     Error.Level = 1
                     GOTO End.Copy
                  END IF
               END IF
	    LOOP
	 END IF
         GOTO Next.Filename2
      END IF

' continue label
Next.Filename1:

      ' process filename
      Max.Recurse = 0
      CALL ProcessFilename(Quit)

      ' check control break
      IF BreakIS THEN
         EXIT DO
      END IF

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

' continue label
Next.Filename2:

      ' check command line
      IF Command.Line = Nul THEN
         EXIT DO
      END IF

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

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

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

' program end label
End.Copy:

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

' reset basic dta
InregsX.AX = &H1A00
InregsX.DS = BASIC.DTA.SEG
InregsX.DX = BASIC.DTA.OFF
CALL InterruptX(&H21, InregsX, OutregsX)

' restore current drive
InregsX.AX = &HE00
InregsX.DX = 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.ASCIIZ)
   InregsX.DX = VARPTR(Directory.ASCIIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
ELSE
   ' restore current directory
   InregsX.AX = &H3B00
   InregsX.DS = VARSEG(Directory.ASCIIZ)
   InregsX.DX = VARPTR(Directory.ASCIIZ)
   CALL InterruptX(&H21, InregsX, OutregsX)
END IF

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

' check timing past midnight
IF Time.Elapsed < SFalse THEN
   Time.Elapsed = Time.Elapsed + 86400! ' add one day of seconds
END IF

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

' restore key trapping
CALL RestInt

' reset color
COLOR Plain, Black

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

' process filename
SUB ProcessFilename(Quit)
 ' check control break
 IF BreakIS THEN
    RETURN
 END IF

 ' process search filename
 CALL ProcessCommand(Quit)

 ' reset basic dta
 InregsX.AX = &H1A00
 InregsX.DS = BASIC.DTA.SEG
 InregsX.DX = BASIC.DTA.OFF
 CALL InterruptX(&H21, InregsX, OutregsX)
END SUB

' make header
Header:
 IF Header.Flag THEN
    RETURN
 END IF
 Header.Flag = True
 IF Continuous.Display = False THEN
    COLOR White, Black
    PRINT "Copyit "+Version$+" "+Release$+": 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 Error.Flag THEN
    RESUME NEXT
 END IF
 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 5
    Temp.Outpt$ = "Syntax error." ' should not happen
 CASE 53
    Temp.Outpt$ = "File not found."
 CASE 57
    Temp.Outpt$ = "Device I/O error."
 CASE 61
    Temp.Outpt$ = "Disk full."
 CASE 64
    Temp.Outpt$ = "Bad file name."
 CASE 67
    Temp.Outpt$ = "Too many files."
 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(Next.Dest.Net$, 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
 Temp.Net$ = RTRIM$(Source.Net)
 IF Temp.Net$ = Nul THEN
    ASCIIZ.Copy = Source.Directory$ + Source.Filename$ + CHR$(0)
 ELSE
    IF LEFT$(Directory.Search$, 1) = "\" THEN
       ASCIIZ.Copy = Temp.Net$ + Source.Directory$ + Source.Filename$ + CHR$(0)
    ELSE
       ASCIIZ.Copy = Temp.Net$ + "\" + Source.Directory$ + Source.Filename$ + CHR$(0)
    END IF
 END IF

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

 ' store file attribute
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Attribute = 0
 ELSE
    Attribute = OutregsX.CX
 END IF
 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 range
 CALL CheckSizeRange(Range)

 ' check return flag
 IF Range THEN
    EXIT SUB
 END IF

 ' store file date and time
 Synch.Milli = False
 IF Windows.Detected THEN
    Synch.Work.Creation.Date = ASC(MID$(FDTAfile.CreateTime, 4, 1))
    Synch.Work.Creation.Date = Synch.Work.Creation.Date * &H100 + ASC(MID$(FDTAfile.CreateTime, 3, 1))
    Synch.Work.Creation.Time = ASC(MID$(FDTAfile.CreateTime, 2, 1))
    Synch.Work.Creation.Time = Synch.Work.Creation.Time * &H100 + ASC(MID$(FDTAfile.CreateTime, 1, 1))
    Synch.Work.Access.Date = ASC(MID$(FDTAfile.AccessTime, 4, 1))
    Synch.Work.Access.Date = Synch.Work.Access.Date * &H100 + ASC(MID$(FDTAfile.AccessTime, 3, 1))
    Synch.Work.Modified.Date = ASC(MID$(FDTAfile.ModTime, 4, 1))
    Synch.Work.Modified.Date = Synch.Work.Modified.Date * &H100 + ASC(MID$(FDTAfile.ModTime, 3, 1))
    Synch.Work.Modified.Time = ASC(MID$(FDTAfile.ModTime, 2, 1))
    Synch.Work.Modified.Time = Synch.Work.Modified.Time * &H100 + ASC(MID$(FDTAfile.ModTime, 1, 1))
    Synch.Work.Modified.Time = Synch.Work.Modified.Time AND NOT(&H1F) ' remove seconds
    ' read file millisecond
    InregsX.AX = &H7143
    InregsX.BX = &H08
    InregsX.DS = VARSEG(ASCIIZ.Copy)
    InregsX.DX = VARPTR(ASCIIZ.Copy)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Synch.Milli = OutregsX.SI
    IF Synch.Milli >= 100 THEN
       Synch.Work.Creation.Time = Synch.Work.Creation.Time + .5!
    END IF
 ELSE
    Synch.Work.Modified.Date = ASC(MID$(TempDTA.FileDate, 2, 1))
    Synch.Work.Modified.Date = Synch.Work.Modified.Date * &H100 + ASC(MID$(TempDTA.FileDate, 1, 1))
    Synch.Work.Modified.Time = ASC(MID$(TempDTA.FileTime, 2, 1))
    Synch.Work.Modified.Time = Synch.Work.Modified.Time * &H100 + ASC(MID$(TempDTA.FileTime, 1, 1))
    Synch.Work.Modified.Time = Synch.Work.Modified.Time AND NOT(&H1F) ' remove seconds
 END IF

 ' check date\time range
 CALL CheckDateRange(Range)

 ' check return flag
 IF Range THEN
    EXIT SUB
 END IF

 ' construct extended date\time string
 FileInfo$ = Nul
 IF Double.Line >= 1 THEN
    IF Windows.Detected THEN
       ' make file info
       Synch.Work.Creation.Time = INT(Synch.Work.Creation.Time)
       FileInfo$ = MakeDate(Synch.Work.Creation.Date, Synch.Work.Creation.Time, Synch.Milli)
       FileInfo$ = FileInfo$ + "\" + MakeDate(Synch.Work.Access.Date, 0, -1)
       FileInfo$ = FileInfo$ + "\" + MakeDate(Synch.Work.Modified.Date, Synch.Work.Modified.Time, -2)
    ELSE
       ' make file info
       FileInfo$ = MakeDate(Synch.Work.Modified.Date, Synch.Work.Modified.Time, -2)
    END IF
 END IF

 ' store filename being 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
    IF Next.Dest.Net$ = Nul THEN
       Write.ASCIIZ = Copy.Dest$ + CHR$(0)
    ELSE
       IF LEFT$(Copy.Dest$, 1) = "\" THEN
          Write.ASCIIZ = Next.Dest.Net$ + Copy.Dest$ + CHR$(0)
       ELSE
          Write.ASCIIZ = Next.Dest.Net$ + "\" + Copy.Dest$ + CHR$(0)
       END IF
    END IF
 END IF

 ' check filename
 Filename2$ = Source.Filename$
 CALL CheckFile(Filename2$,Flag,False)
 IF Flag = False THEN
    EXIT SUB
 END IF

 ' check if appending files
 IF Append.Files THEN
    ' store output file info
    IF Next.Dest.Net$ = Nul THEN
       ASCIIZfile = Dest.Dir + CHR$(0)
    ELSE
       IF LEFT$(Dest.Dir, 1) = "\" THEN
          ASCIIZfile = Next.Dest.Net$ + Dest.Dir + CHR$(0)
       ELSE
          ASCIIZfile = Next.Dest.Net$ + "\" + Dest.Dir + CHR$(0)
       END IF
    END IF

    ' check if files similar
    CALL ReadFileSynch

    ' restore current dta
    GOSUB Copy.DTA

    ' check files
    CALL CheckSynched(Synched)
    IF Synched THEN
       EXIT SUB
    END IF
 ELSE
    ' store output file info
    ASCIIZfile = Write.ASCIIZ

    ' 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 duplicate filenames,
 ' displays filenames being copied.
 CALL FilenameCopying(Next.Dest.Net$, Copy.File$, Copy.Dest$)

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

 ' check windows dos
 IF Windows.Detected THEN
    ' read extended filename date\time
    CALL ExtendedFile(3)
 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(ASCIIZ.Copy)
    InregsX.SI = VARPTR(ASCIIZ.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(ASCIIZ.Copy)
    InregsX.SI = VARPTR(ASCIIZ.Copy)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store file handle
 Handle = OutregsX.AX

 ' display any error
 IF Debug.Mode THEN
    IF OutregsX.AX > False 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 > False 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

 ' set seek position to start of file
 SeekPosition# = 0#

 ' check byte position override
 IF Byte.Switch THEN
    ' offset from specified byte position
    SeekPosition# = Byte.Override
 END IF

 ' check byte position override
 IF Resume.File THEN ' v4.3a 08/24/2007
    ' offset from dest. filesize position
    SeekPosition# = Dest.File.Size
 END IF

 ' calculate high/low seek position.
 High# = INT(SeekPosition# / 65536)
 Low# = SeekPosition# - High# * &H10000

 ' seek position in input file
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High#)))
 InregsX.DX = INT(VAL("&H" + HEX$(Low#)))
 CALL InterruptX(&H21, InregsX, OutregsX)

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

 ' reset delete file flag
 Copy.Error = False

 ' check if appending files
 IF Append.Files THEN
    ' store output file info
    IF Next.Dest.Net$ = Nul THEN
       ASCIIZfile = Dest.Dir + CHR$(0)
    ELSE
       IF LEFT$(Dest.Dir, 1) = "\" THEN
          ASCIIZfile = Next.Dest.Net$ + Dest.Dir + CHR$(0)
       ELSE
          ASCIIZfile = Next.Dest.Net$ + "\" + Dest.Dir + CHR$(0)
       END IF
    END IF
    File.Attribute = Attribute

    ' check to overwrite
    IF Overwrite.Prompt = False THEN
       Copy.File$ = Directory.Search$ + RTRIM$(Filename.Search)
       Copy.Dest$ = Dest.Dir
       CALL PromptOverwrite(Next.Dest.Net$, 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
 ELSE
    ' store output file info
    ASCIIZfile = Write.ASCIIZ
    File.Attribute = Attribute

    ' check to overwrite
    IF Overwrite.Prompt = False THEN
       CALL PromptOverwrite(Next.Dest.Net$, 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 = 4096
    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 = 16 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 = 0 THEN
          IF Percent.Flag = 0 THEN
             PRINT "000%";
          END IF
          Percent.Flag = True
       ELSE
          IF Percent <> Last.Percent THEN
             Last.Percent = Percent
             IF Percent.Flag THEN
                FOR Imbedded = 1 TO 4
                   PRINT CHR$(29);" ";CHR$(29);
                NEXT
             END IF
             PRINT RIGHT$(STR$(Percent + 1000%), 3) + "%";
             Percent.Flag = True
          END IF
       END IF
    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
    Last.Percent = False
    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(Next.Dest.Net$)
 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 > False 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
    IF Append.Dest = False THEN ' v4.2a 07/10/2007
       CALL DeleteDestFile(False, Next.Dest.Net$, Source.Filename$)
    END IF
    EXIT SUB
 END IF

 ' check copy error flag
 IF Copy.Error THEN
    ' delete destination file
    IF Append.Dest = False THEN ' v4.2a 07/10/2007
       CALL DeleteDestFile(True, Next.Dest.Net$, Source.Filename$)
    END IF
    EXIT SUB
 END IF

 ' check to change source attribute
 Reset.Source = False
 IF RTRIM$(Source.Net) <> Nul THEN
    Reset.Source = True
 END IF
 IF Set.Source.Attribute OR Reset.Source THEN
    ' check windows dos
    IF Windows.Detected THEN
       ' reset source attribute
       InregsX.AX = &H7143
       InregsX.BX = &H1
       InregsX.CX = Source.Attribute
       InregsX.DS = VARSEG(ASCIIZ.Copy)
       InregsX.DX = VARPTR(ASCIIZ.Copy)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' reset source attribute
       InregsX.AX = &H4301
       InregsX.CX = Source.Attribute
       InregsX.DS = VARSEG(ASCIIZ.Copy)
       InregsX.DX = VARPTR(ASCIIZ.Copy)
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF

 ' increment bytes copied
 IF Resume.File THEN ' v4.3a 08/24/2007
    Var# = File.Size - Dest.File.Size
 ELSE
    IF Byte.Switch THEN
       Var# = File.Size - Byte.Override
    ELSE
       Var# = File.Size
    END IF
 END IF
 IF Var# < 0# THEN
    Var# = 0#
 END IF
 Total.Bytes = Total.Bytes + Var#

 ' check display type
 Var = False
 IF Double.Line >= 1 THEN
    ' display file date\time
    COLOR Green, Black
    PRINT FileInfo$ + " ";
    Var = True
 END IF

 ' check display type
 IF Double.Line >= 2 THEN
    ' display file size
    COLOR Red, Black
    PRINT Format$(File.Size, "#,##0;;0");
    Var = True
 END IF

 ' check display type
 IF Double.Line >= 3 THEN
    ' 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
    Var = True
 END IF
 IF Var THEN
    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(Next.Dest.Net$, Directory.Search$, Directory.Target$)
 ' declare subroutine variables
 DIM Wfile.Handle AS INTEGER
 DIM Attribute AS INTEGER

 ' make directory filename
 Temp.Net$ = RTRIM$(Source.Net)
 IF Temp.Net$ = Nul THEN
    ASCIIZ.Sub = Directory.Search$ + "*.*" + CHR$(0)
 ELSE
    IF LEFT$(Directory.Search$, 1) = "\" THEN
       ASCIIZ.Sub = Temp.Net$ + Directory.Search$ + "*.*" + CHR$(0)
    ELSE
       ASCIIZ.Sub = Temp.Net$ + "\" + Directory.Search$ + "*.*" + CHR$(0)
    END IF
 END IF

 ' find first long filename
 InregsX.AX = &H714E
 InregsX.CX = &H37
 InregsX.SI = &H1
 InregsX.DS = VARSEG(ASCIIZ.Sub)
 InregsX.DX = VARPTR(ASCIIZ.Sub)
 InregsX.ES = VARSEG(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(Next.Dest.Net$)
    END IF

    ' check directory exists
    IF Directory.Exists = False THEN

       ' search directory names
       CALL Filenames(Next.Dest.Net$, 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.FileBits)
             IF (Attribute AND &H10) = &H10 THEN

                ' store directory name
                Next.Search = CopyWDTA.ASCIIZfull
                Imbedded = INSTR(Next.Search, CHR$(0))
                IF Imbedded THEN
                   Next.Search = LEFT$(Next.Search, Imbedded - 1)
                END IF

                ' 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(Next.Dest.Net$, _
                      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(Next.Dest.Net$, Directory.Search$, Directory.Target$)
 ' declare subroutine variables
 DIM Wfile.Handle AS INTEGER
 DIM DTAfile AS DTAtype
 DIM Attribute AS INTEGER

 ' make directory filename
 Temp.Net$ = RTRIM$(Source.Net)
 IF Temp.Net$ = Nul THEN
    ASCIIZ.Sub = Directory.Search$ + "*.*" + CHR$(0)
 ELSE
    IF LEFT$(Directory.Search$, 1) = "\" THEN
       ASCIIZ.Sub = Temp.Net$ + Directory.Search$ + "*.*" + CHR$(0)
    ELSE
       ASCIIZ.Sub = Temp.Net$ + "\" + Directory.Search$ + "*.*" + CHR$(0)
    END IF
 END IF
 GOSUB Restore.DTA

 ' find first directory
 InregsX.AX = &H4E00
 InregsX.CX = &H37
 InregsX.DS = VARSEG(ASCIIZ.Sub)
 InregsX.DX = VARPTR(ASCIIZ.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(Next.Dest.Net$)
       GOSUB Restore.DTA
    END IF

    ' check directory exists
    IF Directory.Exists = False THEN

       ' search directory names
       CALL Filenames(Next.Dest.Net$, 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.FileBits)
             IF (Attribute AND &H10) = &H10 THEN

                ' store directory name
                Next.Search = DTAfile.ASCIIZfilename
                Imbedded = INSTR(Next.Search, CHR$(0))
                IF Imbedded THEN
                   Next.Search = LEFT$(Next.Search, Imbedded - 1)
                END IF

                ' 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(Next.Dest.Net$, _
                      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(Next.Dest.Net$, Directory.Search$)
 ' declare subroutine variables
 DIM DTAfile AS DTAtype
 DIM Wfile.Handle AS INTEGER

 ' make filename
 Temp.Net$ = RTRIM$(Source.Net)
 IF LEFT$(Filename.Search, 1) = "@" AND Enable.Redirect THEN ' v4.1a
    Var$ = Mid$(Filename.Search, 2)
 ELSE
    Var$ = Filename.Search
 END IF
 Var$ = Rtrim$(Var$)
 IF Temp.Net$ = Nul THEN
    ASCIIZ.File = Directory.Search$ + Var$ + CHR$(0)
 ELSE
    IF LEFT$(Directory.Search$, 1) = "\" THEN
       ASCIIZ.File = Temp.Net$ + Directory.Search$ + Var$ + CHR$(0)
    ELSE
       ASCIIZ.File = Temp.Net$ + "\" + Directory.Search$ + Var$ + CHR$(0)
    END IF
 END IF

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

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

       ' store filename info
       IF Windows.Detected = False THEN
          TempDTA = DTAfile
       END IF

       ' recursively call filelists
       IF LEFT$(Filename.Search$, 1) = "@" AND Enable.Redirect THEN
          Filelist.Filename$ = Ambiguate$(Filename$)

          ' check recursion level
          Max.Recurse = Max.Recurse + 1
          IF Max.Recurse >= RecurseLevel THEN
             OutregsX.Flags = &H1
             CALL DisplayError ("Recursive filelist error.")
             Quit.Searching = True
             Flag = False
             EXIT DO
          END IF

          ' reset prompt flag
          Flag = True

          ' concatenate work filename
          Var$ = Filename$ ' command line filename
          FOR Var = LEN(Var$) TO 1 STEP -1
             IF MID$(Var$, Var, 1) = "\" THEN
                Var$ = MID$(Var$, Var + 1)
                EXIT FOR
             END IF
          NEXT
          IF MID$(Var$, 2, 1) = ":" THEN
             Var$ = MID$(Var$, 3)
          END IF
          Filename2$ = Var$
          Var2$ = Filelist.Filename$ ' ambiguated filename
          FOR Var = LEN(Var2$) TO 1 STEP -1
             IF MID$(Var2$, Var, 1) = "\" THEN
                Var2$ = LEFT$(Var2$, Var)
                Var$ = Var2$ + Var$
                EXIT FOR
             END IF
          NEXT

          ' check filename
          CALL CheckFile(Filename2$,Flag,True)

          ' prompt for filelist
          IF Disable.Prompts = False THEN
             IF Flag THEN
                Prompt$ = "Use '" + Lcase$(Var$) + "' as filelist(y/n/q)?"
                CALL MorePrompt(Prompt$, "ynq", Outpt$, "n")
                IF BreakIS THEN
                   Outpt$ = "q"
                END IF
                SELECT CASE Outpt$
                CASE "q"
                   Quit.Searching = True
                   Flag = False
                CASE "n"
                   Flag = False
                END SELECT
             END IF
          END IF

          ' open work filename
          IF Flag THEN
             V = FREEFILE
             Data.Error = False
             Error.Flag = True
             OPEN Filelist.Filename$ FOR INPUT SHARED AS #V
             Error.Flag = False

             ' check error flag
             IF Data.Error THEN
                OutregsX.Flags = &H1
                CALL DisplayError ("Error opening filelist.")
             ELSE
                ' process input filenames
                DO UNTIL EOF(V)
                   LINE INPUT #V, Next.Filename$
                   Next.Filename$ = RTRIM$(Next.Filename$)
                   IF LEN(Next.Filename$) THEN
                      ' process search filename
                      Command.Work = Next.Filename$
                      CALL ProcessFilename(Quit.Searching)
                   END IF
                LOOP
                CLOSE #V
             END IF
          END IF
       ELSE
          Max.Recurse = 0
          Gosub GetFileSize
          CALL CopyFiles(Next.Dest.Net$, Directory.Search$, Filename$)
       END IF
    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

GetFileSize:
 ' 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
 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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       CALL InterruptX(&H21, InregsX, OutregsX)
       ' delete long filename
       InregsX.AX = &H7141
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       InregsX.SI = &H1
       InregsX.CX = &H27
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' set file attributes
       InregsX.AX = &H4301
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       InregsX.CX = &H0
       CALL InterruptX(&H21, InregsX, OutregsX)
       ' delete filename
       InregsX.AX = &H4100
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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(ASCIIZfile)
       InregsX.SI = VARPTR(ASCIIZfile)
       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 > False 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 > False 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(Dest.Net$)
 ' check file handle
 IF Append.Handle = False THEN
    EXIT SUB
 END IF

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

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

 ' reset file handle
 Append.Handle = False

 ' display any errors
 IF Debug.Mode THEN
    IF OutregsX.AX > False 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
       ' set extended filename date\time in windows
       IF Dest.Net$ = Nul THEN
          CALL ExtendedFile(4)
       END IF
    END IF
 END IF
END SUB

' routine to read 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(ASCIIZfile)
    InregsX.DX = VARPTR(ASCIIZfile)
    InregsX.ES = VARSEG(SDTAfile)
    InregsX.DI = VARPTR(SDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX

    ' store file size
    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))

    ' store file date and time
    Synch.File.Creation.Date = ASC(MID$(SDTAfile.CreateTime, 4, 1))
    Synch.File.Creation.Date = Synch.File.Creation.Date * &H100 + ASC(MID$(SDTAfile.CreateTime, 3, 1))
    Synch.File.Creation.Time = ASC(MID$(SDTAfile.CreateTime, 2, 1))
    Synch.File.Creation.Time = Synch.File.Creation.Time * &H100 + ASC(MID$(SDTAfile.CreateTime, 1, 1))
    Synch.File.Access.Date = ASC(MID$(SDTAfile.AccessTime, 4, 1))
    Synch.File.Access.Date = Synch.File.Access.Date * &H100 + ASC(MID$(SDTAfile.AccessTime, 3, 1))
    Synch.File.Modified.Date = ASC(MID$(SDTAfile.ModTime, 4, 1))
    Synch.File.Modified.Date = Synch.File.Modified.Date * &H100 + ASC(MID$(SDTAfile.ModTime, 3, 1))
    Synch.File.Modified.Time = ASC(MID$(SDTAfile.ModTime, 2, 1))
    Synch.File.Modified.Time = Synch.File.Modified.Time * &H100 + ASC(MID$(SDTAfile.ModTime, 1, 1))
    Synch.File.Modified.Time = Synch.File.Modified.Time AND NOT(&H1F) ' remove seconds

    ' close long filename search
    InregsX.AX = &H71A1
    InregsX.BX = Wfile.Handle
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' read file millisecond
    InregsX.AX = &H7143
    InregsX.BX = &H08
    InregsX.DS = VARSEG(ASCIIZfile)
    InregsX.DX = VARPTR(ASCIIZfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Synch.Milli = OutregsX.SI
    IF Synch.Milli >= 100 THEN
       Synch.File.Creation.Time = Synch.File.Creation.Time + .5!
    END IF
 ELSE
    ' find first filename
    InregsX.AX = &H4E00
    InregsX.CX = &H27
    InregsX.DS = VARSEG(ASCIIZfile)
    InregsX.DX = VARPTR(ASCIIZfile)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' store file size
    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))

    ' store file date and time
    Synch.File.Modified.Date = ASC(MID$(DTAfile.FileDate, 2, 1))
    Synch.File.Modified.Date = Synch.File.Modified.Date * &H100 + ASC(MID$(DTAfile.FileDate, 1, 1))
    Synch.File.Modified.Time = ASC(MID$(DTAfile.FileTime, 2, 1))
    Synch.File.Modified.Time = Synch.File.Modified.Time * &H100 + ASC(MID$(DTAfile.FileTime, 1, 1))
    Synch.File.Modified.Time = Synch.File.Modified.Time AND NOT(&H1F) ' remove seconds
 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
    ' read current directory
    InregsX.AX = &H7147
    InregsX.DX = ASC(UCASE$(Drive.Search)) - 64
    InregsX.DS = VARSEG(ASCIIZ.Command)
    InregsX.SI = VARPTR(ASCIIZ.Command)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' read current directory
    InregsX.AX = &H4700
    InregsX.DX = ASC(UCASE$(Drive.Search)) - 64
    InregsX.DS = VARSEG(ASCIIZ.Command)
    InregsX.SI = VARPTR(ASCIIZ.Command)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' store source netpath
 Source.Net = Nul
 IF LEFT$(Command.Work, 2) = "\\" THEN
    Command.Work = MID$(Command.Work, 3)
    Imbedded = INSTR(Command.Work, "\")
    IF Imbedded > 1 THEN
       Imbedded = INSTR(Imbedded + 2, Command.Work, "\")
       IF Imbedded THEN
          Source.Net = "\\" + LEFT$(Command.Work, Imbedded - 1)
          Command.Work = MID$(Command.Work, Imbedded + 1)
       END IF
    END IF
 END IF

 ' store current directory
 Directory.Search$ = Nul
 Imbedded = INSTR(ASCIIZ.Command, CHR$(0))
 IF Imbedded THEN
    Temp.Search$ = LEFT$(ASCIIZ.Command, Imbedded - 1)
 END IF
 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
   
 ' read 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)

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

    ' check search directory
    ASCIIZ.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(ASCIIZ.Command)
       InregsX.DX = VARPTR(ASCIIZ.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(ASCIIZ.Command)
       InregsX.DX = VARPTR(ASCIIZ.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.FileBits)
       ELSE
          Attribute = ASC(ProcessDTA.FileBits)
       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
    IF RTRIM$(Source.Net) = Nul THEN
       PRINT "Searching: " + Drive.Search + ":" + Directory.Search$ + RTRIM$(Filename.Search)
    ELSE
       IF LEFT$(Directory.Search$, 1) = "\" THEN
          PRINT "Searching: " + RTRIM$(Source.Net) + Directory.Search$ + RTRIM$(Filename.Search)
       ELSE
          PRINT "Searching: " + RTRIM$(Source.Net) + "\" + Directory.Search$ + RTRIM$(Filename.Search)
       END IF
    END IF
 END IF

 ' process destination netpaths
 FOR Count0 = 1 TO Number.Dest.Nets

    ' store netpath
    Next.Dest.Net$ = Destinate.Netpaths(Count0)
    Next.Dest.Net$ = Rtrim$(Next.Dest.Net$)

    ' 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
       IF Next.Dest.Net$ = Nul THEN
          GOSUB Store.Dest.Dir
       END IF

       ' 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/netpath
       IF Next.Dest.Net$ = Nul THEN
          Next.Dest.Dir$ = CHR$(Drive.Number) + ":" + Next.Dest.Dir$
       END IF

       ' 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(Next.Dest.Net$, Directory.Target$)
          END IF

          ' reset recurse flag
          Recurse.Error = False

          ' recursively copy files
          IF Windows.Detected THEN
             CALL WinDirectories(Next.Dest.Net$, Directory.Search$, Directory.Target$)
          ELSE
             CALL DosDirectories(Next.Dest.Net$, Directory.Search$, Directory.Target$)
          END IF

          ' close output file
          IF Append.Files THEN
             CALL CloseFile(Next.Dest.Net$)
          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
       IF Next.Dest.Net$ = Nul THEN
          GOSUB Restore.Dest.Dir
       END IF

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

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

    ' check netpaths
    IF Next.Dest.Net$ = Nul 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)

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

 ' store directory
 Imbedded = INSTR(DefaultASCIIZ, CHR$(0))
 IF Imbedded THEN
    Default.Dest = LEFT$(DefaultASCIIZ, Imbedded - 1)
 END IF
 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)

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

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

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

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

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

 ' 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(Dest.Net$, Source.File$, Destination.File$, Response)
 ' declare work variables
 DIM PromptASCIIZ 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
 IF Dest.Net$ = Nul THEN
    PromptASCIIZ = Destination.File$ + CHR$(0)
 ELSE
    IF LEFT$(Destination.File$, 1) = "\" THEN
       PromptASCIIZ = Dest.Net$ + Destination.File$ + CHR$(0)
    ELSE
       PromptASCIIZ = Dest.Net$ + "\" + Destination.File$ + CHR$(0)
    END IF
 END IF

 ' check windows dos
 IF Windows.Detected THEN
    ' find first long filename
    InregsX.AX = &H714E
    InregsX.CX = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(PromptASCIIZ)
    InregsX.DX = VARPTR(PromptASCIIZ)
    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(PromptASCIIZ)
    InregsX.DX = VARPTR(PromptASCIIZ)
    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 filename
    To.File$ = Destination.File$
    IF MID$(To.File$, 2, 1) = ":" THEN
       To.File$ = MID$(To.File$, 3)
    END IF

    ' make filename
    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$ = RTRIM$(From.File$)
       Temp.Net$ = RTRIM$(Source.Net)
       IF Temp.Net$ = Nul THEN
          Outpt$ = CHR$(Drive.Number) + ":" + Outpt$
       ELSE
          IF LEFT$(Outpt$, 1) = "\" THEN
             Outpt$ = Temp.Net$ + Outpt$
          ELSE
             Outpt$ = Temp.Net$ + "\" + Outpt$
          END IF
       END IF
       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$ = RTRIM$(To.File$)
          IF Dest.Net$ = Nul THEN
             Prompt$ = CHR$(Drive.Number) + ":" + Prompt$
          ELSE
             IF LEFT$(Prompt$, 1) = "\" THEN
                Prompt$ = Dest.Net$ + Prompt$
             ELSE
                Prompt$ = Dest.Net$ + "\" + Prompt$
             END IF
          END IF
	  IF Display.Lowercase THEN
	     Prompt$ = LCASE$(Prompt$)
	  END IF

	  ' make prompt
	  Prompt$ = "File " + Prompt$ + " exists. "
	  IF Append.Dest THEN
             IF Resume.File THEN
                Prompt$ = Prompt$ + "Resume"
             ELSE
                Prompt$ = Prompt$ + "Append"
             END IF
	  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(Copy.Net$)
 ' check for copying directory onto itself
 CALL DirectoryCopying(Copy.Net$)
 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
    IF Copy.Net$ = Nul THEN
       ASCIIZ.Dir = Filename$ + CHR$(0)
    ELSE
       ASCIIZ.Dir = Copy.Net$ + "\" + Filename$ + CHR$(0)
    END IF

    ' check windows dos
    IF Windows.Detected THEN
       ' create directory
       InregsX.AX = &H7139
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create directory
       InregsX.AX = &H3900
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.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!

    ' create filenames
    Old.File$ = Filename$
    New.File$ = Filename$
    CALL MakeFile(Copy.Net$, Old.File$, New.File$)

    ' check to display creating directory
    IF Continuous.Display = False THEN
       ' display directory 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
          END IF
       END IF
    END IF

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

    ' check windows dos
    IF Windows.Detected THEN
       ' read extended directory date\time
       CALL ExtendedFile(5)

       ' read directory attributes
       InregsX.AX = &H7143
       InregsX.BX = &H0
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' read directory attributes
       InregsX.AX = &H4300
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.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
    IF Copy.Net$ = Nul THEN
       ASCIIZ.Dir = MID$(Filename$, 3) + CHR$(0)
    ELSE
       ASCIIZ.Dir = Copy.Net$ + "\" + Filename$ + CHR$(0)
    END IF

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

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

' routine to create directory structure
SUB CreateDirectory(Next.Dest.Net$, 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))

 ' read current drive
 IF Next.Dest.Net$ = Nul THEN
    InregsX.AX = &H1900
    CALL InterruptX(&H21, InregsX, OutregsX)
    Current.Create = OutregsX.AX AND &HFF
 END IF

 ' make directory
 Directory$ = RTRIM$(Directory.Path$)
 IF Next.Dest.Net$ = Nul THEN
    Drive.Create = ASC(UCASE$(LEFT$(Directory$, 1)))
    Directory$ = MID$(Directory$, 3)
 END IF

 ' change to drive
 IF Next.Dest.Net$ = Nul THEN
    InregsX.AX = &HE00
    InregsX.DX = Drive.Create - 65
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF

 ' 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
    ASCIIZ.Dir = Filename$ + CHR$(0)
    IF LEN(Next.Dest.Net$) THEN
       ASCIIZ.Dir = Next.Dest.Net$ + "\" + ASCIIZ.Dir
    END IF

    ' check windows dos
    IF Windows.Detected THEN
       ' create directory
       InregsX.AX = &H7139
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.Dir)
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       ' create directory
       InregsX.AX = &H3900
       InregsX.DS = VARSEG(ASCIIZ.Dir)
       InregsX.DX = VARPTR(ASCIIZ.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)

          ' set extended directory date\time
          CALL ExtendedFile(6)
       END IF
    END IF
 LOOP

 ' restore drive
 IF Next.Dest.Net$ = Nul THEN
    InregsX.AX = &HE00
    InregsX.DX = Current.Create
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

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

 ' close output file
 IF Append.Files THEN
    CALL CloseFile(Work.Net$)
 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
    IF Work.Net$ = Nul THEN
       Source.File$ = CHR$(Drive.Number) + ":" + Start.Dir + Filename$
    ELSE
       Source.File$ = Work.Net$ + "\" + Start.Dir + Filename$
    END IF
 ELSE
    IF Work.Net$ = Nul THEN
       Source.File$ = CHR$(Drive.Number) + ":" + Start.Dir
    ELSE
       Source.File$ = Work.Net$ + "\" + Start.Dir
    END IF
 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
 ASCIIZ = Source.File$ + CHR$(0)
 IF Windows.Detected THEN
    InregsX.AX = &H7141
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.SI = &H1
    InregsX.CX = &H27
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    InregsX.AX = &H4100
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.CX = &H27
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

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

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

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

 ' 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
    Prompt$ = "Delete copied source 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
    ASCIIZ = Source.File$ + CHR$(0)
    IF Windows.Detected THEN
       InregsX.AX = &H7141
       InregsX.DS = VARSEG(ASCIIZ)
       InregsX.DX = VARPTR(ASCIIZ)
       InregsX.SI = &H1
       InregsX.CX = &H27
       CALL InterruptX(&H21, InregsX, OutregsX)
    ELSE
       InregsX.AX = &H4100
       InregsX.DS = VARSEG(ASCIIZ)
       InregsX.DX = VARPTR(ASCIIZ)
       InregsX.CX = &H27
       CALL InterruptX(&H21, InregsX, OutregsX)
    END IF
 END IF
END SUB

' subroutine to delete subdirectory
SUB DeleteSubdirectory(Source.Directory$)
 ' declare work variables
 DIM PromptASCIIZ 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(Nul, 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 source 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 ASCIIZ AS STRING * 260
 DIM Wfile.Handle AS INTEGER

 ' make directory filename
 ASCIIZ = 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(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.ES = VARSEG(TreeWDTA)
    InregsX.DI = VARPTR(TreeWDTA)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX
 ELSE
    ' find first directory
    InregsX.AX = &H4E00
    InregsX.CX = &H37
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    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.FileBits)
       ELSE
          Attribute = ASC(DTAfile.FileBits)
       END IF
       IF (Attribute AND &H10) = &H10 THEN
	   ' store directory name
           IF Windows.Detected THEN
              Directory$ = TreeWDTA.ASCIIZfull
           ELSE
              Directory$ = DTAfile.ASCIIZfilename
           END IF
           Imbedded = INSTR(Directory$, CHR$(0))
           IF Imbedded THEN
              Directory$ = LEFT$(Directory$, Imbedded - 1)
           END IF

	   ' 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

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

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

 ' check windows dos
 IF Windows.Detected THEN
    ' make filename
    ASCIIZ = Directory$ + "*.*" + CHR$(0)
    InregsX.AX = &H7141
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    InregsX.SI = &H1
    InregsX.CX = &H27
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' change to directory
    InregsX.AX = &H3B00
    InregsX.DS = VARSEG(ASCIIZ)
    InregsX.DX = VARPTR(ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)

    ' 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 a directory
SUB DeleteDirectory(Directory$)
 ' declare subroutine variables
 DIM ASCIIZ AS STRING * 260

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

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

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

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

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

' verifies duplicate filenames,
' displays filenames being copied.
SUB FilenameCopying(Copy.Net$, From$, To$)
 ' declare subroutine variables
 DIM ASCIIZ1 AS STRING * 260
 DIM ASCIIZ2 AS STRING * 261

 ' reset flag
 Filename.Exists = False

 ' make filenames
 From.File$ = From$
 To.File$ = To$
 CALL MakeFile(Copy.Net$, 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

 ' make source netpath
 IF Copy.Net$ <> Nul THEN
    From.Filename$ = From$
    Call MakeFile2(From.Filename$)
 END IF

 ' compare filenames
 IF LCASE$(From.Filename$) = LCASE$(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 filename being copied
Display.File:
 ' ambiguate filename
 IF Display.Wide OR Ambiguate.Display THEN
    IF Windows.Detected THEN
       ' read windows 8.3 filename
       ASCIIZ1 = From.File$ + CHR$(0)
       InregsX.AX = &H7160
       InregsX.CX = &H8001
       InregsX.DS = VARSEG(ASCIIZ1)
       InregsX.SI = VARPTR(ASCIIZ1)
       InregsX.ES = VARSEG(ASCIIZ2)
       InregsX.DI = VARPTR(ASCIIZ2)
       CALL InterruptX(&H21, InregsX, OutregsX)

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

          ' store windows 8.3 filename
          From.File$ = ASCIIZ2
          Imbedded = INSTR(From.File$, CHR$(0))
          IF Imbedded THEN
             From.File$ = LEFT$(From.File$, Imbedded - 1)
          END IF
       END IF
    END IF
 END IF

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

 ' truncate pathname
 IF Display.Path OR Display.Wide 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

 ' truncate drive letter
 IF Drive.Letter THEN
    IF MID$(From.File$, 2, 1) = ":" THEN
       From.File$ = MID$(From.File$, 3)
    END IF
    IF MID$(To.File$, 2, 1) = ":" THEN
       To.File$ = MID$(To.File$, 3)
    END IF
 END IF

 ' store filename
 COLOR Yellow, Black
 Outpt$ = RTRIM$(From.File$)

 ' truncate drive letter
 IF Copy.Net$ <> Nul THEN
    IF MID$(Outpt$, 2, 1) = ":" THEN
       Outpt$ = MID$(Outpt$, 3)
    END IF
 END IF

 ' display filename in wide list format
 IF Display.Wide THEN
    ' check filename
    IF LEN(Outpt$) > 14 THEN
       Outpt$ = LEFT$(Outpt$, 13) + "~"
    END IF
    ' display filename in wide format
    PRINT Outpt$ + SPACE$(15 - LEN(Outpt$));
    ' count wide display filenames
    Wide.List = Wide.List + 1
    IF Wide.List = 5 THEN
       Wide.List = False
       PRINT
    END IF
    RETURN
 END IF

 ' prepend copying type
 IF Short.Display2 = False THEN
    IF Append.Dest THEN
       IF Resume.File THEN
          Outpt$ = "Resuming: " + Outpt$
       ELSE
          Outpt$ = "Appending: " + Outpt$
       END IF
    ELSE
       Outpt$ = "Copying: " + Outpt$
    END IF
 END IF

 ' display only source filename
 IF Short.Display THEN
    PRINT Outpt$
 ELSE
    ' display source/destination filenames
    Outpt$ = Outpt$ + " - " + To.File$
    PRINT Outpt$
 END IF
 RETURN
END SUB

' subroutine to execute DOS command
SUB ExecuteCommand(Next.Command$, DOS.Filename$)
 DIM ASCIIZ 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
 IF LEFT$(DOS.Filename$, 2) = "\\" THEN
    Parameters(1) = "\\"
    Parameters(2) = "\\"
    Parameters(3) = DOS.Pathname$
    Parameters(4) = DOS.Pathname$ + "\"
    Parameters(5) = DOS.Filename$
 ELSE
    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$
 END IF
 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
 ASCIIZ = DOS.Pathname$ + CHR$(0)

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

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

 ' replace parameters (fixed v3.9a 02/28/2006)
 Count = 1
 Do
    IF Count >= Len(Execute.Command$) THEN
       EXIT DO
    END IF
    Imbed$ = Mid$(Execute.Command$, Count, 1)
    IF Imbed$ = "%" THEN
       Imbed2$ = Mid$(Execute.Command$, Count + 1, 1)
       Select Case Ucase$(Imbed2$)
       Case "1" To "9", "A" To "C"
          Count2 = Val("&H" + Imbed2$)
          Execute.Command$ = LEFT$(Execute.Command$, Count - 1) + _
          RTRIM$(Parameters(Count2)) + MID$(Execute.Command$, Count + 2)
          Count = Count + LEN(RTRIM$(Parameters(Count2)))
       Case ELSE
          Count = Count + 1
       End Select
    ELSE
       Count = Count + 1
    END IF
 Loop

 ' 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
    ' check display counter
    IF Wide.List THEN
       Wide.List = False
       PRINT
    END IF

    ' read command path from environment
    Comspec$ = Environ$("COMSPEC")
    IF LEN(Comspec$) THEN
       ' call shell routine
       Shell.Command$ = Comspec$ + " /E:4096 /C " + Execute.Command$
       SHELL Shell.Command$
    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)

 ' 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.ASCIIZ)
    InregsX.DX = VARPTR(Directory.ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 ELSE
    ' restore current directory
    InregsX.AX = &H3B00
    InregsX.DS = VARSEG(Directory.ASCIIZ)
    InregsX.DX = VARPTR(Directory.ASCIIZ)
    CALL InterruptX(&H21, InregsX, OutregsX)
 END IF
END SUB

REM -eof-
