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

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

' command line switch parser function
FUNCTION ParseLine(X$)
 Imbedded = INSTR(Command.Line, LCASE$(X$))
 IF Imbedded THEN
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + LEN(X$))
    ParseLine = True
 ELSE
    Imbedded = INSTR(Command.Line, UCASE$(X$))
    IF Imbedded THEN
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + LEN(X$))
       ParseLine = True
    ELSE
       ParseLine = False
    END IF
 END IF
END FUNCTION 

' routine parses command line for switches, filenames, and additional info
SUB ReadSwitches
 ' check command line
 SELECT CASE COMMAND$
 CASE "/?"
    CALL BootUsage
 END SELECT

 ' read command line from PSP
 Command.line = NUL
 InregsX.AX = &H6200
 CALL InterruptX(&H21, InregsX, OutregsX)
 PSPsegment = OutregsX.BX
 PSPoffset = 128
 DEF SEG = PSPsegment
 FOR Count = 1 TO 127
    Command.Char = PEEK(PSPoffset + Count)
    SELECT CASE Command.Char
    CASE 0, 10, 13
       EXIT FOR
    CASE ELSE
       Command.line = Command.line + CHR$(Command.Char)
   END SELECT
 NEXT
 DEF SEG

 ' get dos command from end of command line
 Max.Commands = 10
 Number.Commands = False
 GOSUB ReadDosCommand

 ' concatenate environment variable
 Command.Line = Command.Line + ENVIRON$("COPYIT")

 ' get dos command from end of command line
 GOSUB ReadDosCommand

 ' search for dos utils path
 File.Found = False
 Dosutils$ = CURDIR$

 ' store dos utils path
 IF RIGHT$(Dosutils$, 1) <> "\" THEN
    Dosutils$ = Dosutils$ + "\"
 END IF

 ' read config switch
 Ignore.Config = ParseLine("/.")

 ' search for copyit config file
 Filename$ = Dosutils$ + "COPYIT.CFG"
 IF DIR$(Filename$) <> NUL THEN
    GOSUB ReadConfig
 END IF

 ' search for dos utils path
 IF File.Found = False THEN
    Dosutils$ = ENVIRON$("DOSUTILS")
    IF LEN(Dosutils$) THEN
       ' store dos utils path
       IF RIGHT$(Dosutils$, 1) <> "\" THEN
          Dosutils$ = Dosutils$ + "\"
       END IF
       ' search for copyit config file
       Filename$ = Dosutils$ + "COPYIT.CFG"
       IF DIR$(Filename$) <> NUL THEN
          GOSUB ReadConfig
       END IF
    END IF
 END IF

 ' search path for config file
 IF File.Found = False THEN
    Dosutils$ = ENVIRON$("PATH")
    IF Dosutils$ <> NUL THEN
       DO
          Imbedded = INSTR(Dosutils$, ";")
          IF Imbedded THEN
             Pathname$ = LEFT$(Dosutils$, Imbedded - 1)
             Dosutils$ = MID$(Dosutils$, Imbedded + 1)
          ELSE
             Pathname$ = Dosutils$
             Dosutils$ = NUL
          END IF
          IF LEN(Pathname$) THEN
             IF RIGHT$(Pathname$, 1) <> "\" THEN
                Pathname$ = Pathname$ + "\"
             END IF
             ' search for copyit config file
             Filename$ = Pathname$ + "COPYIT.CFG"
             IF DIR$(Filename$) <> NUL THEN
                GOSUB ReadConfig
                EXIT DO
             END IF
          ELSE
             EXIT DO
          END IF
       LOOP
    END IF
 END IF

 ' get dos command from end of command line
 GOSUB ReadDosCommand

 ' edit special dos command parameters
 FOR Count = 1 TO Number.Commands
    Next.Command$ = DOS.Command(Count)
    Imbedded = INSTR(Next.Command$, "//1")
    WHILE Imbedded
       Next.Command$ = LEFT$(Next.Command$, Imbedded - 1) + ">" + MID$(Next.Command$, Imbedded + 3)
       Imbedded = INSTR(Next.Command$, "//1")
    WEND
    Imbedded = INSTR(Next.Command$, "//2")
    WHILE Imbedded
       Next.Command$ = LEFT$(Next.Command$, Imbedded - 1) + "<" + MID$(Next.Command$, Imbedded + 3)
       Imbedded = INSTR(Next.Command$, "//2")
    WEND
    Imbedded = INSTR(Next.Command$, "//3")
    WHILE Imbedded
       Next.Command$ = LEFT$(Next.Command$, Imbedded - 1) + "|" + MID$(Next.Command$, Imbedded + 3)
       Imbedded = INSTR(Next.Command$, "//3")
    WEND
    Imbedded = INSTR(Next.Command$, "//4")
    WHILE Imbedded
       Next.Command$ = LEFT$(Next.Command$, Imbedded - 1) + "%" + MID$(Next.Command$, Imbedded + 3)
       Imbedded = INSTR(Next.Command$, "//4")
    WEND
    DOS.Command(Count) = Next.Command$
 NEXT

 ' read attribute switches
 Set.Source.Archive = ParseLine("///XA")
 Set.Source.Hidden = ParseLine("///XH")
 Set.Source.Readonly = ParseLine("///XO")
 Set.Source.System = ParseLine("///XS")
 Set.Source.Any = ParseLine("///XX")

 Clear.Source.Archive = ParseLine("///ZA")
 Clear.Source.Hidden = ParseLine("///ZH")
 Clear.Source.Readonly = ParseLine("///ZO")
 Clear.Source.System = ParseLine("///ZS")
 Clear.Source.Any = ParseLine("///ZX")

 Set.Dest.Archive = ParseLine("//XA")
 Set.Dest.Hidden = ParseLine("//XH")
 Set.Dest.Readonly = ParseLine("//XO")
 Set.Dest.System = ParseLine("//XS")
 Set.Dest.Any = ParseLine("//XX")

 Clear.Dest.Archive = ParseLine("//ZA")
 Clear.Dest.Hidden = ParseLine("//ZH")
 Clear.Dest.Readonly = ParseLine("//ZO")
 Clear.Dest.System = ParseLine("//ZS")
 Clear.Dest.Any = ParseLine("//ZX")

 Display.Archive = ParseLine("/XA")
 Display.Hidden = ParseLine("/XH")
 Display.Readonly = ParseLine("/XO")
 Display.System = ParseLine("/XS")
 Display.Any = ParseLine("/XX")

 No.Display.Archive = ParseLine("/ZA")
 No.Display.Hidden = ParseLine("/ZH")
 No.Display.Readonly = ParseLine("/ZO")
 No.Display.System = ParseLine("/ZS")
 No.Display.Any = ParseLine("/ZX")

 ' read synchronize switches
 Synch.Files1 = ParseLine("/A1")
 Synch.Files2 = ParseLine("/A2")
 Synch.Files3 = ParseLine("/A3")
 Synch.Files4 = ParseLine("/A4")
 Synch.Files5 = ParseLine("/A5")
 Synch.Files6 = ParseLine("/A6")
 Synch.Files7 = ParseLine("/A7")
 Synch.Files8 = ParseLine("/A8")
 Synch.Files9 = ParseLine("/A9")
 Synch.FilesA = ParseLine("/AA")
 Synch.FilesB = ParseLine("/AB")
 Synch.FilesC = ParseLine("/AC")

 ' read remaining switches
 Synch.Files = ParseLine("/A")
 Copy.Directory = ParseLine("/B1")
 Zero.Nest = ParseLine("/B2")
 IF Zero.Nest THEN
    Copy.Directory = True
 END IF
 Continuous.Display = ParseLine("/C")
 Drive.Letter = ParseLine("/H")
 Delete.Copied = ParseLine("/I")
 Force.Copy = ParseLine("/J")
 Delete.Directory = ParseLine("/K")
 Double.Line = ParseLine("/L")
 Dont.Overwrite = ParseLine("/M")
 Prompt.Delete1 = ParseLine("/N")
 Display.Wide = ParseLine("/O")
 Prompt.Insert = ParseLine("/P")
 Prompt.Delete2 = ParseLine("/Q")
 Recurse.Directories = ParseLine("/R")
 Display.Path = ParseLine("/S")
 Append.Dest = ParseLine("/V")
 Overwrite.Prompt = ParseLine("/W")
 Short.Display = ParseLine("/X")
 Display.Lowercase = ParseLine("/Y")
 Display.Errors = ParseLine("/Z")
 Control.Break = ParseLine ("/~")
 IF Prompt.Insert THEN
    Force.Copy = True
 END IF

 ' parse progress switches
 Progress.Bar = ParseLine("/*")
 Percent.Display = ParseLine("/!")
 Dot.Mode = ParseLine("/-")
 IF Percent.Display THEN
    Dot.Mode = True
 END IF
 IF Progress.Bar THEN
    Percent.Display = False
    Dot.Mode = True
 END IF
 Rate.Mode = ParseLine("/+")

 ' read debug mode switch
 Debug.Mode = ParseLine("/=")

 ' set attribute display variable
 Display.Attribute = False
 IF Display.Archive OR No.Display.Archive THEN
    Display.Attribute = True
 END IF
 IF Display.Hidden OR No.Display.Hidden THEN
    Display.Attribute = True
 END IF
 IF Display.Readonly OR No.Display.Readonly THEN
    Display.Attribute = True
 END IF
 IF Display.System OR No.Display.System THEN
    Display.Attribute = True
 END IF
 IF Display.Any OR No.Display.Any THEN
    Display.Attribute = True
 END IF

 ' set source attribute flag
 Set.Source.Attribute = False
 IF Clear.Source.Archive OR Set.Source.Archive THEN
    Set.Source.Attribute = True
 END IF
 IF Clear.Source.Hidden OR Set.Source.Hidden THEN
    Set.Source.Attribute = True
 END IF
 IF Clear.Source.Readonly OR Set.Source.Readonly THEN
    Set.Source.Attribute = True
 END IF
 IF Clear.Source.System OR Set.Source.System THEN
    Set.Source.Attribute = True
 END IF
 IF Clear.Source.Any OR Set.Source.Any THEN
    Set.Source.Attribute = True
 END IF

 ' reset file counter variables
 Error.Level = False
 Dirs.Counted = 0!
 Files.Counted = 0#
 More.Display = False
 Total.Deleted = False

 ' get date from command line
 Search.Date = False
 Search.From.Date = False
 Search.To.Date = False
 Imbedded = INSTR(UCASE$(Command.Line), "/E")
 IF Imbedded THEN
    Search.Date = True
    ' /e01/01/1997-01/01/1997
    IF MID$(Command.Line, Imbedded + 4, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 7, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 12, 1) = "-" AND _
    MID$(Command.Line, Imbedded + 15, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 18, 1) = "/" THEN
       D$ = MID$(Command.Line, Imbedded + 2, 21)
       S$ = LEFT$(D$, 10)
       D1! = INT(VAL(MID$(S$, 1, 2))) ' month
       D2! = INT(VAL(MID$(S$, 4, 2))) ' day
       D3! = INT(VAL(MID$(S$, 7, 4))) ' year
       Search.From.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
       S$ = RIGHT$(D$, 10)
       D1! = INT(VAL(MID$(S$, 1, 2))) ' month
       D2! = INT(VAL(MID$(S$, 4, 2))) ' day
       D3! = INT(VAL(MID$(S$, 7, 4))) ' year
       Search.To.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 23)
    ELSE
       ' /e-01/01/1997
       IF MID$(Command.Line, Imbedded + 2, 1) = "-" AND _
       MID$(Command.Line, Imbedded + 5, 1) = "/" AND _
       MID$(Command.Line, Imbedded + 8, 1) = "/" THEN
          Search.From.Date = False
          S$ = MID$(Command.Line, Imbedded + 3, 10)
          D1! = INT(VAL(MID$(S$, 1, 2))) ' month
          D2! = INT(VAL(MID$(S$, 4, 2))) ' day
          D3! = INT(VAL(MID$(S$, 7, 4))) ' year
          Search.To.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
          Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 13)
       ELSE
          ' /e01/01/1997-
          IF MID$(Command.Line, Imbedded + 4, 1) = "/" AND _
          MID$(Command.Line, Imbedded + 7, 1) = "/" AND _
          MID$(Command.Line, Imbedded + 12, 1) = "-" THEN
             Search.To.Date = False
             S$ = MID$(Command.Line, Imbedded + 2, 10)
             D1! = INT(VAL(MID$(S$, 1, 2))) ' month
             D2! = INT(VAL(MID$(S$, 4, 2))) ' day
             D3! = INT(VAL(MID$(S$, 7, 4))) ' year
             Search.From.Date = ((D3! - 1980) * 512) + D1! * 32 + D2!
             Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 13)
          ELSE
             Boot.List$ = "Error specifying search date."
             GOTO Boot.Error
          END IF
       END IF
    END IF
    IF Search.From.Date < False OR Search.To.Date < False THEN
       Boot.List$ = "Error specifying search date."
       GOTO Boot.Error
    END IF
 END IF

 ' get time from command line
 Search.Time = False
 Search.From.Time = False
 Search.To.Time = False
 Imbedded = INSTR(UCASE$(Command.Line), "/T")
 IF Imbedded THEN
    Search.Time = True
    ' /e12:00:00-12:00:00
    IF MID$(Command.Line, Imbedded + 4, 1) = ":" AND _
    MID$(Command.Line, Imbedded + 7, 1) = ":" AND _
    MID$(Command.Line, Imbedded + 10, 1) = "-" AND _
    MID$(Command.Line, Imbedded + 13, 1) = ":" AND _
    MID$(Command.Line, Imbedded + 16, 1) = ":" THEN
       D$ = MID$(Command.Line, Imbedded + 2, 17)
       S$ = LEFT$(D$, 8)
       T1! = INT(VAL(MID$(S$, 1, 2))) ' hours
       T2! = INT(VAL(MID$(S$, 4, 2))) ' minutes
       T3! = INT(VAL(MID$(S$, 7, 2))) ' seconds
       Search.From.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
       S$ = RIGHT$(D$, 8)
       T1! = INT(VAL(MID$(S$, 1, 2))) ' hours
       T2! = INT(VAL(MID$(S$, 4, 2))) ' minutes
       T3! = INT(VAL(MID$(S$, 7, 2))) ' seconds
       Search.To.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 19)
    ELSE
       ' /e-12:00:00
       IF MID$(Command.Line, Imbedded + 2, 1) = "-" AND _
       MID$(Command.Line, Imbedded + 5, 1) = ":" AND _
       MID$(Command.Line, Imbedded + 8, 1) = ":" THEN
          Search.From.Time = False
          S$ = MID$(Command.Line, Imbedded + 3, 8)
          T1! = INT(VAL(MID$(S$, 1, 2))) ' hours
          T2! = INT(VAL(MID$(S$, 4, 2))) ' minutes
          T3! = INT(VAL(MID$(S$, 7, 4))) ' seconds
          Search.To.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
          Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 11)
       ELSE
          ' /e12:00:00-
          IF MID$(Command.Line, Imbedded + 4, 1) = ":" AND _
          MID$(Command.Line, Imbedded + 7, 1) = ":" AND _
          MID$(Command.Line, Imbedded + 10, 1) = "-" THEN
             Search.To.Time = False
             S$ = MID$(Command.Line, Imbedded + 2, 10)
             T1! = INT(VAL(MID$(S$, 1, 2))) ' hours
             T2! = INT(VAL(MID$(S$, 4, 2))) ' minutes
             T3! = INT(VAL(MID$(S$, 7, 4))) ' seconds
             Search.From.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
             Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 11)
          ELSE
             Boot.List$ = "Error specifying search time."
             GOTO Boot.Error
          END IF
       END IF
    END IF
    IF Search.From.Time < False OR Search.To.Time < False THEN
       Boot.List$ = "Error specifying search time."
       GOTO Boot.Error
    END IF
 END IF

 ' get file size from command line
 Search.File.Size = False
 Search.Size.From = 0#
 Search.Size.To = 0#
 Imbedded = INSTR(UCASE$(Command.Line), "/U")
 IF Imbedded THEN
    Search.File.Size = True
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    Search.Size.From = Var#
    IF MID$(Command.Line, Imbedded, 1) <> "-" THEN
       Boot.List$ = "Error specifying search size."
       GOTO Boot.Error
    END IF
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 1)
    CALL GetNumeric(Var#)
    Search.Size.To = Var#
    Search.Size.From = Search.Size.From * 1024#
    Search.Size.To = Search.Size.To * 1024#
 END IF

 ' get dest. file date over-ride
 New.Date.Month = 0
 New.Date.Day = 0
 New.Date.Year = 0
 Override.Date = 0
 Imbedded = INSTR(Command.Line, "/5")
 IF Imbedded THEN
    Date.Override$ = MID$(Command.Line, Imbedded + 2, 10)
    IF MID$(Date.Override$, 3, 1) <> "-" THEN
       Boot.List$ = "Error specifying over-ride date."
       GOTO Boot.Error
    END IF
    IF MID$(Date.Override$, 6, 1) <> "-" THEN
       Boot.List$ = "Error specifying over-ride date."
       GOTO Boot.Error
    END IF
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 12)
    New.Date.Month = INT(VAL(MID$(Date.Override$, 1, 2)))
    New.Date.Day = INT(VAL(MID$(Date.Override$, 4, 2)))
    New.Date.Year = INT(VAL(MID$(Date.Override$, 7, 4)))
    Override.Date = VAL("&H" + HEX$((New.Date.Year - 1980) * 512))
    Override.Date = Override.Date + New.Date.Month * 32
    Override.Date = Override.Date + New.Date.Day
 END IF

 ' get dest. file time over-ride
 New.Time.Hour = 0
 New.Time.Minute = 0
 New.Time.Second = 0
 Override.Time = 0
 Imbedded = INSTR(Command.Line, "/6")
 IF Imbedded THEN
    Time.Override$ = MID$(Command.Line, Imbedded + 2, 8)
    IF MID$(Time.Override$, 3, 1) <> "-" THEN
       Boot.List$ = "Error specifying over-ride time."
       GOTO Boot.Error
    END IF
    IF MID$(Time.Override$, 6, 1) <> "-" THEN
       Boot.List$ = "Error specifying over-ride time."
       GOTO Boot.Error
    END IF
    File.Time$ = TIME$
    HourTemp = VAL(MID$(File.Time$, 1, 2))
    MinuteTemp = VAL(MID$(File.Time$, 4, 2))
    SecondTemp = VAL(MID$(File.Time$, 7, 2))
    Millisecond = CINT(ABS(TIMER - CSNG(HourTemp * 3600 + MinuteTemp * 60 + SecondTemp)) * 100)
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 10)
    New.Time.Hour = INT(VAL(MID$(Time.Override$, 1, 2)))
    New.Time.Minute = INT(VAL(MID$(Time.Override$, 4, 2)))
    New.Time.Second = INT(VAL(MID$(Time.Override$, 7, 2)))
    Override.Time = VAL("&H" + HEX$(New.Time.Hour * 2048))
    Override.Time = Override.Time + New.Time.Minute * 32
    Override.Time = Override.Time + INT(New.Time.Second / 2)
    Millisecond = CINT(ABS(TIMER - CSNG(HourTemp * 3600 + MinuteTemp * 60 + SecondTemp)) * 100)
    IF INT(SecondTemp/2) <> SecondTemp/2 THEN
       Millisecond = Millisecond + 100
    END IF
    IF Millisecond > 199 THEN
       Millisecond = 199
    END IF
 END IF

 ' read more switches
 Copy.Zero = ParseLine("/0")
 Copy.Ascii = ParseLine("/1")
 Create.Time = ParseLine("/7")
 Access.Time = ParseLine("/8")
 Modify.Time = ParseLine("/9")
 IF Create.Time = False THEN
    IF Access.Time = False THEN
       IF Modify.Time = False THEN
          Modify.Time = True
       END IF
    END IF
 END IF

 ' get ascii replacement filters from command line
 Ascii.Filters = False
 Max.Filters = 10
 DO
    Imbedded = INSTR(Command.Line, "/2")
    IF Imbedded = False THEN
       EXIT DO
    END IF
    Ascii.Filters = Ascii.Filters + 1
    IF Ascii.Filters > Max.Filters THEN
       Max.Filters = Max.Filters + 10
       REDIM PRESERVE Convert.Ascii(1 TO Max.Filters, 1 TO 2) AS INTEGER
    END IF
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    Ascii.From = CINT(Var#)
    IF MID$(Command.Line, Imbedded, 1) <> "-" THEN
       Boot.List$ = "Error specifying search filter."
       GOTO Boot.Error
    END IF
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 1)
    CALL GetNumeric(Var#)
    Ascii.To = CINT(Var#)
    Convert.Ascii(Ascii.Filters, 1) = Ascii.From
    Convert.Ascii(Ascii.Filters, 2) = Ascii.To
 LOOP

 ' get ascii strip filters from command line
 Ascii.Strips = False
 Max.Strips = 10
 DO
    Imbedded = INSTR(Command.Line, "/3")
    IF Imbedded = False THEN
       EXIT DO
    END IF
    Ascii.Strips = Ascii.Strips + 1
    IF Ascii.Strips > Max.Strips THEN
       Max.Strips = Max.Strips + 10
       REDIM PRESERVE Strip.Ascii(1 TO Max.Strips) AS INTEGER
    END IF
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    Strip.Ascii(Ascii.Strips) = CINT(Var#)
 LOOP

 ' get nest over-ride switch
 Imbedded = INSTR(Command.Line, "/4")
 IF Imbedded THEN
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    Nested.Recurse = CINT(Var#)
 END IF

 ' get excluded filename specs from command line
 Max.Excluded = 10
 Number.Excluded = False
 DO
    Excluded = INSTR(Command.Line, "/(")
    IF Excluded = False THEN
       EXIT DO
    END IF
    FOR Next.Bracket = Excluded + 2 TO LEN(Command.Line)
       IF MID$(Command.Line, Next.Bracket, 1) = ")" THEN
          Excluded.Spec$ = MID$(Command.Line, Excluded + 2, Next.Bracket - Excluded - 2)
          Command.Line = LEFT$(Command.Line, Excluded - 1) + MID$(Command.Line, Next.Bracket + 1)
          IF LEN(Excluded.Spec$) THEN
             Number.Excluded = Number.Excluded + 1
             IF Number.Excluded > Max.Excluded THEN
                Max.Excluded = Max.Excluded + 10
                REDIM PRESERVE Excluded.Files(1 TO Max.Excluded) AS STRING * 12
             END IF
             Excluded.Files(Number.Excluded) = UCASE$(Excluded.Spec$)
             EXIT FOR
          END IF
       END IF
    NEXT
 LOOP

 ' get remaining filename switches
 Dest.Dir = NUL
 Dest.File = NUL
 Temp.Drive.Dir = NUL
 Number.Dest.Dirs = False
 Number.Dest.Files = False
 Max.Dest.Dirs = 10
 Max.Dest.Files = 10
 Command.Line = RTRIM$(Command.Line)
 DO
    IF INSTR(Command.Line, "/") = False THEN
       EXIT DO
    END IF
    FOR Count = LEN(Command.Line) TO 1 STEP -1
       IF MID$(Command.Line, Count, 1) = "/" THEN
          Switch$ = MID$(Command.Line, Count + 1, 1)
          Switch.Line$ = MID$(Command.Line, Count + 2)
          Switch.Line$ = LTRIM$(Switch.Line$)
          Switch.Line$ = RTRIM$(Switch.Line$)
          IF RIGHT$(Switch.Line$, 1) = CHR$(34) AND LEFT$(Switch.Line$, 1) = CHR$(34) THEN
             Switch.Line$ = MID$(Switch.Line$, 2)
             Switch.Line$ = LEFT$(Switch.Line$, LEN(Switch.Line$) - 1)
          END IF
          SELECT CASE UCASE$(Switch$)
          CASE "G"
             Temp.Drive.Dir = Switch.Line$
          CASE "F"
             Number.Dest.Files = Number.Dest.Files + 1
             IF Number.Dest.Files > Max.Dest.Files THEN
                Max.Dest.Files = Max.Dest.Files + 10
                REDIM PRESERVE Destinate.Filename(1 TO Max.Dest.Files) AS STRING * 260
             END IF
             Destinate.Filename(Number.Dest.Files) = Switch.Line$
             Dest.File = Switch.Line$
          CASE "D"
             Number.Dest.Dirs = Number.Dest.Dirs + 1
             IF Number.Dest.Dirs > Max.Dest.Dirs THEN
                Max.Dest.Dirs = Max.Dest.Dirs + 10
                REDIM PRESERVE Destinate.Directory(1 TO Max.Dest.Dirs) AS STRING * 260
             END IF
             Destinate.Directory(Number.Dest.Dirs) = Switch.Line$
          CASE ELSE
             EXIT DO
          END SELECT
          Command.Line = LEFT$(Command.Line, Count - 1)
          Command.Line = RTRIM$(Command.Line)
          EXIT FOR
       END IF
    NEXT
 LOOP

 ' get temporary drive
 IF Temp.Drive.Dir = NUL THEN
    Temp$ = ENVIRON$("TEMP")
    IF LEN(Temp$) THEN
       Temp.Drive.Dir = Temp$
    ELSE
       Temp$ = ENVIRON$("TMP")
       IF LEN(Temp$) THEN
          Temp.Drive.Dir = Temp$
       END IF
    END IF
 END IF

 ' check temporary drive
 IF Prompt.Insert OR Force.Copy THEN
    IF RTRIM$(Temp.Drive.Dir) = NUL THEN
       Boot.List$ = "Error specifying temporary drive."
       GOTO Boot.Error
    END IF
 END IF

 ' recheck command line
 IF INSTR(Command.Line, "/") THEN
    Boot.List$ = "Unknown / switch."
    GOTO Boot.Error
 END IF

 ' check destinations
 IF Number.Dest.Dirs = False THEN
    IF Number.Dest.Files = False THEN
       Boot.List$ = "Missing destination copy filenames."
       GOTO Boot.Error
    END IF
 END IF

 ' reset work variables
 Copy.Target = NUL
 Continue.Searching = False
 Nested.Levels = False
 Quit.Searching = False
   
 ' store remaining command line
 Command.Line = RTRIM$(Command.Line)
 Command.Line = LTRIM$(Command.Line)
 Command.Line.Redirect = Command.Line
 EXIT SUB

Boot.Error:
 CALL RestInt
 Var$=Inkey$
 COLOR White, Black
 PRINT "Copyit "+Version$+": File copy utility; "
 COLOR Yellow, Black
 PRINT Boot.List$; " Type Copyit /? for help."
 COLOR Plain, Black
 END 2

ReadConfig:
 IF Ignore.Config THEN
    RETURN
 END IF
 CLOSE #1
 OPEN Filename$ FOR INPUT AS #1
 File.Found = True
 DO UNTIL EOF(1)
    LINE INPUT #1, Param$
    Param$ = LTRIM$(RTRIM$(Param$))
    IF Param$ <> NUL THEN
       IF LEFT$(LTRIM$(Param$), 1) <> ";" THEN
          Command.Line = Command.Line + Param$
       END IF
    END IF
 LOOP
 CLOSE #1
 RETURN

ReadDosCommand:
 ' get dos commands
 DO
    IF RIGHT$(Command.Line, 1) <> "]" THEN
       EXIT DO
    END IF
    Command.Line = LEFT$(Command.Line, LEN(Command.Line) - 1)
    FOR Count = LEN(Command.Line) TO 1 STEP -1
       IF MID$(Command.Line, Count, 1) = "[" THEN
          Next.Command$ = MID$(Command.Line, Count + 1)
          Command.Line = LEFT$(Command.Line, Count - 1)
          Command.Line = RTRIM$(Command.Line)
          Number.Commands = Number.Commands + 1
          IF Number.Commands > Max.Commands THEN
             Max.Commands = Max.Commands + 10
             REDIM PRESERVE DOS.Command(1 TO Max.Commands) AS STRING * 128
          END IF
          DOS.Command(Number.Commands) = Next.Command$
          EXIT FOR
       END IF
    NEXT
 LOOP
 ' get dos commands
 DO
    IF RIGHT$(Command.Line, 1) <> "'" THEN
       EXIT DO
    END IF
    Command.Line = LEFT$(Command.Line, LEN(Command.Line) - 1)
    FOR Count = LEN(Command.Line) TO 1 STEP -1
       IF MID$(Command.Line, Count, 1) = "'" THEN
          Next.Command$ = MID$(Command.Line, Count + 1)
          Command.Line = LEFT$(Command.Line, Count - 1)
          Command.Line = RTRIM$(Command.Line)
          Number.Commands = Number.Commands + 1
          IF Number.Commands > Max.Commands THEN
             Max.Commands = Max.Commands + 10
             REDIM PRESERVE DOS.Command(1 TO Max.Commands) AS STRING * 128
          END IF
          DOS.Command(Number.Commands) = Next.Command$
          EXIT FOR
       END IF
    NEXT
 LOOP
 RETURN
END SUB

' subroutine converts string to numeric value
SUB GetNumeric(Value#)
 Value# = 0#
 DO
    Temp$ = MID$(Command.Line, Imbedded, 1)
    IF Temp$ >= "0" AND Temp$ <= "9" THEN
       Value# = Value# * 10# + VAL(Temp$)
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 1)
    ELSE
       EXIT SUB
    END IF
 LOOP
END SUB

' display program usage
SUB BootUsage
 CALL RestInt
 Var$=Inkey$
 CALL SetInt
 Var = ClearBreak
 Display.Page = 1
 DO
    SELECT CASE Display.Page
    CASE 1
       GOSUB Display.Page.One
    CASE 2
       GOSUB Display.Page.Two
    CASE 3
       GOSUB Display.Page.Three
    CASE 4
       GOSUB Display.Page.Four
    CASE 5
       GOSUB Display.Page.Five
    CASE 6
       GOSUB Display.Page.Six
    CASE 7
       GOSUB Display.Page.Seven
    CASE 8
       GOSUB Display.Page.Eight
    CASE 9
       GOSUB Display.Page.Nine
    CASE 10
       GOSUB Display.Page.Ten
    END SELECT
    Prompt$ = "Press 1, 2, 3, a, b, d, e, n, s, x, or q to quit:"
    CALL MorePrompt(Prompt$, "123abdensxq", Outpt$, "q")
    IF BreakIS THEN
       CALL RestInt
       COLOR Plain, Black
       END 2
    END IF
    SELECT CASE Outpt$
    CASE "1"
       Display.Page = 1
    CASE "2"
       Display.Page = 2
    CASE "3"
       Display.Page = 3
    CASE "a"
       Display.Page = 4
    CASE "b"
       Display.Page = 5
    CASE "n"
       Display.Page = 6
    CASE "d"
       Display.Page = 7
    CASE "e"
       Display.Page = 8
    CASE "x"
       Display.Page = 9
    CASE "s"
       Display.Page = 10
    CASE "q"
       COLOR Plain, Black
       CALL RestInt
       END 2
    END SELECT
 LOOP
 CALL RestInt
 END 2

Display.Page.One:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   <filelist> are any multiple files specified [@][[d:][\path\]filename.ext]"
 PRINT "      @ prefix specifies file containing file list to copy"
 PRINT "      /d[[d:]\pathname\] is destination directory. (either /d and/or"
 PRINT "      /f[filename.ext] is destination filename.     /f must be specified)."
 PRINT "   following <parameters> are:"
 PRINT "      /c  continuous display            /h  don't display drive letter"
 PRINT "      /l  double line display           /m  don't overwrite dest. file"
 PRINT "      /o  wide list display             /r  recurse directories"
 PRINT "      /s  don't display pathname        /v  append to dest. file"
 PRINT "      /w  don't prompt before overwrite /x  display short filename"
 PRINT "      /y  display files in lowercase    /z  suppress error messages"
 PRINT "   copy filters:"
 PRINT "      /1  copy ascii file to eof   /2xxx-xxx  convert ascii filter (fast)"
 PRINT "      /3xxx  strip ascii values (slow)  /4xxx nested directories"
 PRINT "   file ranges:
 PRINT "      /7  creation date\time, /8 last access date, /9 last modified date\time"
 PRINT "      /e  is range of file dates in form mm/dd/yyyy-mm/dd/yyyy"
 PRINT "      /t  is range of file times in form hh:mm:ss-hh:mm:ss"
 PRINT "      /u  is range of file sizes in form xxx-xxx in kilobytes"
 RETURN

Display.Page.Two:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   following <parameters> are:"
 PRINT "      /a  synchronize dest. files"
 PRINT "      /b1  copy directory structure w/o rename"
 PRINT "      /b2  copy directory structure w/ rename"
 PRINT "      /i  delete files after copy"
 PRINT "      /k  delete directory after copy"
 PRINT "      /n  don't prompt to delete file after copy"
 PRINT "      /q  don't prompt to delete directory after copy"
 PRINT "      /0  don't copy zero-length files"
 PRINT "   file attributes: [prefix1][prefix2][ahosx]"
 PRINT "      prefix1:"
 PRINT "         / copy files, // reset dest. file, /// reset source file;"
 PRINT "      prefix2:"
 print "         x  with\clear, z  without\set;"
 PRINT "      attribute:"
 PRINT "         a  archive, h  hidden, o  read-only, s  system, x  none"
 PRINT "   exit errorlevel codes"
 PRINT "      0 = files copied successfully   2 = no files were copied"
 PRINT "      4 = disk full/disk not ready    8 = copy interrupted by user"
 RETURN

Display.Page.Three:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   following <parameters> are:"
 PRINT "      floppy-floppy copy (currently unimplemented);"
 PRINT "         /g[d:][\tmp\]  specify default temp directory for floppy copy"
 PRINT "         /j  force copy floppy source files to default temp directory"
 PRINT "         /p  prompt for disk insertion for a: to b: same floppy drive"
 PRINT "      over-ride switches:"
 PRINT "         /5mm-dd-yyyy  over-ride destination file date"
 PRINT "         /6hh:mm:ss  over-ride destination file time"
 PRINT "   DOS command enclosed in '', or []s"
 PRINT "      command replacement parameters:"
 PRINT "         %1 = d:, %2 = d:\, %3 = d:\pathname, %4 = d:\pathname\"
 PRINT "         %5 = d:\pathname\filename.ext, %6 = \pathname, %7 = \pathname\"
 PRINT "         %8 = \pathname\filename.ext, %9 = filename.ext, %a = filename"
 PRINT "         %b = .ext, %c = ext, //1 = >, //2 = <, //3 = |, //4 = %"
 RETURN

Display.Page.Four:
 CLS
 COLOR White, Black
 PRINT "Alphabetic list of Copyit command line switches:"
 COLOR Yellow, Black
 PRINT "/a - synchronize dest. files           /n - don't prompt to delete file"
 PRINT "/b - copy directory structure          /o - display wide file list"
 PRINT "/c - continuous list                   /p - prompt floppy disk insertion (*)"
 PRINT "/d - destination directory [d:][\path] /q - don't prompt to delete directory"
 PRINT "/e - date range mm/dd/yyyy-mm/dd/yyyy  /r - recursive directory search"
 PRINT "/f - destination file [filename.ext]   /s - don't display pathname"
 PRINT "/g - temp. directory [d:][\tmp\] (*)   /t - time range hh:mm:ss-hh:mm:ss"
 PRINT "/h - don't display drive letter        /u - file size range in KB"
 PRINT "/i - delete files after copy           /v - append to dest. file"
 PRINT "/j - force copy to temp. directory (*) /w - don't prompt before over-write"
 PRINT "/k - delete directory after copy       /x - short filename display"
 PRINT "/l - double line display               /y - display files in lowercase"
 PRINT "/m - don't over-write dest. file       /z - don't display errors"
 PRINT "(*) - currently unimplemented"
 RETURN

Display.Page.Five:
 CLS
 COLOR White, Black
 PRINT "Bit copying list of Copyit command line switches:"
 COLOR Yellow, Black
 PRINT "Source copy bits:"
 PRINT "/xa - with archive bit                  /xh - with hidden bits"
 PRINT "/xo - with read-only bit                /xs - with system bit"
 PRINT "/xx - with no bits (without all bits)"
 PRINT "/za - without archive bit               /zh - without hidden bits"
 PRINT "/zo - without read-only bit             /zs - without system bit"
 PRINT "/zx - without no bits (with only all bits)"
 PRINT "Destination reset bits:"
 PRINT "//xa - set dest. archive bit            //xh - set dest. hidden bit"
 PRINT "//xo - set dest. read-only bit          //xs - set dest. system bit"
 PRINT "//xx - set all dest. bits"
 PRINT "//za - clear dest. archive bit          //zh - clear dest. hidden bit"
 PRINT "//zo - clear dest. read-only bit        //zs - clear dest. system bit"
 PRINT "//zx - clear all dest. bits"
 PRINT "Source reset bits:"
 PRINT "///xa - set source archive bit          ///xh - set source hidden bit"
 PRINT "///xo - set source read-only bit        ///xs - set source system bit"
 PRINT "///xx - set all source bits"
 PRINT "///za - clear source archive bit        ///zh - clear source hidden bit"
 PRINT "///zo - clear source read-only bit      ///zs - clear source system bit"
 PRINT "///zx - clear all source bits"
 RETURN

Display.Page.Six:
 CLS
 COLOR White, Black
 PRINT "Numeric list of Copyit command line switches:"
 COLOR Yellow, Black
 PRINT "/0 - don't copy zero-length files"
 PRINT "/1 - copy ascii file to eof"
 PRINT "/2 - convert ascii filter xxx-xxx (fast)"
 PRINT "/3 - strip ascii filter (slow)"
 PRINT "/4 - over-ride nested directories"
 PRINT "/5 - specify dest. file date mm-dd-yyyy"
 PRINT "/6 - specify dest. file time hh:mm:ss"
 PRINT "/7 - file creation date\time range search"
 PRINT "/8 - file last access date range search"
 PRINT "/9 - file last modified date\time range search"
 COLOR White, Black
 PRINT "/(<filename.ext>)"
 COLOR Yellow, Black
 PRINT "   Excluded files containing ? and * characters"
 PRINT "   enclosed in parenthesis. Multiple exclusions allowed."
 RETURN

Display.Page.Seven:
 CLS
 COLOR White, Black
 PRINT "List of Copyit DOS command replacement parameters:"
 PRINT "(DOS command enclosed in '', or []s last on command line)"
 COLOR Yellow, Black
 PRINT "%1 = d:"
 PRINT "%2 = d:\"
 PRINT "%3 = d:\pathname"
 PRINT "%4 = d:\pathname\"
 PRINT "%5 = d:\pathname\filename.ext"
 PRINT "%6 = \pathname"
 PRINT "%7 = \pathname\"
 PRINT "%8 = \pathname\filename.ext"
 PRINT "%9 = filename.ext"
 PRINT "%a = filename"
 PRINT "%b = .ext"
 PRINT "%c = ext"
 PRINT "//1 = >"
 PRINT "//2 = <"
 PRINT "//3 = |"
 PRINT "//4 = %"
 RETURN

Display.Page.Eight:
 CLS
 COLOR White, Black
 PRINT "List of Copyit exit errorlevel codes:"
 COLOR Yellow, Black
 PRINT "0 = files copied successfully"
 PRINT "2 = no files were copied"
 PRINT "4 = disk full/disk not ready"
 PRINT "8 = copy interrupted by user"
 COLOR White, Black
 PRINT "List of Copyit extended display switches:"
 COLOR Yellow, Black
 PRINT "/*  display progress bar"
 PRINT "/!  display percent copied"
 PRINT "/-  disable copying dots"
 PRINT "/+  display transfer rate"
 COLOR White, Black
 PRINT "List of Copyit debug switches:"
 COLOR Yellow, Black
 PRINT "/=  enable extended error messages"
 PRINT "/.  disable reading configure file"
 PRINT "/~  clear break flag in DOS"
 RETURN

Display.Page.Nine:
 CLS
 COLOR White, Black
 PRINT "List of Copyit examples:"
 COLOR Yellow, Black
 PRINT "Copy all .doc files to the \temp directory:
 PRINT "  Copyit *.doc /d\temp
 PRINT "Copy all .lst and .txt files to the c: drive \temp directory:
 PRINT "  Copyit *.lst *.txt /dc:\temp\
 PRINT "Copy the file temp.lst to the file temp2.lst in the temp directory:
 PRINT "  Copyit temp.lst /d\temp /ftemp2.lst
 PRINT "Copy files with the hidden attribute set:
 PRINT "  Copyit *.* /d\temp\ /xh
 PRINT "Copy hidden files, and remove the hidden attribute:
 PRINT "  Copyit *.* /d\temp\ /xh //xh
 PRINT "Copy all .doc files into one file:
 PRINT "  Copyit *.doc /ffiles.txt /v
 PRINT "Copy all directories and files to another directory:
 PRINT "  Copyit \dos /d\temp /r/b2
 PRINT "Copy, then delete source files:
 PRINT "  Copyit \dos /d\temp /r/b2/i/k
 PRINT "Copy a file and take out linefeeds:
 PRINT "  Copyit filename.ext /ffile2.txt /310
 PRINT "Copy a file and change linefeeds to carriage returns:
 PRINT "  Copyit filename.ext /ffile3.txt /210-13
 Prompt$ = "Press <enter> to continue:"
 CALL MorePrompt(Prompt$, CHR$(13), Outpt$, CHR$(13))
 IF BreakIS THEN
    CALL RestInt
    COLOR Plain, Black
    END 2
 END IF
 CLS
 COLOR White, Black
 PRINT "List of Copyit examples:"
 COLOR Yellow, Black
 PRINT "Copy windows dos 7.0 long filenames specified in quotes:
 PRINT "  Copyit " + CHR$(34) + "c:\program files" + CHR$(34) + " /d" + CHR$(34) + "d:\program files" + CHR$(34) + " /r/b2"
 PRINT "Copy all files in root to both directories:"
 PRINT "  Copyit \*.* /dc:\tmp1 /dc:\tmp2"
 PRINT "Copy all doc and tmp files to both files:"
 PRINT "  Copyit *.doc *.tmp /ffile1.out /ffile2.out"
 PRINT "Copy all files to both filenames in the directory:"
 PRINT "  Copyit *.* /dd:\tmp1 /ffilea.out /ffileb.out"
 PRINT "Excludes file starting with 'temp' followed by the extension .dat"
 PRINT "  Copyit *.dat /dd:\work /(temp*.dat)"
 PRINT "Excludes files starting with 'temp'."
 PRINT "  Copyit *.* /dd:\work /(temp*.*)"
 PRINT "Copy files with different file sizes:
 PRINT "  Copyit *.* /d\tmp1 /r/b1/a4"
 PRINT "Copy files and changes its creation time:"
 PRINT "  Copyit file? /foutput.txt /7/612:00:00"
 PRINT "Copy files with sizes from 128k to 256k:"
 PRINT "  Copyit file? /foutput.txt /u128-256"
 PRINT "Copy files and display transfer rate and percent being copied:"
 PRINT "  Copyit \*.bak /d\backup.1 /+/!"
 RETURN

Display.Page.Ten:
 CLS
 COLOR White, Black
 PRINT "List of extended /a synchronize numeric switches:"
 COLOR Yellow,Black
 PRINT "/a1  copy source files with sizes less than destination file sizes."
 PRINT "/a2  copy source files with sizes greater than destination file sizes."
 PRINT "/a3  copy source files with sizes equal to destination file sizes."
 PRINT "/a4  copy source files with sizes not equal to destination file sizes."
 PRINT "/a5  copy source files with dates earlier than destination file dates."
 PRINT "/a6  copy source files with dates greater than destination file dates."
 PRINT "/a7  copy source files with dates equal to destination file dates."
 PRINT "/a8  copy source files with dates not equal to destination file dates."
 PRINT "/a9  copy source files with times earlier than destination file times."
 PRINT "/aa  copy source files with times greater than destination file times."
 PRINT "/ab  copy source files with times equal to destination file times."
 PRINT "/ac  copy source files with times not equal to destination file times."
 RETURN

Display.Header:
 ' make header
 CLS
 COLOR White, Black
 PRINT "Copyit "+Version$+": File copy utility; "
 COLOR Yellow, Black
 PRINT "Usage:"
 PRINT "   Copyit <filelist>[/d][/f][<parameters>][<DOS command>]"
 RETURN
END SUB

' subroutine to check for copying directory structure onto itself
SUB DirectoryCopying
 ' reset return flag
 Directory.Exists = False

 ' store drive copying from
 From.Drive = ASC(UCASE$(Drive.Search)) - 64

 ' check target for drive letter
 Copy.To$ = Copy.Target
 IF MID$(Copy.To$, 2, 1) = ":" THEN
    Drive.Number = ASC(UCASE$(LEFT$(Copy.To$, 1))) - 64
    Copy.To$ = MID$(Copy.To$, 3)
 ELSE
    Drive.Number = ASC(UCASE$(Drive.Search)) - 64
 END IF
 IF LEFT$(Copy.To$, 1) <> "\" THEN
    Copy.To$ = Dest.Dir
    IF MID$(Copy.To$, 2, 1) = ":" THEN
       Drive.Number = ASC(UCASE$(LEFT$(Copy.To$, 1))) - 64
       Copy.To$ = MID$(Copy.To$, 3)
    ELSE
       Drive.Number = ASC(UCASE$(Drive.Search)) - 64
    END IF
    Copy.To$ = Default.Dir + "\" + Copy.To$
 END IF

 ' compare drives copying directories
 IF From.Drive = Drive.Number THEN
    ' store directory
    Copy.From$ = RTRIM$(Dir.Copy.Name)
    ' check directories
    IF LCASE$(Copy.From$) = LCASE$(Copy.To$) THEN
       Directory.Exists = -1
       EXIT SUB
    END IF
    IF Recurse.Directories THEN
       IF Copy.From$ <> "\" THEN
          ' check recursive copying
          IF LCASE$(Copy.From$) = LCASE$(LEFT$(Copy.To$, LEN(Copy.From$))) THEN
             Directory.Exists = -2
             ' check recursive self-copying
             IF LEN(Copy.From$) * 2 - 1 = LEN(Copy.To$) THEN
                IF LCASE$(Copy.From$) = LCASE$(RIGHT$(Copy.To$, LEN(Copy.From$))) THEN
                   Directory.Exists = -3
                END IF
             END IF
          END IF
       END IF
    END IF
 END IF
END SUB

' subroutine constructs base filenames
SUB MakeFile(From.File$, To.File$)
 ' make source filename
 IF MID$(From.File$, 2, 1) = ":" THEN
    From.File$ = MID$(From.File$, 3)
 END IF
 IF LEFT$(From.File$, 1) <> "\" THEN
    IF Default.Dir = "\" THEN
       From.File$ = Default.Dir + From.File$
    ELSE
       From.File$ = Default.Dir + "\" + From.File$
    END IF
    IF MID$(From.File$, 2, 1) = ":" THEN
       From.File$ = MID$(From.File$, 3)
    END IF
 END IF
 From.File$ = Drive.Search + ":" + From.File$

 ' make destination filename
 IF MID$(To.File$, 2, 1) = ":" THEN
    To.File$ = MID$(To.File$, 3)
 END IF
 IF LEFT$(To.File$, 1) <> "\" THEN
    IF Default.Dir = "\" THEN
       To.File$ = Default.Dir + To.File$
    ELSE
       To.File$ = Default.Dir + "\" + To.File$
    END IF
    IF MID$(To.File$, 2, 1) = ":" THEN
       To.File$ = MID$(To.File$, 3)
    END IF
 END IF
 IF MID$(Dest.Dir, 2, 1) = ":" THEN
    Drive.Number = ASC(UCASE$(LEFT$(Dest.Dir, 1)))
 ELSE
    Drive.Number = ASC(UCASE$(Drive.Search))
 END IF
 To.File$ = CHR$(Drive.Number) + ":" + To.File$
 IF Display.Lowercase THEN
    From.File$ = LCASE$(From.File$)
    To.File$ = LCASE$(To.File$)
 ELSE
    IF Windows.Detected = False THEN
       From.File$ = UCASE$(From.File$)
       To.File$ = UCASE$(To.File$)
    END IF
 END IF
END SUB

' subroutine to create source filename from stdin/command line
SUB MakeFilename
 ' store redirected input
 Standard.Input = RTRIM$(Standard.Input)
 Standard.Input = LTRIM$(Standard.Input)
 IF LEFT$(Standard.Input, 1) = CHR$(34) THEN
    Standard.Input = MID$(Standard.Input, 2)
 END IF
 IF RIGHT$(Standard.Input, 1) = CHR$(34) THEN
    Standard.Input = LEFT$(Standard.Input, LEN(Standard.Input) - 1)
 END IF

 ' store entire command
 IF LEFT$(Command.Line, 1) = CHR$(34) THEN
    Imbedded = INSTR(2, Command.Line, CHR$(34))
    IF Imbedded THEN
       Command.Work = Standard.Input + MID$(Command.Line, 2, Imbedded - 2)
       Command.Line = MID$(Command.Line, Imbedded + 1)
    ELSE
       Command.Work = Standard.Input + Command.Line
       Command.Line = NUL
    END IF
 ELSE
    Imbedded = INSTR(Command.Line, " ")
    IF Imbedded THEN
       Command.Work = Standard.Input + LEFT$(Command.Line, Imbedded - 1)
       Command.Line = MID$(Command.Line, Imbedded + 1)
    ELSE
       Command.Work = Standard.Input + Command.Line
       Command.Line = NUL
    END IF
 END IF
 Command.Line = LTRIM$(Command.Line)
 Command.Line = RTRIM$(Command.Line)
END SUB

' routine verifies source - destination synchronized file size, date\time.
SUB CheckSynched(Synched%)
 Synched% = False
 IF Synch.Files THEN
    IF File.Size = Dest.File.Size THEN
       IF File.Work.Date = Dest.File.Date THEN
          IF File.Work.Time = Dest.File.Time THEN
             Synched% = True
             EXIT SUB
          END IF
       END IF
    END IF
 END IF
 IF Synch.Files1 THEN
    IF File.Size >= Dest.File.Size THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files2 THEN
    IF File.Size <= Dest.File.Size THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files3 THEN
    IF File.Size <> Dest.File.Size THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files4 THEN
    IF File.Size = Dest.File.Size THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files5 THEN
    IF File.Work.Date >= Dest.File.Date THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files6 THEN
    IF File.Work.Date <= Dest.File.Date THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files7 THEN
    IF File.Work.Date <> Dest.File.Date THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files8 THEN
    IF File.Work.Date = Dest.File.Date THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.Files9 THEN
    IF File.Work.Time >= Dest.File.Time THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.FilesA THEN
    IF File.Work.Time <= Dest.File.Time THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.FilesB THEN
    IF File.Work.Time <> Dest.File.Time THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
 IF Synch.FilesC THEN
    IF File.Work.Time = Dest.File.Time THEN
       Synched% = True
       EXIT SUB
    END IF
 END IF
END SUB

' routine prompts user for an option
SUB MorePrompt(Input.String$, Input.Mask$, Output.String$, Default$)
 IF Wide.List THEN
    Wide.List = False
    PRINT
 END IF
 COLOR White, Black
 PRINT Input.String$ + " ";
 Input.Char$ = NUL
 Rate.Mode = False
 Start.Timer! = TIMER
 DO
    IF Debug.Mode THEN
       Elapsed.Time! = TIMER - Start.Timer!
       IF Elapsed.Time! < 0! THEN
          Elapsed.Time! = Elapsed.Time! + 86400!
       END IF
       IF Elapsed.Time! >= 3! THEN
          PRINT Default$
          Output.String$ = Default$
          EXIT DO
       END IF
    END IF
    LOCATE , , 1
    DO
       IF BreakIS THEN
          EXIT DO
       END IF
       IF KeyIS THEN
          IF OutregsX.AX <> 0 THEN
             InregsX.AX = &H0000
             CALL InterruptX(&H16, InregsX, OutregsX)
             Input.Char$=CHR$(OutregsX.AX AND &HFF)
             EXIT DO
          END IF
       END IF
    LOOP
    IF BreakIS THEN
       EXIT DO
    END IF
    IF LEN(Input.Char$) THEN
       Input.Char$ = LCASE$(Input.Char$)
       IF INSTR(Input.Mask$, Input.Char$) THEN
	  PRINT Input.Char$
	  Output.String$ = Input.Char$
	  EXIT DO
       END IF
    END IF
 LOOP
END SUB

' displays carry flag error
SUB DisplayError(Temp$)
 ' check carry flag error
 IF (OutregsX.Flags AND &H1) = &H1 THEN

    ' check display errors flag
    IF Display.Errors = False THEN

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

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

       ' display error
       COLOR Red, Black
       PRINT Temp$
    END IF
 END IF
END SUB

' clears Control-Break flag
FUNCTION ClearBreak
 DEF SEG = &H40
 POKE &H71, &H0
 DEF SEG
 ClearBreak = True
END FUNCTION

' checks Control-Break flag
FUNCTION BreakIS
 STATIC Var AS INTEGER
 IF KeyIS THEN
    IF OutregsX.AX = 0 THEN
       Var = True
    END IF
 END IF
 IF Var THEN
    Continuous.Display = True
 END IF
 BreakIS = Var
END FUNCTION

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

' routine compares occurrence of filename1$ in filename2$.
SUB CheckExcluded(Filename1$, Filename2$, Match%)
 Match% = True ' assume mask matches filename2
 Length1 = 1
 Length2 = 1
 DO
    ' global replacement
    IF MID$(Filename1$, Length1, 1) = "*" THEN
       DO
          Length1 = Length1 + 1
          IF Length1 > LEN(Filename1$) THEN
             EXIT SUB
          END IF
          ' global replacement followed by exclusion character
          ' searches remaining string until exclusion character found or not.
          IF MID$(Filename1$, Length1, 1) = "^" THEN
             Length1 = Length1 + 1
             Not.Include$ = MID$(Filename1$, Length1, 1)
             DO
                IF Not.Include$ <> MID$(Filename2$, Length2, 1) THEN
                   Length2 = Length2 + 1
                ELSE
                   Match% = False
                   EXIT SUB
                END IF
                IF Length2 > LEN(Filename2$) THEN
                   EXIT SUB
                END IF
             LOOP
          END IF
          ' global replacement followed by ? or another *
          ' skips to next character.
          IF MID$(Filename1$, Length1, 1) <> "*" THEN
             IF MID$(Filename1$, Length1, 1) <> "?" THEN
                EXIT DO
             END IF
          END IF
       LOOP
       ' global replacement
       ' searches for next matching character
       DO
          IF MID$(Filename1$, Length1, 1) = MID$(Filename2$, Length2, 1) THEN
             EXIT DO
          ELSE
             Length2 = Length2 + 1
          END IF
          IF Length2 > LEN(Filename2$) THEN
             EXIT DO
          END IF
       LOOP
    ELSE
       ' character replacement
       ' matches any next character
       IF MID$(Filename1$, Length1, 1) = "?" THEN
          Length1 = Length1 + 1
          Length2 = Length2 + 1
       ELSE
          ' exclusion character
          ' checks next character unmatched
          IF MID$(Filename1$, Length1, 1) = "^" THEN
             Length1 = Length1 + 1
             Not.Include$ = MID$(Filename1$, Length1, 1)
             IF Not.Include$ <> MID$(Filename2$, Length2, 1) THEN
                Length1 = Length1 + 1
                Length2 = Length2 + 1
             ELSE
                Match% = False
                EXIT DO
             END IF
          ELSE
             ' matches next character
             IF MID$(Filename1$, Length1, 1) = MID$(Filename2$, Length2, 1) THEN
                Length1 = Length1 + 1
                Length2 = Length2 + 1
             ELSE
                Match% = False
                EXIT DO
             END IF
             ' check string lengths
             IF Length1 > LEN(Filename1$) THEN
                IF Length2 <= LEN(Filename2$) THEN
                   Match% = False
                END IF
                EXIT DO
             END IF
          END IF
       END IF
    END IF
 LOOP
END SUB
