REM File compare utility. v1.3a PD 07/17/2007.

' store all default variables as integer
DEFINT A-Z

' dimension arrays at runtime
REM $DYNAMIC

' read include files
REM $INCLUDE: 'qbx.bi'
REM $INCLUDE: 'dta.bi'

' declare common variables
COMMON SHARED ASCIIZ AS STRING * 260
COMMON SHARED ASCIIZshort AS STRING * 13
COMMON SHARED BasicDTAseg AS INTEGER
COMMON SHARED BasicDTAoff AS INTEGER
COMMON SHARED Buffer1 AS STRING * 4096
COMMON SHARED Buffer2 AS STRING * 4096
COMMON SHARED BufferFileName1 AS STRING
COMMON SHARED BufferFileName2 AS STRING
COMMON SHARED BypassPrompt AS INTEGER
COMMON SHARED CompareSize AS INTEGER
COMMON SHARED File1 AS STRING * 260
COMMON SHARED File2 AS STRING * 260
COMMON SHARED FileLength1 AS DOUBLE
COMMON SHARED FileLength2 AS DOUBLE
COMMON SHARED FileName AS STRING * 260
COMMON SHARED FileName2 AS STRING
COMMON SHARED Handle1 AS INTEGER
COMMON SHARED Handle2 AS INTEGER
COMMON SHARED NumberBytes1 AS INTEGER
COMMON SHARED NumberBytes2 AS INTEGER
COMMON SHARED Position AS DOUBLE
COMMON SHARED Quote AS STRING * 1
COMMON SHARED TotalBytes AS DOUBLE

' declare dta structure
COMMON SHARED DTAfile AS DtaType

' declare interrupt structures
COMMON SHARED OutregsX AS RegTypeX
COMMON SHARED InregsX AS RegTypeX

' declare time slice release variable.
COMMON SHARED Supported.Call AS INTEGER

' declare constants
CONST True = -1
CONST False = 0
CONST DFalse = 0#
CONST Nul = ""
Quote = CHR$(34)

' declare time slice release function.
FUNCTION ReleaseTime
 ON LOCAL ERROR RESUME NEXT
 DIM OutregsX2 AS RegTypeX, InregsX2 AS RegTypeX
 IF Supported.Call = 0 THEN
    InregsX2.AX = &H1680
    InregsX2.BX = &H0
    CALL InterruptX(&H2F, InregsX2, OutregsX2)
    IF (OutregsX2.AX AND &HFF) = &H80 THEN
       Supported.Call = -1
    END IF
 END IF
 ReleaseTime = -1
END FUNCTION

' declare error routine
ON ERROR GOTO Error.Routine

' store basic dta
InregsX.AX = &H2F00
CALL InterruptX(&H21, InregsX, OutregsX)
BasicDTAseg = OutregsX.ES
BasicDTAoff = OutregsX.BX

' display banner
COLOR 15, 0
PRINT "File compare v1.3a PD 2007."

' check command line
X$ = COMMAND$
IF X$ = "/?" THEN
   GOTO BootUsage
END IF
L = INSTR(X$, "/C")
IF L > 0 THEN
   BypassPrompt = True
   X$ = LEFT$(X$, L - 1) + MID$(X$, L + 2)
END IF
L = INSTR(X$, "/L")
IF L > 0 THEN
   DisplayType = 1
   X$ = LEFT$(X$, L - 1) + MID$(X$, L + 2)
ELSE
   FOR Z = 1 TO 4
      Z$ = "/P" + MID$(STR$(Z), 2)
      L = INSTR(X$, Z$)
      IF L > 0 THEN
         X$ = LEFT$(X$, L - 1) + MID$(X$, L + 3)
         PortNumber = Z
         FileName2 = "LPT" + Z$ + ":"
         OPEN FileName2 FOR APPEND AS #1
         DisplayType = 3
         EXIT FOR
      END IF
   NEXT
END IF
L = INSTR(X$, "/X")
IF L > 0 THEN
   CompareSize = True
   X$ = LEFT$(X$, L - 1) + MID$(X$, L + 2)
END IF
DO
   X$ = LTRIM$(X$)
   X$ = RTRIM$(X$)
   IF LEFT$(X$, 3) = "/O:" THEN
      IF DisplayType > 0 THEN
         GOTO BootError
      END IF
      F = 0
      X$ = MID$(X$, 4)
      IF LEFT$(X$, 1) = Quote THEN
         FOR L = 2 TO LEN(X$)
            IF MID$(X$, L, 1) = Quote THEN
               DisplayType = 2
               FileName2 = LEFT$(X$, L - 1)
               FileName2 = MID$(FileName2, 2)
               OPEN FileName2 FOR APPEND AS #1
               X$ = MID$(X$, L + 1)
               F = -1
               EXIT FOR
            END IF
         NEXT
      END IF
      IF F = 0 THEN
         GOTO BootError
      END IF
   ELSE
      IF LEFT$(X$, 4) = "/F1:" THEN
         IF LEN(F1$) THEN
            GOTO BootError
         END IF
         F = 0
         X$ = MID$(X$, 5)
         IF LEFT$(X$, 1) = Quote THEN
            FOR L = 2 TO LEN(X$)
               IF MID$(X$, L, 1) = Quote THEN
                  F1$ = LEFT$(X$, L - 1)
                  F1$ = MID$(F1$, 2)
                  X$ = MID$(X$, L + 1)
                  F = -1
                  EXIT FOR
               END IF
            NEXT
         END IF
         IF F = 0 THEN
            GOTO BootError
         END IF
      ELSE
         IF LEFT$(X$, 4) = "/F2:" THEN
            IF LEN(F2$) THEN
               GOTO BootError
            END IF
            F = 0
            X$ = MID$(X$, 5)
            IF LEFT$(X$, 1) = Quote THEN
               FOR L = 2 TO LEN(X$)
                  IF MID$(X$, L, 1) = Quote THEN
                     F2$ = LEFT$(X$, L - 1)
                     F2$ = MID$(F2$, 2)
                     X$ = MID$(X$, L + 1)
                     F = -1
                     EXIT FOR
                  END IF
               NEXT
            END IF
            IF F = 0 THEN
               GOTO BootError
            END IF
         ELSE
            EXIT DO
         END IF
      END IF
   END IF
LOOP
X$ = RTRIM$(X$)
IF LEN(X$) THEN
   GOTO BootError
END IF
IF DisplayType > 0 THEN
   GOTO StartLoop
END IF

' read output type
COLOR 14, 0
DO
   PRINT "Output type(1=screen,2=file,3=printer,4=quit)? ";
   LOCATE ,,1
   DO
      InputChar$ = INKEY$
      IF InputChar$ >= "1" AND InputChar$ <= "4" THEN
         EXIT DO
      END IF
      ' release time slice
      R = ReleaseTime
   LOOP
   PRINT InputChar$
   DisplayType = INT(VAL(InputChar$))
   SELECT CASE DisplayType
   CASE 1
      EXIT DO
   CASE 2
      PRINT "Enter output filename: ";
      LINE INPUT FileName2
      IF DIR$(FileName2) <> Nul THEN
         PRINT "File exists. Append anyway(y/n)";
         DO
            InputChar$ = INKEY$
            IF LCASE$(InputChar$) = "y" THEN
               PRINT "y"
               EXIT DO
            END IF
            IF LCASE$(InputChar$) = "n" THEN
               PRINT "n"
               GOTO Terminate
            END IF
            ' release time slice
            R = ReleaseTime
         LOOP
      END IF
      OPEN FileName2 FOR APPEND AS #1
      EXIT DO
   CASE 3
      PRINT "Enter printer port(1-4)? ";
      DO
         InputChar$ = INKEY$
         IF InputChar$ >= "1" AND InputChar$ <= "4" THEN
            EXIT DO
         END IF
         ' release time slice
         R = ReleaseTime
      LOOP
      PRINT InputChar$
      PortNumber = INT(VAL(InputChar$))
      FileName2 = "LPT" + MID$(STR$(PortNumber), 2) + ":"
      OPEN FileName2 FOR APPEND AS #1
      EXIT DO
   CASE 4
      GOTO Terminate
   END SELECT
LOOP
StartLoop:

' read filename#1
DO
   IF LEN(F1$) THEN
      BufferFileName1 = F1$
   ELSE
      COLOR 14, 0
      PRINT "Enter filename#1 to compare: ";
      LINE INPUT BufferFileName1
      IF BufferFileName1 = Nul THEN
         GOTO Terminate
      END IF
   END IF
   FileName = BufferFileName1
   File1 = BufferFileName1 + CHR$(0)
   GOSUB GetFileInfo
   IF Temp# = DFalse THEN
      COLOR 15
      PRINT "File: '"; BufferFileName1; "' is zero-length."
      IF LEN(F1$) THEN
         GOTO Terminate
      END IF
   ELSE
      IF Temp# = -1# THEN
         COLOR 15
         PRINT "File: '"; BufferFileName1; "' does not exist."
         IF LEN(F1$) THEN
            GOTO Terminate
         END IF
      ELSE
         EXIT DO
      END IF
   END IF
LOOP
FileLength1 = Temp#
BufferFileName1 = RTRIM$(ASCIIZshort)

' read filename#2
DO
   IF LEN(F2$) THEN
      BufferFileName2 = F2$
   ELSE
      COLOR 14, 0
      PRINT "Enter filename#2 to compare: ";
      LINE INPUT BufferFileName2
      IF BufferFileName2 = Nul THEN
         GOTO Terminate
      END IF
   END IF
   FileName = BufferFileName2
   File2 = BufferFileName2 + CHR$(0)
   GOSUB GetFileInfo
   IF Temp# = DFalse THEN
      COLOR 15
      PRINT "File: '"; BufferFileName2;"' is zero-length."
      IF LEN(F2$) THEN
         GOTO Terminate
      END IF
   ELSE
      IF Temp# = -1# THEN
         COLOR 15
         PRINT "File '"; BufferFileName2;"' does not exist."
         IF LEN(F2$) THEN
            GOTO Terminate
         END IF
      ELSE
         EXIT DO
      END IF
   END IF
LOOP
FileLength2 = Temp#
BufferFileName2 = RTRIM$(ASCIIZshort)

' compare file sizes
IF CompareSize THEN
   GOTO NextLoop
END IF
IF FileLength1 <> FileLength2 THEN
   PRINT "Files are different size. Compare anyway(y/n)? ";
   DO
      InputChar$ = INKEY$
      IF LCASE$(InputChar$) = "y" THEN
         PRINT "y"
         EXIT DO
      END IF
      IF LCASE$(InputChar$) = "n" THEN
         PRINT "n"
         GOTO Terminate
      END IF
      ' release time slice
      R = ReleaseTime
   LOOP
END IF
NextLoop:

' open file1 for input
InregsX.AX = &H6C00
InregsX.BX = &H44
InregsX.CX = &H27
InregsX.DX = &H1
InregsX.DS = VARSEG(File1)
InregsX.SI = VARPTR(File1)
CALL InterruptX(&H21, InregsX, OutregsX)

' check open file error
IF (OutregsX.Flags AND &H1) = &H1 THEN
   PRINT "Error"; OutregsX.AX; "error opening input file: " + BufferFileName1
   GOTO Terminate
END IF

' store file handle
Handle1 = OutregsX.AX

' open file2 for input
InregsX.AX = &H6C00
InregsX.BX = &H44
InregsX.CX = &H27
InregsX.DX = &H1
InregsX.DS = VARSEG(File2)
InregsX.SI = VARPTR(File2)
CALL InterruptX(&H21, InregsX, OutregsX)

' check open file error
IF (OutregsX.Flags AND &H1) = &H1 THEN
   PRINT "Error"; OutregsX.AX; "error opening input file: " + BufferFileName2
   GOTO Terminate
END IF

' store file handle
Handle2 = OutregsX.AX

' reset display flags
Display1 = False
QuitFlag = False

' file input loop
DO
   ' read from file1
   Buffer1 = Nul
   InregsX.AX = &H3F00
   InregsX.BX = Handle1
   InregsX.CX = 4096
   InregsX.DS = VARSEG(Buffer1)
   InregsX.DX = VARPTR(Buffer1)
   CALL InterruptX(&H21, InregsX, OutregsX)

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

   ' check number of bytes
   NumberBytes1 = OutregsX.AX
   IF NumberBytes1 = False THEN
      EXIT DO
   END IF

   ' read from file2
   Buffer2 = Nul
   InregsX.AX = &H3F00
   InregsX.BX = Handle2
   InregsX.CX = 4096
   InregsX.DS = VARSEG(Buffer2)
   InregsX.DX = VARPTR(Buffer2)
   CALL InterruptX(&H21, InregsX, OutregsX)

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

   ' check number of bytes
   NumberBytes2 = OutregsX.AX
   IF NumberBytes2 = False THEN
      EXIT DO
   END IF

   ' reset least number of bytes
   IF NumberBytes2 < NumberBytes1 THEN
      NumberBytes1 = NumberBytes2
   END IF

   ' check buffers
   IF Buffer1 <> Buffer2 THEN

      ' scan all buffered bytes
      FOR Var = 1 TO NumberBytes1

         ' check byte values
         IF MID$(Buffer1, Var, 1) <> MID$(Buffer2, Var, 1) THEN

            ' check display flag
            IF Display1 = False THEN
               GOSUB Header
            END IF

            ' display prompt
            IF DisplayType = 1 THEN
               IF LineCount = 23 THEN
                  IF BypassPrompt = False THEN
                     COLOR 15, 0
                     PRINT "Press any key(q to quit):";
                     LOCATE , , 1
                     InputChar$ = Nul
                     DO
                        InputChar$ = INKEY$
                        IF InputChar$ <> Nul THEN
                           IF LCASE$(InputChar$) = "q" THEN
                              PRINT "q";
                              QuitFlag = True
                           END IF
                           EXIT DO
                        END IF
                        ' release time slice
                        R = ReleaseTime
                     LOOP
                     PRINT
                     IF QuitFlag THEN
                        EXIT FOR
                     END IF
                  END IF
                  GOSUB Header
               END IF
            END IF

            ' printer formfeed
            IF DisplayType = 3 THEN
               IF LineCount >= 55 THEN
                  PRINT #1, CHR$(12);
                  GOSUB Header
               END IF
            END IF

            ' create output string
            Output$ = "0x" + RIGHT$("00000000" + HEX$(Position + Var - 1), 8) + " "
            Output$ = Output$ + "(" + RIGHT$("0000000000" + MID$(STR$(Position + Var), 2), 10) + ") "

            Var1 = ASC(MID$(Buffer1, Var, 1))
            Var1$ = "0x" + RIGHT$("000" + HEX$(Var1), 2) + " "
            Var1$ = Var1$ + "(" + RIGHT$("000" + MID$(STR$(Var1), 2), 3) + ")"

            Var2 = ASC(MID$(Buffer2, Var, 1))
            Var2$ = "0x" + RIGHT$("000" + HEX$(Var2), 2) + " "
            Var2$ = Var2$ + "(" + RIGHT$("000" + MID$(STR$(Var2), 2), 3) + ")"

            Output$ = Output$ + "   " + Var1$
            Output$ = Output$ + "   " + Var2$

            ' output string
            SELECT CASE DisplayType
            CASE 1 ' display on screen
               COLOR 14, 0
               PRINT Output$
               LineCount = LineCount + 1
            CASE 2 ' print to file
               PRINT #1, Output$
            CASE 3 ' send to printer
               PRINT #1, Output$
               LineCount = LineCount + 1
            END SELECT

            ' increment mismatched byte counter
            TotalBytes = TotalBytes + 1#
         END IF
      NEXT
   END IF

   ' get keyboard input
   InputChar$ = INKEY$

   ' check escape key
   IF InputChar$ = CHR$(27) THEN
      QuitFlag = True
   END IF

   ' check quit flag
   IF QuitFlag THEN
      EXIT DO
   END IF

   ' increment file position
   Position = Position + NumberBytes1

   ' release time slice
   R = ReleaseTime
LOOP

' close file1
InregsX.AX = &H3E00
InregsX.BX = Handle1
CALL InterruptX(&H21, InregsX, OutregsX)

' close file2
InregsX.AX = &H3E00
InregsX.BX = Handle2
CALL InterruptX(&H21, InregsX, OutregsX)

' display final message
COLOR 15, 0
IF Display1 = False THEN
   Output$ = "Files: " + BufferFileName1 + "(" + MID$(STR$(FileLength1), 2) + ")"
   Output$ = Output$ + " - " + BufferFileName2 + "(" + MID$(STR$(FileLength2), 2) + ")"
   Output$ = Output$ + " are equal."
   PRINT Output$
   IF DisplayType = 2 THEN
      PRINT #1, ""
      PRINT #1, "FileComp: " + DATE$ + " " + TIME$
      PRINT #1, Output$
   ELSE
      IF DisplayType = 3 THEN
         PRINT #1, Output$
      END IF
   END IF
ELSE
   Output$ = "Total bytes not matching:" + STR$(TotalBytes)
   PRINT Output$
   IF DisplayType >= 2 THEN
      PRINT #1, Output$
   END IF
END IF
IF DisplayType >= 2 THEN
   PRINT "File info appended to " + FileName2
END IF
IF DisplayType = 3 THEN
   PRINT #1, CHR$(12);
END IF
IF DisplayType = 1 THEN
   IF BypassPrompt = False THEN
      PRINT "Press any key:";
      LOCATE , , 1
      WHILE INKEY$ = Nul
         ' release time slice
         R = ReleaseTime
      WEND
      PRINT
   END IF
END IF

' terminate program
Terminate:
COLOR 7, 0
PRINT "Returning to system:"
END

' display header
Header:
 COLOR 15, 0
 LineCount = 2
 IF Display1 = False THEN
    LineCount = 3
    Output2$ = "Files: " + BufferFileName1 + "(" + MID$(STR$(FileLength1), 2) + ")"
    Output2$ = Output2$ + " - " + BufferFileName2 + "(" + MID$(STR$(FileLength2), 2) + ")"
    IF DisplayType = 1 THEN
       PRINT Output2$
    ELSE
       IF DisplayType = 2 THEN
          PRINT #1, ""
          PRINT #1, "FileComp: " + DATE$ + " " + TIME$
          PRINT #1, Output2$
       ELSE
          PRINT #1, Output2$
       END IF
    END IF
 END IF
 IF Display1 THEN
    IF DisplayType = 1 THEN
       IF BypassPrompt THEN
          RETURN
       END IF
    END IF
 END IF
 Display1 = True
 Strng1$ = BufferFileName1
 Strng1$ = Strng1$ + Space$(12 - Len(Strng1$))
 Strng2$ = BufferFileName2
 Strng2$ = Strng2$ + Space$(12 - Len(Strng2$))
 Output2$ = "Position offset (hex/asc)  " + Strng1$ + " " + Strng2$
 Output3$ = "-------------------------  ------------ ------------"
 IF DisplayType = 1 THEN
    PRINT Output2$
    PRINT Output3$
 ELSE
    PRINT #1, Output2$
    PRINT #1, Output3$
 END IF
 RETURN

' read filesize/8.3 shortfilename from asciiz filename
GetFileInfo:
 ' reset filesize
 Temp# = DFalse

 ' store asciiz filename
 ASCIIZ = RTRIM$(FileName) + CHR$(0)

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

 ' find first filename
 InregsX.AX = &H4E00
 InregsX.CX = &H27
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 CALL InterruptX(&H21, InregsX, OutregsX)

 ' check findfirst error
 IF (OutregsX.Flags AND &H1) = &H1 THEN
    Temp# = -1#
 ELSE
    ' store file size
    Temp# = ASC(MID$(DTAfile.FileSize, 4, 1))
    Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 3, 1))
    Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 2, 1))
    Temp# = Temp# * &H100 + ASC(MID$(DTAfile.FileSize, 1, 1))
 END IF

 ' store 8.3 filename
 ASCIIZshort = DTAfile.ASCIIZfilename
 Imbedded = INSTR(ASCIIZshort, CHR$(0)) 
 IF Imbedded THEN
    ASCIIZshort = LEFT$(ASCIIZshort, Imbedded - 1)
 END IF

 ' restore basic dta
 InregsX.AX = &H1A00
 InregsX.DS = BasicDTAseg
 InregsX.DX = BasicDTAoff
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' boot error display
BootError:
 COLOR 14, 0
 PRINT "Command line error. Type Filecomp /? for help."
 COLOR 7, 0
 END

' boot usage display
BootUsage:
 COLOR 14, 0
 PRINT "Usage:"
 Outpt$ = "  Filecomp [/C][/X][/L|/Px|/O:" + Quote + "file.ext" + Quote + "]"
 Outpt$ = Outpt$ + "[/F1:" + Quote + "file.ext" + Quote + "]"
 Outpt$ = Outpt$ + "[/F2:" + Quote + "file.ext" + Quote + "]"
 PRINT Outpt$
 PRINT "Where:"
 PRINT "  /C  bypass prompts"
 PRINT "  /X  bypass size prompt"
 PRINT "Output:"
 PRINT "  /L  display to screen, or"
 PRINT "  /Px send to printer port(x=1 to 4), or"
 PRINT "  /O:" + Quote + "filename.ext" + Quote + " send to output file."
 PRINT "    (must be enclosed in quotes)"
 PRINT "Files to compare are:"
 PRINT "  /F1:" + Quote + "c:\path\filename.ext" + Quote
 PRINT "  /F2:" + Quote + "c:\path\filename.ext" + Quote
 PRINT "    (must be enclosed in quotes)"
 GOTO Terminate

' critical error trap
Error.Routine:
 COLOR 7, 0
 Data.Error = ERR
 SELECT CASE Data.Error
 CASE 53
    PRINT "File not found."
 CASE 54
    PRINT "Bad file mode."
 CASE 57
    PRINT "Device I/O error."
 CASE 61
    PRINT "Disk full."
 CASE 64
    PRINT "Bad filename."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 76
    PRINT "Pathname not found."
 CASE ELSE
    PRINT "Untrapped error" + STR$(Data.Error) + "."
 END SELECT
 END
