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

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

' backwards compatible for bc 7.1
'  (edit out REMs in include file for BC7)
REM $INCLUDE: 'BC71.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$))
    Last.Switch = Imbedded - 1
    ParseLine = True
 ELSE
    Imbedded = INSTR(Command.Line, UCASE$(X$))
    IF Imbedded THEN
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + LEN(X$))
       Last.Switch = Imbedded - 1
       ParseLine = True
    ELSE
       ParseLine = False
    END IF
 END IF
END FUNCTION 

' routine parses command line for switches, filenames, and additional info.
SUB ReadSwitches
 On Local Error Goto Boot.Error2
 ' assign a quote.
 Quote = CHR$(34)

 ' 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

 ' check command line.
 IF Command$ = "/?" THEN
    CALL BootUsage
 END IF

 ' concatenate environment variable.
 IF INSTR(Command.Line, "/J") OR INSTR(Command.Line, "/j") THEN
    Force.Lowercase = True
 END IF
 IF Force.Lowercase THEN
    Command.Line = Command.Line + LCASE$(ENVIRON$("COPYIT"))
 ELSE
    Command.Line = Command.Line + ENVIRON$("COPYIT")
 END IF

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

 ' check config switch.
 IF Ignore.Config = False Then

    ' search for config file in dos path.
    Call ReadConfig(Curdir$, File.Found)

    ' search for config file in dosutils path.
    IF File.Found = False THEN
       Call ReadConfig(Environ$("DOSUTILS"), File.Found)
    END IF

    ' search config file in path variable.
    IF File.Found = False THEN
       Var$ = ENVIRON$("PATH")
       IF Var$ <> Nul THEN
          DO
             Imbedded = INSTR(Var$, ";")
             IF Imbedded THEN
                Path$ = LEFT$(Var$, Imbedded - 1)
                Var$ = MID$(Var$, Imbedded + 1)
             ELSE
                Path$ = Var$
                Var$ = Nul
             END IF
             IF LEN(Path$) THEN
                Call ReadConfig(Path$, File.Found)
                IF File.Found THEN
                   EXIT DO
                END IF
             END IF
             IF Var$ = Nul THEN ' v4.1a r2.0a (06/30/2007)
                EXIT DO
             END IF
          LOOP
       END IF
    END IF
 END IF

 ' read command line display switch.
 IF INSTR(Command.Line, "/P") OR INSTR(Command.Line, "/p") THEN
    Display.CommandLine = True
 END IF
 IF Display.CommandLine THEN
    PRINT Command.Line
 END IF

 ' read startup command line switches.
 Force.Lowercase = ParseLine("/J")
 Display.CommandLine = ParseLine("/P")
 IF INSTR(COMMAND$, "/_") THEN
    Windows.Detected = True
    Load.Windows = True
 END IF
 Var = ParseLine("/_")
 IF ParseLine("/&") THEN
    Load.DOS = True
 END IF

 ' 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 synchronize override switches.
 Synch.Type1 = ParseLine("/Z1")
 Synch.Type2 = ParseLine("/Z2")
 Synch.Type3 = ParseLine("/Z3")

 ' reset default date\time switch.
 IF Synch.Type1 = False THEN
    IF Synch.Type2 = False THEN
       IF Synch.Type3 = False THEN
          Synch.Type3 = True
       END IF
    END IF
 END IF

 ' read synchronize override switches.
 Compare.Creation.Date = ParseLine("/S1")
 Compare.Access.Date = ParseLine("/S2")
 Compare.Modified.Date = ParseLine("/S3")

 ' reset default synchronize override switch.
 IF Compare.Creation.Date = False THEN
    IF Compare.Access.Date = False THEN
       IF Compare.Modified.Date = False THEN
          Compare.Modified.Date = True
       END IF
    END IF
 END IF

 ' read remaining alphabetic switches.
 Synch.Files = ParseLine("/A")
 Copy.Directory = ParseLine("/B1")
 Zero.Nest = ParseLine("/B2")
 Continuous.Display = ParseLine("/C")
 Drive.Letter = ParseLine("/H")
 Delete.Copied = ParseLine("/I")
 Delete.Directory = ParseLine("/K")
 Dont.Overwrite = ParseLine("/M")
 Prompt.Delete1 = ParseLine("/N")
 Display.Wide = ParseLine("/O1")
 Ambiguate.Display = ParseLine("/O2")
 Prompt.Delete2 = ParseLine("/Q")
 Recurse.Directories = ParseLine("/R")
 Display.Path = ParseLine("/S")
 Overwrite.Prompt = ParseLine("/W")
 Short.Display = ParseLine("/X1")
 Short.Display2 = ParseLine("/X2")
 Display.Lowercase = ParseLine("/Y")
 Display.Errors = ParseLine("/Z")

 ' parse append switches
 Imbedded = INSTR(UCASE$(Command.Line), "/V2")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 3)
    CALL GetNumeric(Var#)
    IF Var# > 4294967295# THEN
       Boot.List$ = "Error specifying append value."
       GOTO Boot.Error
    END IF
    Byte.Switch = True
    Byte.Override = Var#
    Append.Dest = ParseLine("/V")
 ELSE
    Resume.File = ParseLine("/V1")
    IF Resume.File THEN
       Append.Dest = True
    ELSE
       Append.Dest = ParseLine("/V")
    END IF
 END IF

 ' read more switches
 Control.Break = ParseLine ("/~")
 Pattern.Match = ParseLine ("/@2")
 Enable.Redirect = ParseLine ("/@")
 Disable.Prompts = ParseLine ("/#")

 ' read extended display switches.
 IF ParseLine("/L1") THEN
    Double.Line = 1
 END IF
 IF ParseLine("/L2") THEN
    Double.Line = 2
 END IF
 IF ParseLine("/L3") THEN
    Double.Line = 3
 END IF
 IF ParseLine("/L4") THEN
    Double.Line = 4
 END IF
 IF ParseLine("/L") THEN
    Double.Line = 4
 END IF
 
 ' init first copied directory flag.
 IF Zero.Nest THEN
    Copy.Directory = True
 END IF

 ' parse progress switches.
 Progress.Bar = ParseLine("/*")
 Percent.Display = ParseLine("/!")
 Dot.Mode = ParseLine("/-")

 ' init display switches.
 IF Percent.Display THEN
    Dot.Mode = True
 END IF
 IF Progress.Bar THEN
    Percent.Display = False
    Dot.Mode = True
 END IF

 ' read timing switch.
 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 = SFalse
 Files.Counted = DFalse
 More.Display = False
 Total.Deleted = False

 ' read 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
    Last.Switch = Excluded - 1
    Excluded.Spec$ = Nul
    Next.Bracket = INSTR(Excluded + 2, Command.Line, ")")
    IF Next.Bracket 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)
    END IF
    IF Excluded.Spec$ = Nul THEN
       Boot.List$ = "Error specifying excluded list."
       GOTO Boot.Error
    END IF
    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 * 128
    END IF
    Excluded.Files(Number.Excluded) = UCASE$(Excluded.Spec$)
    Command.Line = Rtrim$(Command.Line)
 LOOP

 ' read DOS commands from command line.
 Max.Commands = 10
 Number.Commands = False
 DO
    DOSswitch = INSTR(Command.Line, "/[")
    IF DOSswitch = False THEN
       EXIT DO
    END IF
    Last.Switch = DOSswitch - 1
    DOS.Spec$ = Nul
    Next.Bracket = INSTR(DOSswitch + 2, Command.Line, "]")
    IF Next.Bracket THEN
       DOS.Spec$ = MID$(Command.Line, DOSswitch + 2, Next.Bracket - DOSswitch - 2)
       Command.Line = LEFT$(Command.Line, DOSswitch - 1) + MID$(Command.Line, Next.Bracket + 1)
    END IF
    IF DOS.Spec$ = Nul THEN
       Boot.List$ = "Error specifying DOS command."
       GOTO Boot.Error
    END IF
    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) = DOS.Spec$
    Command.Line = Rtrim$(Command.Line)
 LOOP

 ' parse 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
    Imbedded = INSTR(Next.Command$, "//5")
    WHILE Imbedded
       Next.Command$ = LEFT$(Next.Command$, Imbedded - 1) + "]" + MID$(Next.Command$, Imbedded + 3)
       Imbedded = INSTR(Next.Command$, "//5")
    WEND
    DOS.Command(Count) = Next.Command$
 NEXT

 ' read destination file date override.
 New.Date.Month = False
 New.Date.Day = False
 New.Date.Year = False
 Override.Date = False
 Imbedded = INSTR(Command.Line, "/5")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Date.Override$ = MID$(Command.Line, Imbedded + 2, 10)
    IF MID$(Date.Override$, 3, 1) <> "/" THEN
       Boot.List$ = "Error specifying override date."
       GOTO Boot.Error
    END IF
    IF MID$(Date.Override$, 6, 1) <> "/" THEN
       Boot.List$ = "Error specifying override 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)))
    IF New.Date.Month = 99 AND New.Date.Day = 99 AND New.Date.Year = 9999 THEN
       New.Date.Month = VAL(LEFT$(DATE$, 2))
       New.Date.Day = VAL(MID$(DATE$, 4, 2))
       New.Date.Year = VAL(RIGHT$(DATE$, 4))
    END IF
    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

 ' read destination file time override.
 New.Time.Hour = False
 New.Time.Minute = False
 New.Time.Second = False
 Override.Time = False
 Imbedded = INSTR(Command.Line, "/6")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Time.Override$ = MID$(Command.Line, Imbedded + 2, 8)
    IF MID$(Time.Override$, 3, 1) <> ":" THEN
       Boot.List$ = "Error specifying override time."
       GOTO Boot.Error
    END IF
    IF MID$(Time.Override$, 6, 1) <> ":" THEN
       Boot.List$ = "Error specifying override time."
       GOTO Boot.Error
    END IF
    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)))
    IF New.Time.Hour = 99 AND New.Time.Minute = 99 AND New.Time.Second = 99 THEN
       New.Time.Hour = INT(VAL(LEFT$(TIME$, 2)))
       New.Time.Minute = INT(VAL(MID$(TIME$, 4, 2)))
       New.Time.Second = INT(VAL(RIGHT$(TIME$, 2)))
    END IF
    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)
    ' seconds are rounded to divisions of two.
    IF New.Time.Second / 2 <> INT(New.Time.Second / 2) THEN
       Millisecond = 100 ' add one second.
    ELSE
       Millisecond = False
    END IF
 END IF

 ' read date from command line.
 Search.Date = False
 Search.From.Date = False
 Search.To.Date = False
 Imbedded = INSTR(UCASE$(Command.Line), "/E")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    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
       IF D1! = 99! AND D2! = 99! AND D3! = 9999! THEN ' current date
          D1! = VAL(LEFT$(DATE$, 2))
          D2! = VAL(MID$(DATE$, 4, 2))
          D3! = VAL(RIGHT$(DATE$, 4))
       END IF
       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
       IF D1! = 99! AND D2! = 99! AND D3! = 9999! THEN ' current date
          D1! = VAL(LEFT$(DATE$, 2))
          D2! = VAL(MID$(DATE$, 4, 2))
          D3! = VAL(RIGHT$(DATE$, 4))
       END IF
       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
          IF D1! = 99! AND D2! = 99! AND D3! = 9999! THEN ' current date
             D1! = VAL(LEFT$(DATE$, 2))
             D2! = VAL(MID$(DATE$, 4, 2))
             D3! = VAL(RIGHT$(DATE$, 4))
          END IF
          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
             IF D1! = 99! AND D2! = 99! AND D3! = 9999! THEN ' current date
                D1! = VAL(LEFT$(DATE$, 2))
                D2! = VAL(MID$(DATE$, 4, 2))
                D3! = VAL(RIGHT$(DATE$, 4))
             END IF
             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

 ' read time from command line.
 Search.Time = False
 Search.From.Time = False
 Search.To.Time = False
 Half.Second1 = False
 Half.Second2 = False
 Imbedded = INSTR(UCASE$(Command.Line), "/T")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Search.Time = True
    ' /t12: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
       IF T1! = 99 AND T2! = 99 AND T3! = 99 THEN
          T1! = INT(VAL(LEFT$(TIME$, 2)))
          T2! = INT(VAL(MID$(TIME$, 4, 2)))
          T3! = INT(VAL(RIGHT$(TIME$, 2)))
       END IF
       IF T3! / 2! <> INT(T3! / 2!) THEN
          Half.Second1 = .5
       END IF
       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
       IF T1! = 99 AND T2! = 99 AND T3! = 99 THEN
          T1! = INT(VAL(LEFT$(TIME$, 2)))
          T2! = INT(VAL(MID$(TIME$, 4, 2)))
          T3! = INT(VAL(RIGHT$(TIME$, 2)))
       END IF
       IF T3! / 2! <> INT(T3! / 2!) THEN
          Half.Second2 = .5
       END IF
       Search.To.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 19)
    ELSE
       ' /t-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
          IF T1! = 99 AND T2! = 99 AND T3! = 99 THEN
             T1! = INT(VAL(LEFT$(TIME$, 2)))
             T2! = INT(VAL(MID$(TIME$, 4, 2)))
             T3! = INT(VAL(RIGHT$(TIME$, 2)))
          END IF
          IF T3! / 2! <> INT(T3! / 2!) THEN
             Half.Second2 = .5
          END IF
          Search.To.Time = T1! * 2048 + T2! * 32 + INT(T3! / 2)
          Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 11)
       ELSE
          ' /t12: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
             IF T1! = 99 AND T2! = 99 AND T3! = 99 THEN
                T1! = INT(VAL(LEFT$(TIME$, 2)))
                T2! = INT(VAL(MID$(TIME$, 4, 2)))
                T3! = INT(VAL(RIGHT$(TIME$, 2)))
             END IF
             IF T3! / 2! <> INT(T3! / 2!) THEN
                Half.Second1 = .5
             END IF
             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

 ' read file size from command line.
 Search.File.Size = False
 Search.Size.From = DFalse
 Search.Size.To = DFalse
 Imbedded = INSTR(UCASE$(Command.Line), "/U")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    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

 ' read pattern date from command line.
 Pattern.Search.Date = False
 Pattern.Search.From.Date = Nul
 Pattern.Search.To.Date = Nul
 Imbedded = INSTR(UCASE$(Command.Line), "/E1:")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Pattern.Search.Date = True
    ' /e1:01/01/2000-01/01/2007
    IF MID$(Command.Line, Imbedded + 6, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 9, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 14, 1) = "-" AND _
    MID$(Command.Line, Imbedded + 17, 1) = "/" AND _
    MID$(Command.Line, Imbedded + 20, 1) = "/" THEN
       D$ = MID$(Command.Line, Imbedded + 4, 21)
       Pattern.Search.From.Date = LEFT$(D$, 10)
       IF Pattern.Search.From.Date = "99/99/9999" THEN ' current date
          Pattern.Search.From.Date = DATE$
          MID$(Pattern.Search.From.Date, 3, 1) = "/"
          MID$(Pattern.Search.From.Date, 6, 1) = "/"
       END IF
       Pattern.Search.To.Date = RIGHT$(D$, 10)
       IF Pattern.Search.To.Date = "99/99/9999" THEN ' current date
          Pattern.Search.To.Date = DATE$
          MID$(Pattern.Search.To.Date, 3, 1) = "/"
          MID$(Pattern.Search.To.Date, 6, 1) = "/"
       END IF
       Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 25)
    ELSE
       Boot.List$ = "Error specifying pattern search date."
       GOTO Boot.Error
    END IF
 END IF

 ' read filename pattern from command line.
 Pattern.Search = False
 Pattern.Filename = Nul
 Imbedded = INSTR(UCASE$(Command.Line), "/E2:")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    IF MID$(Command.Line, Imbedded + 4, 1) = Quote THEN
       FOR Var = Imbedded + 5 TO LEN(Command.Line)
          IF MID$(Command.Line, Var, 1) = Quote THEN
             Pattern.Search = True
             Pattern.Filename = UCASE$(MID$(Command.Line, Imbedded + 5, Var - Imbedded - 5))
             Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Var + 1)
             EXIT FOR
          END IF
       NEXT
       IF Pattern.Search = False THEN
          Boot.List$ = "Error specifying filename pattern."
          GOTO Boot.Error
       END IF
    END IF
 END IF

 ' verify both parameters set.
 IF Pattern.Search OR Pattern.Search.Date THEN
    IF Pattern.Search = False OR Pattern.Search.Date = False THEN
       Boot.List$ = "Error specifying filename pattern."
       GOTO Boot.Error
    END IF
 END IF

 ' read remaining numeric switches.
 Copy.Zero = ParseLine("/0")
 Copy.Ascii = ParseLine("/1")

 ' read 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
    Last.Switch = Imbedded - 1
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    IF Var# > 255# THEN
       Boot.List$ = "Error specifying search filter."
       GOTO Boot.Error
    END IF
    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#)
    IF Var# > 255# THEN
       Boot.List$ = "Error specifying search filter."
       GOTO Boot.Error
    END IF
    Ascii.To = CINT(Var#)
    IF Ascii.To <> Ascii.From THEN
       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
       Convert.Ascii(Ascii.Filters, 1) = Ascii.From
       Convert.Ascii(Ascii.Filters, 2) = Ascii.To
    END IF
 LOOP

 ' read 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
    Last.Switch = Imbedded - 1
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    IF Var# > 255# THEN
       Boot.List$ = "Error specifying strip filter."
       GOTO Boot.Error
    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
    Strip.Ascii(Ascii.Strips) = CINT(Var#)
 LOOP

 ' read nest override switch.
 Imbedded = INSTR(Command.Line, "/4")
 IF Imbedded THEN
    Last.Switch = Imbedded - 1
    Command.Line = LEFT$(Command.Line, Imbedded - 1) + MID$(Command.Line, Imbedded + 2)
    CALL GetNumeric(Var#)
    IF Var# > 32767# THEN
       Boot.List$ = "Error specifying nest override."
       GOTO Boot.Error
    END IF
    Nested.Recurse = CINT(Var#)
 END IF

 ' read override switches.
 Override.Creation.Date = ParseLine("/7")
 Override.Access.Date = ParseLine("/8")
 Override.Modified.Date = ParseLine("/9")

 ' reset default date\time switch.
 IF Override.Creation.Date = False THEN
    IF Override.Access.Date = False THEN
       IF Override.Modified.Date = False THEN
          Override.Modified.Date = True
       END IF
    END IF
 END IF

 ' check trailing command line.
 Command.Line = RTRIM$(Command.Line)
 IF Last.Switch THEN
    IF LEN(Command.Line) > Last.Switch THEN
       Boot.List$ = "Unknown command line characters."
       GOTO Boot.Error
    END IF
 END IF

 ' reset remaining variables.
 Dest.Dir = Nul
 Max.Dest.Dirs = 10
 Number.Dest.Dirs = False

 Dest.File = Nul
 Max.Dest.Files = 10
 Number.Dest.Files = False

 Max.Dest.Nets = 10
 Number.Dest.Nets = False

 Temp.Drive.Dir = Nul

 ' check command line.
 Command.Line = RTRIM$(Command.Line)
 Command.Line = LTRIM$(Command.Line)
 IF Command.Line = Nul THEN
    ' check stdin status
    InregsX.AX = &H0B00
    CALL InterruptX(&H21, InregsX, OutregsX)
    IF (OutregsX.AX AND &HFF) = &H00 THEN
       Boot.List$ = "Missing command line characters."
       GOTO Boot.Error
    END IF
 END IF

 ' read remaining filename switches.
 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
          ' store switch letter.
          Switch$ = MID$(Command.Line, Count + 1, 1)

          ' store switch value.
          Switch.Line$ = MID$(Command.Line, Count + 2)
          Switch.Line$ = LTRIM$(Switch.Line$)
          Switch.Line$ = RTRIM$(Switch.Line$)

          ' check quoted string.
          File.Quotes = False
          IF RIGHT$(Switch.Line$, 1) = Quote THEN
             IF LEFT$(Switch.Line$, 1) = Quote THEN
                Switch.Line$ = MID$(Switch.Line$, 2)
                Switch.Line$ = LEFT$(Switch.Line$, LEN(Switch.Line$) - 1)
                File.Quotes = True
             END IF
          END IF

          ' process switch.
          SELECT CASE UCASE$(Switch$)
          CASE "F" ' filename
             Last.Switch = Count - 1
             IF LEN(Switch.Line$) = False THEN
                Boot.List$ = "Missing command line characters."
                GOTO Boot.Error
             END IF
             IF INSTR(Switch.Line$, " ") THEN
                IF File.Quotes = False THEN
                   Boot.List$ = "Unknown command line characters."
                   GOTO Boot.Error
                END IF
             END IF
             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" ' directory
             Last.Switch = Count - 1
             IF LEN(Switch.Line$) = False THEN
                ' check /d w/o parameters specified
                IF Zero.Dirs THEN
                   Boot.List$ = "Multiple nul directories not allowed."
                   GOTO Boot.Error
                END IF
                ' set /d w/o parameters specified
                Zero.Dirs = True
             END IF
             IF INSTR(Switch.Line$, " ") THEN
                IF File.Quotes = False THEN
                   Boot.List$ = "Unknown command line characters."
                   GOTO Boot.Error
                END IF
             END IF
             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
             ' check /d w/o parameters specified
             IF Number.Dest.Dirs > 1 THEN
                IF Zero.Dirs THEN
                   Boot.List$ = "Combined specified and nul directory not allowed."
                   GOTO Boot.Error
                ENDIF
             END IF
             Destinate.Directory(Number.Dest.Dirs) = Switch.Line$
          CASE "G" ' netpath
             Last.Switch = Count - 1
             IF LEN(Switch.Line$) = False THEN
                ' check /g w/o parameters specified
                IF Zero.Nets THEN
                   Boot.List$ = "Multiple nul netpaths not allowed."
                   GOTO Boot.Error
                END IF
                ' set /g w/o parameters specified
                Zero.Nets = True
             END IF
             IF INSTR(Switch.Line$, " ") THEN
                IF File.Quotes = False THEN
                   Boot.List$ = "Unknown command line characters."
                   GOTO Boot.Error
                END IF
             END IF
             Number.Dest.Nets = Number.Dest.Nets + 1
             IF Number.Dest.Nets > Max.Dest.Nets THEN
                Max.Dest.Nets = Max.Dest.Nets + 10
                REDIM PRESERVE Destinate.Netpaths(1 TO Max.Dest.Nets) AS STRING * 260
             END IF
             ' check /g w/o parameters specified
             IF Number.Dest.Nets > 1 THEN
                IF Zero.Nets THEN
                   Boot.List$ = "Combined specified and nul netpaths not allowed."
                   GOTO Boot.Error
                ENDIF
             END IF
             IF LEFT$(Switch.Line$, 2) <> "\\" THEN
                IF Zero.Nets = False THEN
                   Boot.List$ = "Unknown netpath characters."
                   GOTO Boot.Error
                END IF
             END IF
             Destinate.NetPaths(Number.Dest.Nets) = Switch.Line$
          CASE ELSE ' unknown switch
             EXIT DO
          END SELECT
          Command.Line = LEFT$(Command.Line, Count - 1)
          Command.Line = RTRIM$(Command.Line)
          EXIT FOR
       END IF
    NEXT
 LOOP

 ' check trailing command line.
 Command.Line = RTRIM$(Command.Line)
 IF Last.Switch THEN
    IF LEN(Command.Line) > Last.Switch THEN
       Boot.List$ = "Unknown command line characters."
       GOTO Boot.Error
    END IF
 END IF

 ' check zero dirs specified
 IF Number.Dest.Nets > 1 THEN
    IF Zero.Dirs THEN
       Boot.List$ = "Nul directory and multiple netpaths not allowed."
       GOTO Boot.Error
    END IF
 END IF

 ' check zero nets specified
 IF Number.Dest.Nets > 1 THEN
    IF Zero.Nets THEN
       Boot.List$ = "Nul netpath and multiple netpaths not allowed."
       GOTO Boot.Error
    END IF
 END IF

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

 ' recheck command line.
 IF INSTR(Command.Line, "/") THEN
    Boot.List$ = "Unknown / switch."
    GOTO Boot.Error
 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

 ' restore arrays to original order.
 IF Number.Dest.Files > 1 THEN
    Last.Array = Number.Dest.Files
    Pivot.Array = INT(Last.Array / 2)
    FOR Var = 1 TO Pivot.Array
       SWAP Destinate.Filename(Var), Destinate.Filename(Last.Array)
       Last.Array = Last.Array - 1
    NEXT
 END IF
 IF Number.Dest.Dirs > 1 THEN
    Last.Array = Number.Dest.Dirs
    Pivot.Array = INT(Last.Array / 2)
    FOR Var = 1 TO Pivot.Array
       SWAP Destinate.Directory(Var), Destinate.Directory(Last.Array)
       Last.Array = Last.Array - 1
    NEXT
 END IF
 IF Number.Dest.Nets > 1 THEN
    Last.Array = Number.Dest.Nets
    Pivot.Array = INT(Last.Array / 2)
    FOR Var = 1 TO Pivot.Array
       SWAP Destinate.Netpaths(Var), Destinate.Netpaths(Last.Array)
       Last.Array = Last.Array - 1
    NEXT
 END IF
 EXIT SUB

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

' trap any errors before switch subroutine exits to main copying code.
Boot.Error2:
 Data.Error = ERR
 SELECT CASE Data.Error
 CASE 9 ' since redims use up string space fast, trap them here:
    Boot.List$ = "Critical error: 'Subscript out of range' in boot sequence."
 CASE 14
    Boot.List$ = "Critical error: 'Out of string space' in boot sequence."
 CASE ELSE
    Boot.List$ = "Critical error" + Str$(Err) + " in boot sequence."
 END SELECT
 Resume Boot.Error
END SUB

' subroutines searchs for and parses config file.
SUB ReadConfig(Var$, File.Found) ' (fixed v4.1a 06/10/2007)
 On Local Error Goto ConfigError
 File.Found = False
 IF LEFT$(Var$, 1) = Chr$(34) Then
    Var$ = Mid$(Var$, 2)
 END IF
 IF Right$(Var$, 1) = Chr$(34) Then
    Var$ = Left$(Var$, Len(Var$) - 1)
 END IF
 IF RIGHT$(Var$, 1) <> "\" THEN
    Var$ = Var$ + "\"
 END IF
 Var$ = Var$ + "COPYIT.CFG"
 Var$ = Ambiguate$(Var$)
 IF DIR$(Var$) <> Nul THEN
    CLOSE #1
    OPEN Var$ FOR INPUT AS #1
    File.Found = True
    DO UNTIL EOF(1)
       LINE INPUT #1, Param$
       Var2$ = LTRIM$(RTRIM$(Param$))
       IF Var2$ <> Nul THEN
          IF LEFT$(Var2$, 1) <> ";" THEN
             Command.Line = Command.Line + Param$
          END IF
       END IF
    LOOP
 END IF
 CLOSE #1
ConfigResume:
 EXIT SUB
ConfigError:
 Resume ConfigResume
END SUB

' subroutine converts string to numeric value
SUB GetNumeric(Value#)
 On Local Error Goto GetNumError
 Value# = DFalse
 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
GetNumResume:
 EXIT SUB
GetNumError:
 Resume GetNumResume
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
    CASE 11
       GOSUB Display.Page.Eleven
    END SELECT
    Prompt$ = "Press 1, 2, 3, 4, 5, a, b, n, s, x, ?, or q to quit:"
    CALL MorePrompt(Prompt$, "12345abnsx?q", Outpt$, "q")
    IF BreakIS THEN
       CALL RestInt
       COLOR Plain, Black
       PRINT
       PRINT "Exiting to DOS:"
       END 2
    END IF
    SELECT CASE Outpt$
    CASE "1"
       Display.Page = 1
    CASE "2"
       Display.Page = 2
    CASE "3"
       Display.Page = 3
    CASE "4"
       Display.Page = 4
    CASE "5"
       Display.Page = 5
    CASE "a"
       Display.Page = 6
    CASE "b"
       Display.Page = 7
    CASE "n"
       Display.Page = 8
    CASE "s"
       Display.Page = 9
    CASE "x"
       Display.Page = 10
    CASE "?"
       Display.Page = 11
    CASE "q"
       CALL RestInt
       COLOR Plain, Black
       PRINT "Exiting to DOS:"
       END 2
    END SELECT
 LOOP
 CALL RestInt
 COLOR Plain, Black
 PRINT "Exiting to DOS:"
 END 2

Display.Page.One:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   <filelist> is source files to copy and are:"
 PRINT "     multiple files specified:
 PRINT "       [@][d:][\path\][filename.ext]"
 PRINT "     or with netpath specified \\server\share\"
 PRINT "       @ prefix specifies filename containing filelist to copy"
 PRINT "   (destination switches):"
 PRINT "     /d[d:][\pathname\]  is destination directory"
 PRINT "     /f[filename.ext]    is destination filename"
 PRINT "     /g[\\server\share\] is destination netpath"
 PRINT "   (directory/file switches):"
 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"
 RETURN

Display.Page.Two:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   (standard copying switches):"
 PRINT "     /j  force environment variable to lowercase"
 PRINT "     /m  don't overwrite dest. file"
 PRINT "     /p  display command line"
 PRINT "     /r  recurse directories"
 PRINT "     /v  append to dest. file"
 PRINT "     /v1 append/resume to dest. file"
 PRINT "     /v2### copy from byte position"
 PRINT "     /w  don't prompt before overwrite"
 PRINT "   (display switches):"
 PRINT "     /c  continuous display         /s  don't display pathname"
 PRINT "     /h  don't display drive letter /x1 don't display dest. filename"
 PRINT "     /lx added info switches x=1-4  /x2 don't prepend display type"
 PRINT "     /o1 display wide list          /y  display files in lowercase"
 PRINT "     /o2 ambiguate filename         /z  suppress error messages"
 PRINT "   (filelist switches):"
 PRINT "     /@  enable filelists           /#  disable filelist prompts"
 PRINT "     /@2 ignore pattern matching for filelist filenames"
 RETURN

Display.Page.Three:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   (copy filter switches):"
 PRINT "     /1  copy file to eof"
 PRINT "     /2xxx-xxx  convert ascii filter (fast)"
 PRINT "     /3xxx  strip ascii values (slow)"
 PRINT "     /4xxx nested directories"
 PRINT "   (file override switches):"
 PRINT "     /5mm/dd/yyyy  override destination file date"
 PRINT "     /6hh:mm:ss    override destination file time"
 PRINT "     /7  creation date\time"
 PRINT "     /8  last access date"
 PRINT "     /9  last modified (write) date\time"
 PRINT "   (file override ranges):"
 PRINT "     /e  search date in form mm/dd/yyyy-mm/dd/yyyy"
 PRINT "     /t  search time in form hh:mm:ss-hh:mm:ss"
 PRINT "     /u  search file size in form xxx-xxx in kilobytes"
 PRINT "   (compare switches used with /e, /t):"
 PRINT "     /s1  compare source creation date\time"
 PRINT "     /s2  compare source last access date"
 PRINT "     /s3  compare source last modified date\time"
 RETURN

Display.Page.Four:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   (date range switches):"
 PRINT "     /e1:mm/dd/yyyy-mm/dd/yyyy  pattern match date range"
 PRINT "     /e2:" + Quote + "filename.ext" + Quote + "  pattern match filename, include:"
 PRINT "       MM, DD, YY, or YYYY, ?, to match with /e1:"
 PRINT "   (file attributes switches):"
 PRINT "     [prefix1][prefix2][ahosx]"
 PRINT "       prefix1: / copy files, // reset dest. file, /// reset source file;"
 PRINT "       prefix2: x  with\clear, z  without\set;"
 PRINT "       attribute: a  archive, h  hidden, o  read-only, s  system, x  none"
 PRINT "   (extended display switches):"
 PRINT "     /*  display progress bar"
 PRINT "     /!  display percent copied"
 PRINT "     /-  disable copying dots"
 PRINT "     /+  display transfer rate"
 RETURN

Display.Page.Five:
 GOSUB Display.Header
 PRINT "Where:"
 PRINT "   (debug switches):"
 PRINT "     /=  enable extended error messages"
 PRINT "     /.  disable reading configure file"
 PRINT "     /~  clear break flag in DOS"
 PRINT "     /_  force windows detected"
 PRINT "     /&  force DOS loaded"
 PRINT "   (exclude file list switch):"
 PRINT "     /(<filename.ext>)"
 PRINT "       Excludes files containing ? and * characters when"
 PRINT "       enclosed in parenthesis. Multiple exclusions allowed."
 PRINT "   (DOS command switch):"
 PRINT "     /[<command>]  with 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 = %, //5 = ]"
 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.Six:
 CLS
 COLOR White, Black
 PRINT "Alphabetic list of Copyit command line switches:"
 COLOR Yellow, Black
 PRINT "/a - synchronize dest. files           /o2- ambiguate filename"
 PRINT "/b - copy directory structure          /p - display command line"
 PRINT "/c - continuous list                   /q - don't prompt to delete directory"
 PRINT "/d - destination directory [d:][\path] /r - recursive directory search"
 PRINT "/e - date range mm/dd/yyyy-mm/dd/yyyy  /s - don't display pathname"
 PRINT "/f - destination file [filename.ext]   /t - time range hh:mm:ss-hh:mm:ss"
 PRINT "/g - specifies dest. \\server\share\   /u - file size range in KB"
 PRINT "/h - don't display drive letter        /v - append to dest. file"
 PRINT "/i - delete files after copy           /v1- append/resume to dest. file"
 PRINT "/j - force environment to lowercase    /v2- copy from byte position"
 PRINT "/k - delete directory after copy       /w - don't prompt before overwrite"
 PRINT "/lx- added info switches x=1-4         /x1- don't display dest. filename"
 PRINT "/m - don't overwrite dest. file        /x2- don't prepend display type"
 PRINT "/n - don't prompt to delete file       /y - display files in lowercase"
 PRINT "/o1- display wide list                 /z - don't display errors"
 RETURN

Display.Page.Seven:
 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.Eight:
 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 - override nested directories"
 PRINT "/5 - specify dest. file date mm/dd/yyyy override"
 PRINT "/6 - specify dest. file time hh:mm:ss override"
 PRINT "/7 - file creation date\time override"
 PRINT "/8 - file last access date override"
 PRINT "/9 - file last modified date\time override"
 RETURN

Display.Page.Nine:
 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."
 COLOR White, Black
 PRINT "Synchronize switches for date\time comparing: (used with /a)"
 COLOR Yellow, Black
 PRINT "/z1  compare source creation date\time"
 PRINT "/z2  compare source last access date"
 PRINT "/z3  compare source last modified date\time"
 RETURN

Display.Page.Ten:
 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
    PRINT
    PRINT "Exiting to DOS:"
    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 " + Quote + "c:\program files" + Quote + " /d" + Quote + "d:\program files" + Quote + " /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 /612:00:00/7"
 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.Eleven:
 GOSUB Display.Header
 PRINT "Where:"
 COLOR White, Black
 PRINT "List of reserved characters:"
 COLOR Yellow,Black
 PRINT "In DOS 8.3, the following characters are reserved and
 PRINT "cannot be used in filenames:"
 COLOR White, Black
 PRINT "\:/*?"+Chr$(34)+"><|"
 COLOR Yellow,Black
 PRINT "In XP, the following characters are reserved and should"
 PRINT "be enclosed in quotes when used in filenames on the command line:"
 COLOR White, Black
 PRINT "&()[]{}^=;!'+,`~"
 COLOR Yellow,Black
 PRINT "Otherwise, prepend the reserved character with ^ character"
 PRINT "when being used in XP on the Copyit command line, for example:"
 PRINT "  Copyit file.dat /ffile.bak /^&"
 RETURN

Display.Header:
 ' make header.
 CLS
 COLOR White, Black
 PRINT "Copyit "+Version$+" "+Release$+": File copy utility; "
 COLOR Yellow, Black
 PRINT "Usage:  Copyit <filelist>[/dfg][<switches>]"
 RETURN
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
    LOCATE , , 1
    ' store registers.
    InregsX2 = InregsX
    DO
       ' exits with default prompt in debug mode after 3 seconds.
       IF Debug.Mode THEN
          Elapsed.Time! = TIMER - Start.Timer!
          IF Elapsed.Time! < SFalse THEN
             Elapsed.Time! = Elapsed.Time! + 86400!
          END IF
          IF Elapsed.Time! >= 3! THEN
             Input.Char$ = Default$
             EXIT DO
          END IF
       END IF
       ' check break key.
       IF BreakIS THEN
          EXIT DO
       END IF
       ' check keyboard.
       IF KeyIS THEN
          ' get key.
          IF OutregsX.AX <> False THEN
             InregsX.AX = &H0000
             CALL InterruptX(&H16, InregsX, OutregsX)
             Input.Char$=CHR$(OutregsX.AX AND &HFF)
             EXIT DO
          END IF
       END IF
    LOOP
    ' restore registers.
    InregsX = InregsX2
    ' check break key.
    IF BreakIS THEN
       EXIT DO
    END IF
    ' check keyboard input.
    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 Redirected.Input THEN
    DEF SEG = &H40
    X = PEEK(&H71)
    DEF SEG
    IF X = 64 THEN
       Var = True
    END IF
 END IF
 IF KeyIS THEN
    IF OutregsX.AX = False THEN
       Var = True
       DEF SEG = &H40
       POKE &H71, 64
       DEF SEG
    END IF
 END IF
 IF Var THEN
    Continuous.Display = True
 END IF
 BreakIS = Var
END FUNCTION

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

' ambiguate function
FUNCTION Ambiguate$(Var$)
 ' initialize asciiz filenames
 DIM ASCIIZ1 AS STRING * 260
 DIM ASCIIZ2 AS STRING * 261

 ' check DOS loaded
 IF Windows.Detected = False THEN
    Ambiguate$ = Var$ ' return 8.3 filename
 END IF

 ' read windows 8.3 filename
 IF Windows.Detected THEN
    ' ambiguate filename
    ASCIIZ1 = Var$ + 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) = &H1 THEN
       Ambiguate$ = Nul
    END IF

    ' check carry flag error
    IF (OutregsX.Flags AND &H1) = &H0 THEN
       ' store windows 8.3 filename
       Var2$ = ASCIIZ2
       Imbedded = INSTR(Var2$, CHR$(0))
       IF Imbedded THEN
          Var2$ = LEFT$(Var2$, Imbedded - 1)
       END IF
       Ambiguate$ = Var2$
    END IF
 END IF
END FUNCTION

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

 ' read source drive.
 From.Drive = ASC(UCASE$(Drive.Search)) - 64

 ' read source directory.
 Copy.From$ = RTRIM$(Dir.Copy.Name)
 Copy.From2$ = Copy.From$
 Work.Net3$ = RTRIM$(Source.Net)
 Work.Net4$ = RTRIM$(Default.Net)
 IF LEFT$(Copy.From$, 1) = "\" THEN
    IF Work.Net3$ <> Nul THEN
       Copy.From$ = RTRIM$(Work.Net3$) + Copy.From$
    ELSE
       IF Work.Net4$ <> Nul THEN
          Copy.From$ = RTRIM$(Work.Net4$) + Drive.Search + Copy.From$
       END IF
    END IF
 ELSE
    IF Work.Net3$ <> Nul THEN
       Copy.From$ = RTRIM$(Work.Net3$) + "\" + Copy.From$
    ELSE
       IF Work.Net4$ <> Nul THEN
          Copy.From$ = RTRIM$(Work.Net4$) + Drive.Search + "\" + Copy.From$
       END IF
    END IF
 END IF

 ' read target drive.
 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

 ' read target directory.
 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
    IF Work.Net$ = Nul THEN
       Copy.To$ = Default.Dir + "\" + Copy.To$
    END IF
 END IF
 IF Work.Net$ <> Nul THEN
    IF LEFT$(Copy.To$, 1) = "\" THEN
       Copy.To$ = Work.Net$ + Copy.To$
    ELSE
       Copy.To$ = Work.Net$ + "\" + Copy.To$
    END IF
 END IF
 Copy.From$ = LCASE$(Copy.From$)
 Copy.To$ = LCASE$(Copy.To$)

 ' compare drives.
 IF From.Drive = Drive.Number THEN
    ' check directories.
    IF Copy.From$ = Copy.To$ THEN
       Directory.Exists = -1
       EXIT SUB
    END IF
    ' check recursing directories.
    IF Recurse.Directories THEN
       IF Copy.From2$ <> "\" THEN
          ' check recursive copying.
          IF Copy.From$ = 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 Copy.From$ = 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(Work.Net$, From.File$, To.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 Work.Net$ = Nul THEN
       IF Default.Dir = "\" THEN
          To.File$ = Default.Dir + To.File$
       ELSE
          To.File$ = Default.Dir + "\" + To.File$
       END IF
    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
 IF Work.Net$ = Nul THEN
    To.File$ = CHR$(Drive.Number) + ":" + To.File$
 ELSE
    IF LEFT$(To.File$, 1) = "\" THEN
       To.File$ = Work.Net$ + To.File$
    ELSE
       To.File$ = Work.Net$ + "\" + To.File$
    END IF
 END IF

 ' make source filename.
 Work.Net2$ = RTRIM$(Source.Net)
 IF MID$(From.File$, 2, 1) = ":" THEN
    From.File$ = MID$(From.File$, 3)
 END IF
 IF LEFT$(From.File$, 1) <> "\" THEN
    IF Work.Net2$ = Nul THEN
       IF Default.Dir = "\" THEN
          From.File$ = Default.Dir + From.File$
       ELSE
          From.File$ = Default.Dir + "\" + From.File$
       END IF
    END IF
    IF MID$(From.File$, 2, 1) = ":" THEN
       From.File$ = MID$(From.File$, 3)
    END IF
 END IF
 IF Work.Net2$ = Nul THEN
    From.File$ = Drive.Search + ":" + From.File$
 ELSE
    IF LEFT$(From.File$, 1) = "\" THEN
       From.File$ = Work.Net2$ + From.File$
    ELSE
       From.File$ = Work.Net2$ + "\" + From.File$
    END IF
 END IF

 ' check filenames.
 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 constructs base netpath
SUB MakeFile2(From.File$)
 Work.Net$ = Rtrim$(Source.Net)
 Work.Net2$ = Rtrim$(Default.Net)
 IF MID$(From.File$, 2, 1) = ":" THEN
    From.File$ = MID$(From.File$, 3)
 END IF
 IF LEFT$(From.File$, 1) <> "\" THEN
    IF Work.Net$ = Nul AND Work.Net2$ = Nul THEN
       IF Default.Dir = "\" THEN
          From.File$ = Default.Dir + From.File$
       ELSE
          From.File$ = Default.Dir + "\" + From.File$
       END IF
    END IF
    IF MID$(From.File$, 2, 1) = ":" THEN
       From.File$ = MID$(From.File$, 3)
    END IF
 END IF
 IF Work.Net$ = Nul AND Work.Net2$ = Nul THEN
    From.File$ = Drive.Search + ":" + From.File$
 ELSE
    IF LEFT$(From.File$, 1) = "\" THEN
       IF Work.Net$ <> Nul THEN
          From.File$ = RTRIM$(Work.Net$) + From.File$
       ELSE
          From.File$ = RTRIM$(Work.Net2$) + Drive.Search + From.File$
       END IF
    ELSE
       IF Work.Net$ <> Nul THEN
          From.File$ = RTRIM$(Work.Net$) + "\" + From.File$
       ELSE
          From.File$ = RTRIM$(Work.Net2$) + Drive.Search + "\" + From.File$
       END IF
    END IF
 END IF
 From.File$ = LCASE$(From.File$)
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) = Quote THEN
    Standard.Input = MID$(Standard.Input, 2)
 END IF
 IF RIGHT$(Standard.Input, 1) = Quote THEN
    Standard.Input = LEFT$(Standard.Input, LEN(Standard.Input) - 1)
 END IF

 ' store entire command.
 IF LEFT$(Command.Line, 1) = Quote THEN
    Imbedded = INSTR(2, Command.Line, Quote)
    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 constructs file date\time string
FUNCTION MakeDate$(WorkDate!, WorkTime!, M)

 ' construct file date.
 YearTemp! = INT(WorkDate! / 512)
 MonthTemp! = INT((WorkDate! AND &H1E0) / 32)
 DayTemp! = INT(WorkDate! AND &H1F)
 YearTemp! = YearTemp! + 1980
 File.Date$ = RIGHT$(STR$(MonthTemp! + 100), 2) + "-"
 File.Date$ = File.Date$ + RIGHT$(STR$(DayTemp! + 100), 2) + "-"
 File.Date$ = File.Date$ + MID$(STR$(YearTemp!), 2)

 ' check millisecond/flag switch.
 IF M <> True THEN
    ' construct file time.
    HourTemp! = INT(WorkTime! / 2048)
    MinuteTemp! = INT((WorkTime! AND &H7E0) / 32)
    SecondsTemp! = INT((WorkTime! AND &H1F) * 2)
    ' add one second.
    IF M >= 100 THEN ' 100 * 1/10th millisecond.
       SecondsTemp! = SecondsTemp! + 1!
    END IF
    File.Time$ = RIGHT$(STR$(HourTemp! + 100), 2) + ":"
    File.Time$ = File.Time$ + RIGHT$(STR$(MinuteTemp! + 100), 2)
    IF M >= False THEN
       File.Time$ = File.Time$ + ":"
       File.Time$ = File.Time$ + RIGHT$(STR$(SecondsTemp! + 100), 2)
    END IF
    File.Date$ = File.Date$ + " " + File.Time$
 END IF

 ' create output date\time string.
 MakeDate$ = File.Date$
END FUNCTION

' routine checks file size range
SUB CheckSizeRange(Range)
 ' reset return flag.
 Range = False

 ' check for zero-byte files.
 IF Copy.Zero THEN
    IF File.Size = DFalse THEN
       Range = True
       EXIT SUB
    END IF
 END IF

 ' check file size range.
 IF Search.File.Size = False THEN
    EXIT SUB
 END IF

 ' files of zero-byte are copied.
 IF Search.Size.From = DFalse AND Search.Size.To = DFalse THEN
    IF File.Size <> DFalse THEN
       Range = True
    END IF
    EXIT SUB
 END IF

 ' check /u10-
 IF Search.Size.From > DFalse THEN
    IF Search.Size.To = DFalse THEN
       IF File.Size < Search.Size.From THEN
          Range = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /u-10
 IF Search.Size.From = DFalse THEN
    IF Search.Size.To > DFalse THEN
       IF File.Size > Search.Size.To THEN
          Range = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /u10-20
 IF Search.Size.From <= Search.Size.To THEN
    IF File.Size < Search.Size.From OR File.Size > Search.Size.To THEN
       Range = True
       EXIT SUB
    END IF
 END IF

 ' check /u20-10
 IF Search.Size.From > Search.Size.To THEN
    IF File.Size < Search.Size.From AND File.Size > Search.Size.To THEN
       Range = True
       EXIT SUB
    END IF
 END IF
END SUB

' routine checks date\time ranges
'   checks creation date\time, last access date, last modified date\time.
SUB CheckDateRange(Range)
 ' reset return flag.
 Range = False

 ' check date range.
 IF Search.Date THEN
    IF Windows.Detected THEN
       IF Compare.Creation.Date THEN ' /s1
          IF Search.From.Date > False OR Search.To.Date > False THEN
             ' /e01/01/1980-
             IF Search.From.Date > False THEN
                IF Search.To.Date = False THEN
                   IF Synch.Work.Creation.Date < Search.From.Date THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /e-12/31/2007
             IF Search.From.Date = False THEN
                IF Search.To.Date > False THEN
                   IF Synch.Work.Creation.Date > Search.To.Date THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /e01/01/1980-12/31/2007
             IF Search.From.Date <= Search.To.Date THEN
                IF Synch.Work.Creation.Date < Search.From.Date OR _
                Synch.Work.Creation.Date > Search.To.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
             ' /e12/31/2007-01/01/1980
             IF Search.From.Date > Search.To.Date THEN
                IF Synch.Work.Creation.Date < Search.From.Date AND _
                Synch.Work.Creation.Date > Search.To.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
       END IF
       IF Compare.Access.Date THEN ' /s2
          IF Search.From.Date > False OR Search.To.Date > False THEN
             ' /e01/01/1980-
             IF Search.From.Date > False THEN
                IF Search.To.Date = False THEN
                   IF Synch.Work.Access.Date < Search.From.Date THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /e-12/31/2007
             IF Search.From.Date = False THEN
                IF Search.To.Date > False THEN
                   IF Synch.Work.Access.Date > Search.To.Date THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /e01/01/1980-12/31/2007
             IF Search.From.Date <= Search.To.Date THEN
                IF Synch.Work.Access.Date < Search.From.Date OR _
                Synch.Work.Access.Date > Search.To.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
             ' /e12/31/2007-01/01/1980
             IF Search.From.Date > Search.To.Date THEN
                IF Synch.Work.Access.Date < Search.From.Date AND _
                Synch.Work.Access.Date > Search.To.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
       END IF
    END IF
    IF Compare.Modified.Date THEN ' /s3
       IF Search.From.Date > False OR Search.To.Date > False THEN
          ' /e01/01/1980-
          IF Search.From.Date > False THEN
             IF Search.To.Date = False THEN
                IF Synch.Work.Modified.Date < Search.From.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
          ' /e-12/31/2007
          IF Search.From.Date = False THEN
             IF Search.To.Date > False THEN
                IF Synch.Work.Modified.Date > Search.To.Date THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
          ' /e01/01/1980-12/31/2007
          IF Search.From.Date <= Search.To.Date THEN
             IF Synch.Work.Modified.Date < Search.From.Date OR _
             Synch.Work.Modified.Date > Search.To.Date THEN
                Range = True
                EXIT SUB
             END IF
          END IF
          ' /e12/31/2007-01/01/1980
          IF Search.From.Date > Search.To.Date THEN
             IF Synch.Work.Modified.Date < Search.From.Date AND _
             Synch.Work.Modified.Date > Search.To.Date THEN
                Range = True
                EXIT SUB
             END IF
          END IF
       END IF
    END IF
 END IF

 ' check time range.
 IF Search.Time THEN
    IF Windows.Detected THEN
       Search.From.Time = INT(Search.From.Time)
       Search.To.Time = INT(Search.To.Time)
       IF Compare.Creation.Date THEN ' /s1
          IF Search.From.Time > False OR Search.To.Time > False THEN
             ' add one second.
             Search.From.Time = Search.From.Time + Half.Second1
             Search.To.Time = Search.To.Time + Half.Second2
             ' /t00:00:01-
             IF Search.From.Time > False THEN
                IF Search.To.Time = False THEN
                   IF Synch.Work.Creation.Time < Search.From.Time THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /t-23:59:59
             IF Search.From.Time = False THEN
                IF Search.To.Time > False THEN
                   IF Synch.Work.Creation.Time > Search.To.Time THEN
                      Range = True
                      EXIT SUB
                   END IF
                END IF
             END IF
             ' /t00:00:01-23:59:59
             IF Search.From.Time <= Search.To.Time THEN
                IF Synch.Work.Creation.Time < Search.From.Time OR _
                Synch.Work.Creation.Time > Search.To.Time THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
             ' /t23:59:59-00:00:01
             IF Search.From.Time > Search.To.Time THEN
                IF Synch.Work.Creation.Time < Search.From.Time AND _
                Synch.Work.Creation.Time > Search.To.Time THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
       END IF
    END IF
    Search.From.Time = INT(Search.From.Time)
    Search.To.Time = INT(Search.To.Time)
    Search.From.Time2! = Search.From.Time AND NOT(&H1F) ' remove seconds.
    Search.To.Time2! = Search.To.Time AND NOT(&H1F) ' remove seconds.
    IF Compare.Modified.Date THEN ' /s3
       IF Search.From.Time2! > SFalse OR Search.To.Time2! > SFalse THEN
          ' /t00:00:01-
          IF Search.From.Time2! > SFalse THEN
             IF Search.To.Time2! = SFalse THEN
                IF Synch.Work.Modified.Time < Search.From.Time2! THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
          ' /t-23:59:59
          IF Search.From.Time2! = SFalse THEN
             IF Search.To.Time2! > SFalse THEN
                IF Synch.Work.Modified.Time > Search.To.Time2! THEN
                   Range = True
                   EXIT SUB
                END IF
             END IF
          END IF
          ' /t00:00:01-23:59:59
          IF Search.From.Time2! <= Search.To.Time2! THEN
             IF Synch.Work.Modified.Time < Search.From.Time2! OR _
             Synch.Work.Modified.Time > Search.To.Time2! THEN
                Range = True
                EXIT SUB
             END IF
          END IF
          ' /t23:59:59-00:00:01
          IF Search.From.Time2! > Search.To.Time2! THEN
             IF Synch.Work.Modified.Time < Search.From.Time2! AND _
             Synch.Work.Modified.Time > Search.To.Time2! THEN
                Range = True
                EXIT SUB
             END IF
          END IF
       END IF
    END IF
 END IF
END SUB

' routine verifies source - destination synchronized file size, date\time.
'   checks creation date\time, last access date, last modified date\time.
SUB CheckSynched(Synched)
 ' reset return value.
 Synched = False

 ' store last modified time and remove seconds.
 Synch.File.Modified.Time2! = INT(Synch.File.Modified.Time) AND NOT(&H1F)
 Synch.Work.Modified.Time2! = INT(Synch.Work.Modified.Time) AND NOT(&H1F)

 ' check /a
 IF Synch.Files THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF File.Size = Dest.File.Size THEN
             IF Synch.Work.Creation.Date = Synch.File.Creation.Date THEN
                IF Synch.Work.Creation.Time = Synch.File.Creation.Time THEN
                   Synched = True
                   EXIT SUB
                END IF
             END IF
          END IF
       END IF
       IF Synch.Type2 THEN ' last access date.
          IF File.Size = Dest.File.Size THEN
             IF Synch.Work.Access.Date = Synch.File.Access.Date THEN
                Synched = True
                EXIT SUB
             END IF
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF File.Size = Dest.File.Size THEN
          IF Synch.Work.Modified.Date = Synch.File.Modified.Date THEN
             IF Synch.Work.Modified.Time2! = Synch.File.Modified.Time2! THEN
                Synched = True
                EXIT SUB
             END IF
          END IF
       END IF
    END IF
 END IF

 ' check /a1
 IF Synch.Files1 THEN
    IF File.Size >= Dest.File.Size THEN
       Synched = True
       EXIT SUB
    END IF
 END IF

 ' check /a2
 IF Synch.Files2 THEN
    IF File.Size <= Dest.File.Size THEN
       Synched = True
       EXIT SUB
    END IF
 END IF

 ' check /a3
 IF Synch.Files3 THEN
    IF File.Size <> Dest.File.Size THEN
       Synched = True
       EXIT SUB
    END IF
 END IF

 ' check /a4
 IF Synch.Files4 THEN
    IF File.Size = Dest.File.Size THEN
       Synched = True
       EXIT SUB
    END IF
 END IF

 ' check /a5
 IF Synch.Files5 THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Date >= Synch.File.Creation.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
       IF Synch.Type2 THEN ' last access date.
          IF Synch.Work.Access.Date >= Synch.File.Access.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Date >= Synch.File.Modified.Date THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /a6
 IF Synch.Files6 THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Date <= Synch.File.Creation.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
       IF Synch.Type2 THEN ' last access date.
          IF Synch.Work.Access.Date <= Synch.File.Access.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Date <= Synch.File.Modified.Date THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /a7
 IF Synch.Files7 THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Date <> Synch.File.Creation.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
       IF Synch.Type2 THEN ' last access date.
          IF Synch.Work.Access.Date <> Synch.File.Access.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Date <> Synch.File.Modified.Date THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /a8
 IF Synch.Files8 THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Date = Synch.File.Creation.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
       IF Synch.Type2 THEN ' last access date.
          IF Synch.Work.Access.Date = Synch.File.Access.Date THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Date = Synch.File.Modified.Date THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /a9
 IF Synch.Files9 THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Time >= Synch.File.Creation.Time THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Time2! >= Synch.File.Modified.Time2! THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /aa
 IF Synch.FilesA THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Time <= Synch.File.Creation.Time THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Time2! <= Synch.File.Modified.Time2! THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /ab
 IF Synch.FilesB THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Time <> Synch.File.Creation.Time THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Time2! <> Synch.File.Modified.Time2! THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF

 ' check /ac
 IF Synch.FilesC THEN
    IF Windows.Detected THEN
       IF Synch.Type1 THEN ' creation date\time.
          IF Synch.Work.Creation.Time = Synch.File.Creation.Time THEN
             Synched = True
             EXIT SUB
          END IF
       END IF
    END IF
    IF Synch.Type3 THEN ' last modified date\time.
       IF Synch.Work.Modified.Time2! = Synch.File.Modified.Time2! THEN
          Synched = True
          EXIT SUB
       END IF
    END IF
 END IF
END SUB

' routine preserves destination file date\time.
' TimeType:
'   1 - read dos filename date\time
'   2 - write dos filename date\time
'   3 - read windows filename date\time
'   4 - write windows filename date\time
'   5 - read windows directory date\time
'   6 - write windows directory date\time
SUB ExtendedFile(TimeType)
 SELECT CASE TimeType
 ' read dos filename date\time.
 CASE 1
    ' read date\time.
    InregsX.AX = &H5700
    InregsX.BX = Handle
    CALL InterruptX(&H21, InregsX, OutregsX)
    Source.File.Time = OutregsX.CX
    Source.File.Date = OutregsX.DX
 ' write dos filename date\time.
 CASE 2
    InregsX.AX = &H5701
    InregsX.BX = Append.Handle
    IF Override.Time THEN
       InregsX.CX = Override.Time
    ELSE
       InregsX.CX = Source.File.Time
    END IF
    IF Override.Date THEN
       InregsX.DX = Override.Date
    ELSE
       InregsX.DX = Source.File.Date
    END IF
    CALL InterruptX(&H21, InregsX, OutregsX)
 ' read filename extended date\time.
 CASE 3
    ' open long filename search.
    InregsX.AX = &H714E
    InregsX.CX = &H27
    InregsX.SI = &H1
    InregsX.DS = VARSEG(ASCIIZ.Copy)
    InregsX.DX = VARPTR(ASCIIZ.Copy)
    InregsX.ES = VARSEG(WDTAfile)
    InregsX.DI = VARPTR(WDTAfile)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Wfile.Handle = OutregsX.AX

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

    ' store file info.
    Windows.LastWrite.Date = ASC(MID$(WDTAfile.ModTime, 4, 1))
    Windows.LastWrite.Date = Windows.LastWrite.Date * &H100 + ASC(MID$(WDTAfile.ModTime, 3, 1))
    Windows.LastWrite.Time = ASC(MID$(WDTAfile.ModTime, 2, 1))
    Windows.LastWrite.Time = Windows.LastWrite.Time * &H100 + ASC(MID$(WDTAfile.ModTime, 1, 1))
    Windows.Creation.Date = ASC(MID$(WDTAfile.CreateTime, 4, 1))
    Windows.Creation.Date = Windows.Creation.Date * &H100 + ASC(MID$(WDTAfile.CreateTime, 3, 1))
    Windows.Creation.Time = ASC(MID$(WDTAfile.CreateTime, 2, 1))
    Windows.Creation.Time = Windows.Creation.Time * &H100 + ASC(MID$(WDTAfile.CreateTime, 1, 1))
    Windows.LastAccess.Date = ASC(MID$(WDTAfile.AccessTime, 4, 1))
    Windows.LastAccess.Date = Windows.LastWrite.Date * &H100 + ASC(MID$(WDTAfile.AccessTime, 3, 1))

    ' check override switches.
    IF Override.Date THEN
       IF Override.Creation.Date THEN 
          Windows.Creation.Date = Override.Date
       END IF
       IF Override.Access.Date THEN 
          Windows.LastAccess.Date = Override.Date
       END IF
       IF Override.Modified.Date THEN 
          Windows.LastWrite.Date = Override.Date
       END IF
    END IF
    IF Override.Time THEN
       IF Override.Creation.Date THEN 
          Windows.Creation.Time = Override.Time
          Windows.Creation.Milli = Millisecond
       END IF
       IF Override.Modified.Date THEN 
          Windows.LastWrite.Time = Override.Time
       END IF
    END IF
 ' write filename extended date\time.
 CASE 4
    ' creation date\time.
    IF Override.Creation.Date THEN
       InregsX.AX = &H7143
       InregsX.BX = &H07
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       InregsX.CX = Windows.Creation.Time
       InregsX.DI = Windows.Creation.Date
       InregsX.SI = Windows.Creation.Milli
       CALL InterruptX(&H21, InregsX, OutregsX)
       ' display any errors.
       IF Debug.Mode THEN
          CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H touching filename creation date\time.")
       END IF
    END IF

    ' last access date.
    IF Override.Access.Date THEN
       InregsX.AX = &H7143
       InregsX.BX = &H05
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       InregsX.DI = Windows.LastAccess.Date
       CALL InterruptX(&H21, InregsX, OutregsX)
       ' display any errors.
       IF Debug.Mode THEN
          CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H touching filename last access date.")
       END IF
    END IF

    ' last modified date\time.
    IF Override.Modified.Date THEN
       InregsX.AX = &H7143
       InregsX.BX = &H03
       InregsX.DS = VARSEG(ASCIIZfile)
       InregsX.DX = VARPTR(ASCIIZfile)
       InregsX.CX = Windows.LastWrite.Time
       InregsX.DI = Windows.LastWrite.Date
       CALL InterruptX(&H21, InregsX, OutregsX)
       ' display any errors.
       IF Debug.Mode THEN
          CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H touching filename last modified date\time.")
       END IF
    END IF
 ' read directory extended date\time.
 CASE 5
    IF Source.Net <> Nul THEN
       EXIT SUB
    END IF

    ' read directory date/time.
    InregsX.AX = &H7143
    InregsX.BX = &H04
    InregsX.DS = VARSEG(ASCIIZ.Dir)
    InregsX.DX = VARPTR(ASCIIZ.Dir)
    CALL InterruptX(&H21, InregsX, OutregsX)
    Directory.LastWrite.Time = OutregsX.CX
    Directory.LastWrite.Date = OutregsX.DI

    ' display any errors.
    IF Debug.Mode THEN
       CALL DisplayError("Error " + HEX$(OutregsX.AX) + "H reading directory last modified date\time.")
    END IF
 ' write directory extended date\time.
 CASE 6
    ' check nul directory.
    IF MID$(ASCIIZ.Dir, 2, 2) = ":" + CHR$(0) THEN
       EXIT SUB
    END IF
    IF LEFT$(ASCIIZ.Dir, 2) = "\\" THEN
       EXIT SUB
    END IF
    IF Source.Net <> Nul THEN
       EXIT SUB
    END IF

    ' write directory date/time.
    InregsX.AX = &H7143
    InregsX.BX = &H03
    InregsX.DS = VARSEG(ASCIIZ.Dir)
    InregsX.DX = VARPTR(ASCIIZ.Dir)
    InregsX.CX = Directory.LastWrite.Time
    InregsX.DI = Directory.LastWrite.Date
    CALL InterruptX(&H21, InregsX, OutregsX)

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

' routine checks filename
SUB CheckFile(Var$,Flag,Check)
 ' store test flag
 Flag = True

 ' store input filename
 Var2$ = UCASE$(Var$)
 Var2$ = RTRIM$(Var2$)

 ' check skip files
 FOR Skip.File = 1 TO 10
    Var1$ = Skip.Filenames(Skip.File)
    Var1$ = UCASE$(Var1$)
    Var1$ = RTRIM$(Var1$)
    IF LEN(Var1$) THEN
       IF Var1$ = Var2$ THEN
          Flag = False
          EXIT SUB
       END IF
    END IF
 NEXT

 ' check excluded files
 FOR File.Number = 1 TO Number.Excluded
    Var1$ = Excluded.Files(File.Number)
    Var1$ = UCASE$(Var1$)
    Var1$ = RTRIM$(Var1$)
    IF LEN(Var1$) THEN
       CALL CheckExcluded(Var1$,Var2$,Exclude.File)
       IF Exclude.File THEN
          Flag = False
          EXIT SUB
       END IF
    END IF
 NEXT

 ' check pattern files
 IF Check THEN
    IF Pattern.Match THEN
       EXIT SUB
    END IF
 END IF
 CALL CheckPattern(Var2$,Exclude.File)
 IF Exclude.File = False THEN
    Flag = False
 END IF
END SUB

' routine compares occurrence of filename1$ in filename2$
'  with pattern matching.
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

' routine checks filename date pattern
SUB CheckPattern(Filename2$, Match)
 ' reset to match.
 Match = True
 ' exit for no matching.
 IF Pattern.Search = False THEN
    EXIT SUB
 END IF
 ' compare exact filename lengths.
 IF LEN(Filename2$) <> LEN(Pattern.Filename) THEN
    Match = False
    EXIT SUB
 END IF
 ' scan pattern.
 FOR Var = 1 TO LEN(Pattern.Filename)
    Var$ = MID$(Pattern.Filename, Var, 1)
    SELECT CASE Var$
    CASE "?" ' nul
       Var0$ = Nul
    CASE "M", "D", "Y" ' skip
       Var0$ = Nul
    CASE ELSE
       ' match exact filename character.
       IF MID$(Filename2$, Var, 1) <> Var$ THEN
          Match = False
          EXIT SUB
       END IF
    END SELECT
 NEXT
 ' scan year pattern type 1.
 Year1 = False
 FOR Var = 1 TO LEN(Pattern.Filename)
    IF MID$(Pattern.Filename, Var, 4) = "YYYY" THEN
       Year1 = VAL(MID$(Filename2$, Var, 4))
       EXIT FOR
    END IF
 NEXT
 ' scan year pattern type 2.
 IF Year1 = False THEN
    FOR Var = 1 TO LEN(Pattern.Filename)
       IF MID$(Pattern.Filename, Var, 2) = "YY" THEN
          Year1 = VAL(MID$(Filename2$, Var, 2))
          EXIT FOR
       END IF
    NEXT
 END IF
 ' scan month pattern.
 Month1 = False
 FOR Var = 1 TO LEN(Pattern.Filename)
    IF MID$(Pattern.Filename, Var, 2) = "MM" THEN
       Month1 = VAL(MID$(Filename2$, Var, 2))
       EXIT FOR
    END IF
 NEXT
 ' scan day pattern.
 Day1 = False
 FOR Var = 1 TO LEN(Pattern.Filename)
    IF MID$(Pattern.Filename, Var, 2) = "DD" THEN
       Day1 = VAL(MID$(Filename2$, Var, 2))
       EXIT FOR
    END IF
 NEXT

 ' store date patterns.
 Day.From = VAL(MID$(Pattern.Search.From.Date, 4, 2))
 Month.From = VAL(LEFT$(Pattern.Search.From.Date, 2))
 Year.From = VAL(RIGHT$(Pattern.Search.From.Date, 4))
 Day.To = VAL(MID$(Pattern.Search.To.Date, 4, 2))
 Month.To = VAL(LEFT$(Pattern.Search.To.Date, 2))
 Year.To = VAL(RIGHT$(Pattern.Search.To.Date, 4))

 ' match date patterns. checks all possible patterned combinations.
 IF Day1 > False AND Month1 > False AND Year1 > False THEN ' DD-MM-YYYY
    ' for a complete date, serial function can be used.
    From.Serial# = DateSerial(Year.From, Month.From, Day.From)
    To.Serial# = DateSerial(Year.To, Month.To, Day.To)
    Now.Serial# = DateSerial(Year1, Month1, Day1)
    IF From.Serial# <= To.Serial# THEN
       IF Now.Serial# >= From.Serial# AND Now.Serial# <= To.Serial# THEN
          Match = True
       ELSE
          Match = False
       END IF
    ELSE
       Match = False
    END IF
 ELSE
    ' for all remaining combinations, specific ranges must be checked.
    IF Day1 > False AND Month1 > False AND Year1 = False THEN ' DD-MM
       IF Month.From = Month.To THEN
          IF Month1 = Month.From THEN
             IF Day.From <= Day.To THEN
                IF Day1 >= Day.From AND Day1 <= Day.To THEN
                   Match = True
                ELSE
                   Match = False
                END IF
             ELSE
                Match = False
             END IF
          ELSE
             Match = False
          END IF
       ELSE
          IF Month.From < Month.To THEN
             IF Month1 >= Month.From AND Month1 <= Month.To THEN
                IF Day.From > Day.To THEN
                   IF Day1 >= Day.From OR Day1 <= Day.To THEN
                      Match = True
                   ELSE
                      Match = False
                   END IF
                ELSE
                   IF Day1 >= Day.From AND Day1 <= Day.To THEN
                      Match = True
                   ELSE
                      Match = False
                   END IF
                END IF
             ELSE
                Match = False
             END IF
          ELSE
             Match = False
          END IF
       END IF
    ELSE
       IF Day1 > False AND Month1 = False AND Year1 > False THEN ' DD-YYYY
          IF Year.From = Year.To THEN
             IF Year1 = Year.From THEN
                IF Day.From <= Day.To THEN
                   IF Day1 >= Day.From AND Day1 <= Day.To THEN
                      Match = True
                   ELSE
                      Match = False
                   END IF
                ELSE
                   Match = False
                END IF
             ELSE
                Match = False
             END IF
          ELSE
             IF Year.From < Year.To THEN
                IF Year1 >= Year.From AND Year1 <= Year.To THEN
                   IF Day.From > Day.To THEN
                      IF Day1 >= Day.From OR Day1 <= Day.To THEN
                         Match = True
                      ELSE
                         Match = False
                      END IF
                   ELSE
                      IF Day1 >= Day.From AND Day1 <= Day.To THEN
                         Match = True
                      ELSE
                         Match = False
                      END IF
                   END IF
                ELSE
                   Match = False
                END IF
             ELSE
                Match = False
             END IF
          END IF
       ELSE
          IF Day1 > False AND Month1 = False AND Year1 = False THEN ' DD
             IF Day1 >= Day.From AND Day1 <= Day.To THEN
                Match = True
             ELSE
                Match = False
             END IF
          ELSE
             IF Day1 = False AND Month1 > False AND Year1 > False THEN ' MM-YYYY
                IF Year.From = Year.To THEN
                   IF Year1 = Year.From THEN
                      IF Month.From <= Month.To THEN
                         IF Month1 >= Month.From AND Month1 <= Month.To THEN
                            Match = True
                         ELSE
                            Match = False
                         END IF
                      ELSE
                         Match = False
                      END IF
                   ELSE
                      Match = False
                   END IF
                ELSE
                   IF Year.From < Year.To THEN
                      IF Year1 >= Year.From AND Year1 <= Year.To THEN
                         IF Month.From > Month.To THEN
                            IF Month1 >= Month.From OR Month1 <= Month.To THEN
                               Match = True
                            ELSE
                               Match = False
                            END IF
                         ELSE
                            IF Month1 >= Month.From AND Month1 <= Month.To THEN
                               Match = True
                            ELSE
                               Match = False
                            END IF
                         END IF
                      ELSE
                         Match = False
                      END IF
                   ELSE
                      Match = False
                   END IF
                END IF
             ELSE
                IF Day1 = False AND Month1 > False AND Year1 = False THEN ' MM
                   IF Month1 >= Month.From AND Month1 <= Month.To THEN
                      Match = True
                   ELSE
                      Match = False
                   END IF
                ELSE
                   IF Day1 = False AND Month1 = False AND Year1 > False THEN ' YYYY
                      IF Year1 >= Year.From AND Year1 <= Year.To THEN
                         Match = True
                      ELSE
                         Match = False
                      END IF
                   ELSE
                      IF Day1 = False AND Month1 = False AND Year1 = False THEN ' nul
                         ' no date in pattern, so filename matches.
                         EXIT SUB
                      END IF
                   END IF
                END IF
             END IF
          END IF
       END IF
    END IF
 END IF
END SUB

REM -eof-
