home *** CD-ROM | disk | FTP | other *** search
- (* COMPARE - COMPARE TWO TEXT FILES AND REPORT THEIR DIFFERENCES.
- *
- * COPYRIGHT (C) 1977, 1978
- * JAMES F. MINER
- * SOCIAL SCIENCE RESEARCH FACILITIES CENTER
- * UNIVERSITY OF MINNESOTA
- *
- * GENERAL PERMISSION TO MAKE FAIR USE IN NON-PROFIT ACTIVITIES
- * OF ALL OR PART OF THIS MATERIAL IS GRANTED PROVIDED THAT
- * THIS NOTICE IS GIVEN. TO OBTAIN PERMISSION FOR OTHER USES
- * AND/OR MACHINE READABLE COPIES WRITE TO:
- *
- * THE DIRECTOR
- * SOCIAL SCIENCE RESEARCH FACILITIES CENTER
- * 25 BLEGEN HALL
- * 269 19TH AVE. SO.
- * UNIVERSITY OF MINNESOTA
- * MINNEAPOLIS, MINNESOTA 55455
- * U S A
- *)
-
-
- (* COMPARE IS USED TO DISPLAY ON "DIFFS" THE DIFFERENCES
- * BETWEEN TWO SIMILAR TEXTS ("FILEA" AND "FILEB"). NOTABLE
- * CHARACTERISTICS ARE:
- *
- * - COMPARE IS LINE ORIENTED. THE SMALLEST UNIT OF COMPARISON
- * IS THE TEXT LINE (IGNORING TRAILING BLANKS). THE PRESENT
- * IMPLEMENTATION HAS A FIXED MAXIMUM LINE LENGTH.
- *
- * - BY MANIPULATING A PROGRAM PARAMETER, THE USER CAN AFFECT
- * COMPARE'S SENSITIVITY TO THE "LOCALITY" OF DIFFERENCES.
- * MORE SPECIFICALLY THIS PARAMETER, "MINLINESFORMATCH",
- * SPECIFIES THE NUMBER OF CONSECUTIVE LINES ON EACH FILE
- * WHICH MUST MATCH IN ORDER THAT THEY BE CONSIDERED AS
- * TERMINATING THE PRIOR MISMATCH. A LARGE VALUE OF
- * "MINLINESFORMATCH" TENDS TO PRODUCE FEWER BUT LARGER
- * MISMATCHES THAN DOES A SMALL VALUE. THE VALUE SIX APPEARS
- * TO GIVE GOOD RESULTS ON PASCAL SOURCE FILES BUT MAY BE
- * INAPPROPRIATE FOR OTHER APPLICATIONS.
- *
- * IF COMPARE IS TO BE USED AS A GENERAL UTILITY PROGRAM,
- * "MINLINESFORMATCH" SHOULD BE TREATED AS A PROGRAM
- * PARAMETER OF SOME SORT. IT IS DECLARED AS A CONSTANT HERE
- * FOR PORTABILITY'S SAKE.
- *
- * - ANOTHER PROGRAM PARAMETER (CONSTANT), "MARKUNEQUALCOLUMNS",
- * SPECIFIES THAT WHEN UNEQUAL LINES ARE FOUND, EACH LINE FROM
- * FILEA IS PRINTED NEXT TO ITS CORRESPONDING LINE FROM FILEB,
- * AND UNEQUAL COLUMNS ARE MARKED. THIS OPTION IS PARTICULARLY
- * USEFUL FOR FIXED-FORMAT DATA FILES. NOTES: LINE PAIRING IS
- * NOT ATTEMPTED IF THE MISMATCHING SECTIONS ARE NOT THE SAME
- * NUMBER OF LINES ON EACH FILE, or if the number of mismatching
- * lines is >= this constant. Set this to 0 if you do not want
- * to mark columns. (W.KEMPTON, NOV 78 and Feb 84)
- *
- * - COMPARE EMPLOYS A SIMPLE BACKTRACKING SEARCH ALGORITHM TO
- * ISOLATE MISMATCHES FROM THEIR SURROUNDING MATCHES. THIS
- * REQUIRES (HEAP) STORAGE ROUGHLY PROPORTIONAL TO THE SIZE
- * OF THE LARGEST MISMATCH, AND TIME ROUGHLY PROPORTIONAL TO
- * THE SQUARE OF THE SIZE OF THE MISMATCH FOR EACH MISMATCH.
- * FOR THIS REASON IT MAY NOT BE FEASIBLE TO USE COMPARE ON
- * FILES WTH VERY LONG MISMATCHES.
- *
- * - TO THE BEST OF THE AUTHOR'S KNOWLEDGE, COMPARE UTILIZES
- * ONLY FEATURES OF STANDARD PASCAL. (CDC version; see below)
- *)
- (* Mods by Willett Kempton, Mich State U, 1984: add CP/M parameters,
- * allow for TABs, make leading FORTRAN blank optional, allow ANSI
- * control codes for 96 columns, bold & underline, force multiple line
- * reads (to reduce disk drive burnout), compress output (fewer blank
- * lines) to fit within a screen better, add constant to allow reverse
- * index as line terminator (for SELECT wp), adapt to Turbo Pascal system
- * (passing files on command line, using file DIFFS rather than OUTPUT).
- * Screen and printer control codes make the output much nicer, but also
- * make the program more system-dependent. Therefore, they are all
- * controlled by constants, and can be easily turned back off.
- *)
- (* Known bugs:
- * 1) Very long mismatches will cause memory overflow. On Turbo,
- * the user gets the obscure error message "Runtime error FF",
- * or just a program hang.
- * 2) CP/M files without normal file terminator (no ^Z) will cause
- * COMPARE to crash with the message I/O ERROR 99.
- *)
-
- PROGRAM COMPARE (FILEA, FILEB, DIFFS {OUTPUT} );
-
- CONST
- VERSION = '1.45'; (* 15 March 1985 *)
-
- LINELENGTH = 100; (* MAXIMUM SIGNIFICANT INPUT LINE LENGTH *)
- MINLINESFORMATCH = 4; (* NUMBER OF CONSECUTIVE EQUIVALENT *)
- (* LINES TO END A MIS-MATCH *)
- MARKUNEQUALCOLUMNS = 5; (* UNEQUAL LINES ARE PAIRED, AND COLUMNS *)
- (* MARKED, IF MISMATCH < THIS LENGTH *)
- MINREAD = 40; (* try for multiple reads (=1 saves heap) *)
- prespace = 7; (* spaces before text on ReportFile lines *)
- TAB = 9; (* ASCII tab for aligning ^ indicator *)
- BarChar = '|'; (* separator between line number and text *)
- Turbo = TRUE; (* compiler-specific features; FALSE=ISO *)
- ANSIcon = TRUE; (* console has ANSI driver *)
- ANSIdiffs = TRUE ; (* assume ANSI for redirected output *)
- RIeoln = TRUE; (* RI=chr(141) & CR=chr(13) both are eoln *)
- (* 8-bit ANSI feature; 15% run time penalty *)
-
- (* for conservative, device-independant output from this program, set:
- ANSIoutput = FALSE; RIeoln = FALSE; and enable PROCEDURE FORTRAN
- *)
-
- TYPE
- LINEPOINTER = ^LINE;
- LINEIMAGETYPE = PACKED ARRAY [1..LINELENGTH] OF CHAR;
- LINE = (* SINGLE LINE BUFFER *)
- PACKED RECORD
- NEXTLINE : LINEPOINTER;
- LENGTH : 0..LINELENGTH;
- IMAGE : LINEIMAGETYPE
- END;
-
- STREAM = (* BOOKKEEPING FOR EACH INPUT FILE *)
- RECORD
- NAME : CHAR;
- CURSOR, HEAD, TAIL : LINEPOINTER;
- CURSORLINENO, HEADLINENO, TAILLINENO : INTEGER;
- ENDFILE : BOOLEAN
- END;
-
- VAR
- FILEA, FILEB, DIFFS : TEXT;
- A, B : STREAM;
- MATCH : BOOLEAN;
- ENDFILE : BOOLEAN; (* SET IF END OF STREAM A OR B *)
-
- FREELINES : LINEPOINTER; (* FREE LIST OF LINE BUFFERS *)
-
- ANSIoutput,
- SAME : BOOLEAN; (* FALSE IF NO MIS-MATCHES OCCUR *)
- LINESTOOLONG : BOOLEAN; (* TRUE IF SOME LINES NOT COMPLETELY CHECKED*)
-
- (*$I ArgLib.pas Turbo routines for Command Line Arguments *)
- (* from ArgLib IMPORT: argcount, argv, resetOK ArgStrType *)
-
- PROCEDURE FORTRAN;
- (* Called at the beginning of each new output line. *)
- (* Write a blank if using a FORTRAN-convention output device *)
- BEGIN
- { WRITE(DIFFS,' '); } (* not used on ASCII devices *)
- END;
-
- PROCEDURE UNDERLINE (ON: BOOLEAN);
- CONST ESC = 27;
- BEGIN
- IF ANSIoutput THEN
- IF ON THEN WRITE(DIFFS,CHR(ESC),'[4m')
- ELSE WRITE(DIFFS,CHR(ESC),'[0m');
- END;
-
- PROCEDURE BOLD (ON: BOOLEAN);
- CONST ESC = 27;
- BEGIN
- IF ANSIoutput THEN
- IF ON THEN WRITE(DIFFS,CHR(ESC),'[1m')
- ELSE WRITE(DIFFS,CHR(ESC),'[0m')
- END;
-
- PROCEDURE NARROWPRINTING (ON: BOOLEAN);
- CONST ESC = 27;
- BEGIN
- IF ANSIoutput THEN
- IF ON THEN WRITE(DIFFS,CHR(ESC),'[2w') (* 12 columns/inch *)
- ELSE WRITE(DIFFS,CHR(ESC),'[0w') (* normal print width *)
- END;
-
- PROCEDURE COMPAREFILES;
-
- FUNCTION ENDSTREAM(VAR X : STREAM) : BOOLEAN;
- BEGIN (* ENDSTREAM *)
- ENDSTREAM := (X.CURSOR = NIL) AND X. ENDFILE
- END; (* ENDSTREAM *)
-
- PROCEDURE MARK(VAR X : STREAM);
-
- (* CAUSES BEGINNING OF STREAM TO BE POSITIONED BEFORE *)
- (* CURRENT STREAM CURSOR. BUFFERS GET RECLAIMED, LINE *)
- (* COUNTERS RESET, ETC. *)
-
- VAR
- P : LINEPOINTER;
-
- BEGIN (* MARK *)
- WITH X DO
- IF HEAD <> NIL THEN
- BEGIN
- WHILE HEAD <> CURSOR DO (* RECLAIM BUFFERS *)
- BEGIN
- WITH HEAD^ DO
- BEGIN P := NEXTLINE;
- NEXTLINE := FREELINES; FREELINES := HEAD
- END;
- HEAD := P
- END;
- HEADLINENO := CURSORLINENO;
- IF CURSOR = NIL THEN
- BEGIN TAIL := NIL; TAILLINENO := CURSORLINENO END
- END
- END; (* MARK *)
-
- PROCEDURE MOVECURSOR(VAR X : STREAM; VAR FILEX : TEXT);
-
- (* FILEX IS THE INPUT FILE ASSOCIATED WITH STREAM X. THE *)
- (* CURSOR FOR X IS MOVED FORWARD ONE LINE, READING FROM X *)
- (* IF NECESSARY, AND INCREMENTING THE LINE COUNT. ENDFILE *)
- (* IS SET IF EOF IS ENCOUNTERED ON EITHER STREAM. *)
-
- PROCEDURE READLINE;
- (* Read from FILEX. To save disk drive wear on small-buffer systems, *)
- (* this will try to read MINREAD lines at a time. The only *)
- (* disadvantage of a large MINREAD is running out of heap sooner on *)
- (* large mismatches. *)
- (* Example: CP/M with 128 byte buffer: This source code took 298 disk *)
- (* 'clicks' with MINREAD=1 and 28 clicks with MINREAD=40. *)
- (* CDC version read into an unpacked TEMPLINE, eliminated here *)
- CONST
- ORDCR=13; MASK=127; (* ASCII values; to equate CR and RI *)
- VAR
- NEWLINE : LINEPOINTER;
- C, C2 : 0..LINELENGTH;
- CH : CHAR;
- MOREREADS : INTEGER;
- BEGIN (* READLINE *)
- MOREREADS := MINREAD;
- WHILE (NOT X.ENDFILE) AND (MOREREADS>0) DO
- BEGIN
- (* allocate space for the line *)
- NEWLINE := FREELINES;
- IF NEWLINE <> NIL THEN FREELINES := FREELINES^.NEXTLINE
- ELSE (* need more from heap *)
- BEGIN
- (* Sould check for heap exhaustion here. Even if check is ok here, *)
- (* recursive calls may later cause stack/heap collision. *)
- NEW(NEWLINE);
- NEWLINE^.LENGTH := LINELENGTH; (* new: must blank fill *)
- END;
- C := 0;
- WITH NEWLINE^ DO
- IF (Turbo AND RIeoln)
-
- THEN
- (* for SELECT wp: either CR or RI terminates line *)
- BEGIN
- IF NOT EOF(FILEX) THEN READ(FILEX,CH); (* first char of line *)
- WHILE ((ORD(CH) AND MASK)<>ORDCR)
- AND (C < LINELENGTH)
- AND (NOT EOF(FILEX)) DO
- BEGIN C := C + 1; IMAGE[C]:=CH; READ(FILEX,CH); END;
- WHILE ((ORD(CH) AND MASK)<>ORDCR) AND (NOT EOF(FILEX)) DO
- BEGIN READ(FILEX,CH); LINESTOOLONG := TRUE; END;
- IF NOT EOF(FILEX) THEN READ(FILEX,CH); (* should be the LF *)
- END
-
- ELSE
- (* normal lines--terminated by standard Pascal EOLN *)
- BEGIN
- WHILE NOT EOLN(FILEX) AND (C < LINELENGTH) DO
- BEGIN C := C + 1; READ(FILEX,IMAGE[C]); END;
- IF NOT EOLN(FILEX) THEN LINESTOOLONG := TRUE;
- READLN(FILEX);
- END;
-
- WHILE (NEWLINE^.IMAGE[C] = ' ') AND (C>1) DO C := C - 1;
- WITH NEWLINE^ DO
- IF C < LENGTH THEN
- FOR C2 := C+1 TO LENGTH DO IMAGE[C2] := ' ';
- NEWLINE^.LENGTH := C;
- NEWLINE^.NEXTLINE := NIL;
- IF X. TAIL = NIL THEN
- BEGIN X.HEAD :=NEWLINE;
- X.TAILLINENO :=1; X. HEADLINENO := 1
- END
- ELSE
- BEGIN X.TAIL^.NEXTLINE := NEWLINE;
- X.TAILLINENO := X. TAILLINENO + 1
- END;
- X.TAIL := NEWLINE;
- X.ENDFILE := EOF(FILEX);
- MOREREADS := MOREREADS - 1;
- END
- END; (* READLINE *)
-
- BEGIN (* MOVECURSOR *)
- IF X. CURSOR <> NIL THEN
- BEGIN
- IF X. CURSOR = X.TAIL THEN READLINE;
- X.CURSOR := X.CURSOR^.NEXTLINE;
- IF X.CURSOR = NIL THEN ENDFILE := TRUE;
- X.CURSORLINENO := X.CURSORLINENO + 1
- END
- ELSE
- IF NOT X.ENDFILE THEN (* BEGINNING OF STREAM *)
- BEGIN
- READLINE; X.CURSOR := X.HEAD;
- X.CURSORLINENO := X. HEADLINENO
- END
- ELSE (* END OF STREAM *)
- ENDFILE := TRUE;
- END; (* MOVECURSOR *)
-
- PROCEDURE BACKTRACK(VAR X : STREAM; VAR XLINES : INTEGER);
-
- (* CAUSES THE CURRENT POSITION OF STREAM X TO BECOME THAT *)
- (* OF THE LAST MARK OPERATION. I.E., THE CURRENT LINE *)
- (* WHEN THE STREAM WAS MARKED LAST BECOMES THE NEW CURSOR. *)
- (* XLINES IS SET TO THE NUMBER OF LINES FROM THE NEW CURSOR *)
- (* TO THE OLD CURSOR, INCLUSIVE. *)
-
- BEGIN (* BACKTRACK *)
- XLINES := X.CURSORLINENO + 1 - X.HEADLINENO;
- X.CURSOR := X.HEAD; X.CURSORLINENO := X.HEADLINENO;
- ENDFILE := ENDSTREAM(A) OR ENDSTREAM(B)
- END; (* BACKTRACK *)
-
- PROCEDURE COMPARELINES(VAR MATCH : BOOLEAN);
-
- (* COMPARE THE CURRENT LINES OF STREAMS A AND B, RETURNING *)
- (* MATCH TO SIGNAL THEIR (NON-) EQUIVALENCE. EOF ON BOTH STREAMS *)
- (* IS CONSIDERED A MATCH, BUT EOF ON ONLY ONE STREAM IS A MISMATCH *)
-
- BEGIN (* COMPARELINES *)
- IF (A.CURSOR = NIL) OR (B.CURSOR =NIL) THEN
- MATCH := ENDSTREAM(A) AND ENDSTREAM(B)
- ELSE
- BEGIN
- MATCH := (A.CURSOR^.LENGTH = B.CURSOR^.LENGTH);
- IF MATCH THEN
- MATCH := (A.CURSOR^.IMAGE =B.CURSOR^.IMAGE)
- END
- END; (* COMPARELINES *)
-
- PROCEDURE FINDMISMATCH;
- BEGIN (* FINDMISMATCH *)
- (* NOT ENDFILE AND MATCH *)
- REPEAT (* COMPARENEXTLINES *)
- MOVECURSOR(A, FILEA); MOVECURSOR(B,FILEB);
- MARK(A); MARK(B);
- COMPARELINES(MATCH)
- UNTIL ENDFILE OR NOT MATCH;
- END; (* FINDMISMATCH *)
-
- PROCEDURE FINDMATCH;
- VAR
- ADVANCEB : BOOLEAN; (* TOGGLE ONE-LINE LOOKAHEAD BETWEEN STREAMS *)
-
- PROCEDURE SEARCH(VAR X : STREAM; (* STREAM TO SEARCH *)
- VAR FILEX : TEXT;
- VAR Y : STREAM; (*STREAM TO LOOKAHEAD *)
- VAR FILEY : TEXT);
-
- (* LOOK AHEAD ONE LINE ON STREAM Y, AND SEARCH FOR THAT LINE *)
- (* BACKTRACKING ON STREAM X. *)
-
- VAR
- COUNT : INTEGER; (* NUMBER OF LINES BACKTRACKED ON X *)
-
- PROCEDURE CHECKFULLMATCH;
- (* FROM THE CURRENT POSITIONS IN X AND Y, WHICH MATCH, *)
- (* MAKE SURE THAT THE NEXT MINLINESFORMATCH-1 LINES ALSO *)
- (* MATCH, OR ELSE SET MATCH := FALSE. *)
- VAR
- N : INTEGER;
- SAVEXCUR, SAVEYCUR : LINEPOINTER;
- SAVEXLINE, SAVEYLINE : INTEGER;
- BEGIN (* CHECKFULLMATCH *)
- SAVEXCUR :=X.CURSOR; SAVEYCUR := Y.CURSOR;
- SAVEXLINE :=X.CURSORLINENO; SAVEYLINE :=Y.CURSORLINENO;
- COMPARELINES(MATCH);
- N := MINLINESFORMATCH - 1;
- WHILE MATCH AND (N <> 0) DO
- BEGIN MOVECURSOR(X, FILEX); MOVECURSOR(Y, FILEY);
- COMPARELINES(MATCH); N := N - 1
- END;
- X.CURSOR := SAVEXCUR; X.CURSORLINENO := SAVEXLINE;
- Y.CURSOR := SAVEYCUR; Y.CURSORLINENO := SAVEYLINE;
- END; (* CHECKFULLMATCH *)
-
- BEGIN (* SEARCH *)
- MOVECURSOR(Y, FILEY); BACKTRACK(X, COUNT);
- CHECKFULLMATCH; COUNT := COUNT - 1;
- WHILE (COUNT <> 0) AND NOT MATCH DO
- BEGIN
- MOVECURSOR(X, FILEX); COUNT := COUNT - 1;
- CHECKFULLMATCH
- END
- END; (* SEARCH *)
-
- PROCEDURE PRINTMISMATCH;
- VAR
- EMPTYA, EMPTYB : BOOLEAN;
-
- PROCEDURE WRITEONELINE(NAME : CHAR; L : INTEGER; P : LINEPOINTER);
- VAR I: INTEGER;
- BEGIN (* WRITEONELINE *)
- FORTRAN;
- WRITE(DIFFS,NAME, L:5,BarChar); BOLD(TRUE);
- IF P^.LENGTH <> 0
- THEN FOR I:= 1 TO P^.LENGTH DO WRITE(DIFFS,P^.IMAGE[I]);
- WRITELN(DIFFS); BOLD(FALSE);
- END; (* WRITEONELINE *)
-
- PROCEDURE WRITETEXT(VAR X : STREAM);
- (* WRITE FROM X.HEAD TO ONE LINE BEFORE X.CURSOR *)
- VAR
- P, Q : LINEPOINTER; LINENO : INTEGER;
- BEGIN (* WRITETEXT *)
- P:=X.HEAD; Q:=X.CURSOR; LINENO:=X.HEADLINENO;
- WHILE (P <> NIL) AND (P <> Q) DO
- BEGIN
- WRITEONELINE( X.NAME, LINENO, P);
- P := P^.NEXTLINE;
- LINENO := LINENO + 1;
- END;
- IF P = NIL THEN WRITELN(DIFFS,' *** EOF ***');
- (* WRITELN *)
- END; (* WRITETEXT *)
-
- PROCEDURE WRITEPAIRS( PA, PB : LINEPOINTER; LA, LB : INTEGER);
- (* THIS WRITES FROM THE HEAD TO THE CURSOR, LIKE PROCEDURE WRITETEXT. *)
- (* UNLIKE PROCEDURE WRITETEXT, THIS WRITES FROM BOTH FILES AT ONCE, *)
- (* COMPARES COLUMNS WITHIN LINES, AND MARKS UNEQUAL COLUMNS *)
- VAR
- TEMPA, TEMPB : LINEIMAGETYPE;
- COL, MAXCOL : INTEGER;
- BEGIN (* WRITEPAIRS *)
- REPEAT
- WRITEONELINE('A', LA, PA); WRITEONELINE('B', LB, PB);
- (* UNPACK(PA^.IMAGE,TEMPA,1); UNPACK(PB^.IMAGE,TEMPB,1); *)
- (* IF NO UNPACK ON YOUR COMPILER, USE THE FOLLOWING: *)
- TEMPA := PA^.IMAGE; TEMPB := PB^.IMAGE;
- IF PA^.LENGTH > PB^.LENGTH
- THEN MAXCOL := PA^.LENGTH ELSE MAXCOL := PB^.LENGTH;
- FORTRAN;
- WRITE(DIFFS,' ': prespace);
- FOR COL := 1 TO MAXCOL DO
- IF TEMPA[COL] <> TEMPB[COL] THEN WRITE(DIFFS,'^')
- ELSE BEGIN IF TEMPA[COL]=CHR(TAB) THEN WRITE(DIFFS,CHR(TAB))
- ELSE WRITE(DIFFS,' ');
- END;
- WRITELN(DIFFS);(* WRITELN(DIFFS); *)
- PA := PA^.NEXTLINE; LA := LA + 1;
- PB := PB^.NEXTLINE; LB := LB + 1;
- UNTIL (PA = A.CURSOR) OR (PA = NIL);
- END; (* WRITEPAIRS *)
-
- PROCEDURE WRITEBOLDPAIRS(PA, PB: LINEPOINTER; LA, LB: INTEGER);
- (* Parallel to WRITEPAIRS, but uses ANSI screen control *)
- VAR
- MAXCOL, COL: INTEGER;
- EQ: ARRAY[0..LINELENGTH] OF BOOLEAN;
-
- PROCEDURE WRITEbONELINE(NAME : CHAR; L : INTEGER; P : LINEPOINTER);
- (* Parallel to WRITEONELINE *) (* imports: MAXCOL, EQ *)
- VAR I: INTEGER;
- BEGIN (* WRITEbONELINE *)
- FORTRAN;
- WRITE(DIFFS, NAME, L:5, BarChar);
- FOR I := 1 TO MAXCOL DO
- BEGIN
- IF EQ[I-1] AND NOT EQ[I] THEN BOLD(TRUE)
- ELSE IF (NOT EQ[I-1]) AND EQ[I] THEN BOLD(FALSE);
- WRITE(DIFFS,P^.IMAGE[I]);
- END;
- BOLD(FALSE); WRITELN(DIFFS);
- END; (* WRITEbONELINE *)
-
- BEGIN (* WRITEBOLDPAIRS *)
- EQ[0] := TRUE;
- REPEAT
- IF PA^.LENGTH > PB^.LENGTH
- THEN MAXCOL := PA^.LENGTH ELSE MAXCOL := PB^.LENGTH;
- FOR COL := 1 TO MAXCOL DO
- EQ[COL] := PA^.IMAGE[COL] = PB^.IMAGE[COL];
- WRITEbONELINE('A', LA, PA); WRITEbONELINE('B', LB, PB); WRITELN(DIFFS);
- PA := PA^.NEXTLINE; LA := LA + 1;
- PB := PB^.NEXTLINE; LB := LB + 1;
- UNTIL (PA = A.CURSOR) OR (PA = NIL);
- END (* WRITEBOLDPAIRS *);
-
- PROCEDURE WRITELINENO(VAR X : STREAM);
- VAR
- F, L : INTEGER;
- BEGIN (* WRITELINENO *)
- WRITE(DIFFS,' file',X.NAME,', ');
- F := X.HEADLINENO; L := X.CURSORLINENO - 1;
- WRITE(DIFFS,'line');
- IF F = L THEN WRITE(DIFFS,' ', F:1)
- ELSE WRITE(DIFFS,'s ', F:1, ' - ', L:1);
- IF X.CURSOR = NIL THEN WRITE(DIFFS,' (before EOF)');
- END; (* WRITELINENO *)
-
- PROCEDURE PRINTEXTRATEXT(VAR X, Y : STREAM);
-
- BEGIN (* PRINTEXTRATEXT *)
- WRITE(DIFFS,'EXTRA TEXT: on file', X.NAME, ', ');
-
- IF Y.HEAD = NIL THEN
- WRITELN(DIFFS,' before EOF on file', Y.NAME)
- ELSE
- WRITELN(DIFFS,' between lines ', Y.HEADLINENO-1:1, ' and ',
- Y.HEADLINENO:1, ' of file', Y.NAME);
- UNDERLINE(FALSE);
- WRITELN(DIFFS);
- WRITETEXT(X)
- END; (* PRINTEXTRATEXT *)
-
- BEGIN (* PRINTMISMATCH *)
- WRITELN(DIFFS);
- FORTRAN; BOLD(FALSE); UNDERLINE(TRUE);
- (* write a divider if no underlining/bold available *)
- IF NOT ANSIoutput
- THEN WRITELN(DIFFS,' ':prespace,
- '*************************************************************');
- EMPTYA := (A.HEAD = A.CURSOR);
- EMPTYB := (B.HEAD = B.CURSOR);
- IF EMPTYA OR EMPTYB THEN
- IF EMPTYA THEN PRINTEXTRATEXT(B, A)
- ELSE PRINTEXTRATEXT(A, B)
- ELSE
- BEGIN
- FORTRAN;
- WRITE(DIFFS,'MISMATCH: ');
- WRITELINENO(A); WRITE(DIFFS,' NOT EQUAL TO ');
- WRITELINENO(B); WRITELN(DIFFS,':');
- UNDERLINE(FALSE);
- WRITELN(DIFFS);
- IF (MARKUNEQUALCOLUMNS > (A.CURSORLINENO - A.HEADLINENO -1) ) AND
- ((A.CURSORLINENO - A.HEADLINENO) = (B.CURSORLINENO - B.HEADLINENO))
- THEN
- BEGIN
- IF ANSIoutput
- THEN WRITEBOLDPAIRS(A.HEAD, B.HEAD, A.HEADLINENO, B.HEADLINENO)
- ELSE WRITEPAIRS(A.HEAD, B.HEAD, A.HEADLINENO, B.HEADLINENO)
- END
- ELSE
- BEGIN WRITETEXT(A);
- WRITELN(DIFFS,' ':prespace,'----------------');
- WRITETEXT(B)
- END;
- END
- END; (* PRINTMISMATCH *)
-
- BEGIN (* FINDMATCH *)
- (* NOT MATCH *)
- ADVANCEB := TRUE;
- REPEAT
- IF NOT ENDFILE THEN ADVANCEB := NOT ADVANCEB
- ELSE ADVANCEB := ENDSTREAM(A);
- IF ADVANCEB THEN SEARCH(A, FILEA, B, FILEB)
- ELSE SEARCH(B, FILEB, A, FILEA)
- UNTIL MATCH;
- PRINTMISMATCH;
- END; (* FINDMATCH *)
-
- BEGIN (* COMPAREFILES *)
- MATCH := TRUE; (* I.E., BEGINNINGS-OF-FILES MATCH *)
- REPEAT
- IF MATCH THEN FINDMISMATCH ELSE BEGIN SAME := FALSE; FINDMATCH END
- UNTIL ENDFILE AND MATCH;
- (* MARK(A); MARK(B); MARK END OF FILES, THEREBY DISPOSING BUFFERS *)
- END; (* COMPAREFILES *)
-
- PROCEDURE INITIALIZE;
- (* setup files, using names from command line *)
- (* IMPORT from file ArgLib.pas: ArgStrType, argcount, argv, resetOK *)
- var ArgFileA,ArgFileB,ArgFileC: ArgStrType;
- nFiles: integer;
- ArgumentsOK: boolean;
-
- PROCEDURE INITSTREAM(NAMECHAR : CHAR; VAR X : STREAM;
- VAR FILEX : TEXT; Arg: ArgStrType);
- BEGIN (* INITSTREAM *)
- IF resetOK(FILEX, Arg)
- THEN X.ENDFILE := EOF(FILEX)
- ELSE
- BEGIN BOLD(TRUE);
- WRITE(DIFFS,' ERROR Cannot find'); ArgumentsOK := FALSE;
- END;
- WITH X DO
- BEGIN
- NAME := NAMECHAR;
- CURSOR :=NIL; HEAD := NIL; TAIL := NIL;
- CURSORLINENO := 0; HEADLINENO := 0; TAILLINENO := 0;
- BOLD(TRUE); WRITELN(DIFFS,' file', NAME,': ', Arg); BOLD(FALSE);
- END;
- END; (* INITSTREAM *)
-
-
- BEGIN (* INITIALIZE *)
- nFiles := argcount;
- if (nFiles < 2) or (nFiles > 3)
- THEN
- BEGIN
- WRITELN({DIFFS,}'Usage: COMPARE FileA FileB');
- WRITELN({DIFFS,}' or COMPARE FileA FileB Report-file');
- ArgumentsOK:=FALSE;
- END
- ELSE
- BEGIN
- argv(1,ArgFileA); argv(2,ArgFileB);
- ArgumentsOK := resetOK(FILEA,ArgFileA) AND resetOK(FILEB,ArgFileB);
- IF ArgumentsOK AND (nFiles > 2)
- THEN BEGIN
- argv(3,ArgFileC); Assign(DIFFS,ArgFileC);
- ANSIoutput := ANSIdiffs;
- END
- ELSE BEGIN Assign(DIFFS,'CON:'); ANSIoutput:=ANSIcon; END;
- REWRITE(DIFFS);
- { PAGE(OUTPUT); } FORTRAN;
- WRITE(DIFFS,'Compare');
- WRITE(DIFFS,' version ', VERSION,' (Options:');
- IF RIeoln THEN WRITE(DIFFS,' RI=CR,');
- WRITELN(DIFFS,' rematch on ', MINLINESFORMATCH:1, ' lines.)');
- INITSTREAM('A',A, FILEA,ArgFileA);
- INITSTREAM('B',B, FILEB,ArgFileB);
- ENDFILE := A.ENDFILE OR B.ENDFILE;
- LINESTOOLONG := FALSE;
- FREELINES := NIL;
- IF ArgumentsOK THEN NARROWPRINTING(TRUE);
- END;
- IF NOT ArgumentsOK then ENDFILE := TRUE;
- END; (*INITIALIZE*)
-
-
- BEGIN (*COMPARE*)
- INITIALIZE;
- IF NOT ENDFILE THEN
- BEGIN SAME := TRUE;
- COMPAREFILES;
- WRITELN(DIFFS);
- IF SAME
- THEN WRITELN(DIFFS,' ',(A.CURSORLINENO-1):1,
- ' lines read; no differences.')
- ELSE WRITELN(DIFFS,' files are different.');
- IF LINESTOOLONG THEN
- BEGIN WRITELN(DIFFS);
- WRITELN(DIFFS,' WARNING: Some lines were longer than ',
- LINELENGTH:1, ' characters.');
- WRITELN(DIFFS,' ':11,'They were not compared past that point.');
- END;
- NARROWPRINTING(FALSE);
- END;
- IF Turbo THEN CLOSE(DIFFS); (* Turbo nonstandardly requires CLOSE *)
- END. (* COMPARE *)