home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / COMPARE.ZIP / COMPARE.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  23.3 KB  |  653 lines

  1. (*      COMPARE - COMPARE TWO TEXT FILES AND REPORT THEIR DIFFERENCES.
  2. *
  3. *       COPYRIGHT (C) 1977, 1978
  4. *       JAMES F. MINER
  5. *       SOCIAL SCIENCE RESEARCH FACILITIES CENTER
  6. *       UNIVERSITY OF MINNESOTA
  7. *
  8. *       GENERAL PERMISSION TO MAKE FAIR USE IN NON-PROFIT ACTIVITIES
  9. *       OF ALL OR PART OF THIS MATERIAL IS GRANTED PROVIDED THAT
  10. *       THIS NOTICE IS GIVEN.  TO OBTAIN PERMISSION FOR OTHER USES
  11. *       AND/OR MACHINE READABLE COPIES WRITE TO:
  12. *
  13. *               THE DIRECTOR
  14. *               SOCIAL SCIENCE RESEARCH FACILITIES CENTER
  15. *               25 BLEGEN HALL
  16. *               269 19TH AVE. SO.
  17. *               UNIVERSITY OF MINNESOTA
  18. *               MINNEAPOLIS, MINNESOTA 55455
  19. *               U S A
  20. *)
  21.  
  22.  
  23. (*      COMPARE IS USED TO DISPLAY ON "DIFFS" THE DIFFERENCES
  24. *       BETWEEN TWO SIMILAR TEXTS ("FILEA" AND "FILEB").  NOTABLE
  25. *       CHARACTERISTICS ARE:
  26. *
  27. *       - COMPARE IS LINE ORIENTED.  THE SMALLEST UNIT OF COMPARISON
  28. *         IS THE TEXT LINE (IGNORING TRAILING BLANKS).  THE PRESENT
  29. *         IMPLEMENTATION HAS A FIXED MAXIMUM LINE LENGTH.
  30. *
  31. *       - BY MANIPULATING A PROGRAM PARAMETER, THE USER CAN AFFECT
  32. *         COMPARE'S SENSITIVITY TO THE "LOCALITY" OF DIFFERENCES.
  33. *         MORE SPECIFICALLY THIS PARAMETER, "MINLINESFORMATCH",
  34. *         SPECIFIES THE NUMBER OF CONSECUTIVE LINES ON EACH FILE
  35. *         WHICH MUST MATCH IN ORDER THAT THEY BE CONSIDERED AS
  36. *         TERMINATING THE PRIOR MISMATCH.  A LARGE VALUE OF
  37. *         "MINLINESFORMATCH" TENDS TO PRODUCE FEWER BUT LARGER
  38. *         MISMATCHES THAN DOES A SMALL VALUE.  THE VALUE SIX APPEARS
  39. *         TO GIVE GOOD RESULTS ON PASCAL SOURCE FILES BUT MAY BE
  40. *         INAPPROPRIATE FOR OTHER APPLICATIONS.
  41. *
  42. *         IF COMPARE IS TO BE USED AS A GENERAL UTILITY PROGRAM,
  43. *         "MINLINESFORMATCH" SHOULD BE TREATED AS A PROGRAM
  44. *         PARAMETER OF SOME SORT.  IT IS DECLARED AS A CONSTANT HERE
  45. *         FOR PORTABILITY'S SAKE.
  46. *
  47. *       - ANOTHER PROGRAM PARAMETER (CONSTANT), "MARKUNEQUALCOLUMNS",
  48. *         SPECIFIES THAT WHEN UNEQUAL LINES ARE FOUND, EACH LINE FROM
  49. *         FILEA IS PRINTED NEXT TO ITS CORRESPONDING LINE FROM FILEB,
  50. *         AND UNEQUAL COLUMNS ARE MARKED.  THIS OPTION IS PARTICULARLY
  51. *         USEFUL FOR FIXED-FORMAT DATA FILES.  NOTES: LINE PAIRING IS
  52. *         NOT ATTEMPTED IF THE MISMATCHING SECTIONS ARE NOT THE SAME
  53. *         NUMBER OF LINES ON EACH FILE, or if the number of mismatching
  54. *         lines is  >=  this constant.  Set this to 0 if you do not want
  55. *         to mark columns.  (W.KEMPTON, NOV 78 and Feb 84)
  56. *
  57. *       - COMPARE EMPLOYS A SIMPLE BACKTRACKING SEARCH ALGORITHM TO
  58. *         ISOLATE MISMATCHES FROM THEIR SURROUNDING MATCHES.  THIS
  59. *         REQUIRES (HEAP) STORAGE ROUGHLY PROPORTIONAL TO THE SIZE
  60. *         OF THE LARGEST MISMATCH, AND TIME ROUGHLY PROPORTIONAL TO
  61. *         THE SQUARE OF THE SIZE OF THE MISMATCH FOR EACH MISMATCH.
  62. *         FOR THIS REASON IT MAY NOT BE FEASIBLE TO USE COMPARE ON
  63. *         FILES WTH VERY LONG MISMATCHES.
  64. *
  65. *       - TO THE BEST OF THE AUTHOR'S KNOWLEDGE, COMPARE UTILIZES
  66. *         ONLY FEATURES OF STANDARD PASCAL. (CDC version; see below)
  67. *)
  68. (*      Mods by Willett Kempton, Mich State U, 1984:  add CP/M parameters,
  69. *       allow for TABs, make leading FORTRAN blank optional, allow ANSI
  70. *       control codes for 96 columns, bold & underline, force multiple line
  71. *       reads (to reduce disk drive burnout), compress output (fewer blank
  72. *       lines) to fit within a screen better, add constant to allow reverse
  73. *       index as line terminator (for SELECT wp), adapt to Turbo Pascal system
  74. *       (passing files on command line, using file DIFFS rather than OUTPUT).
  75. *       Screen and printer control codes make the output much nicer, but also
  76. *       make the program more system-dependent.  Therefore, they are all
  77. *       controlled by constants, and can be easily turned back off.
  78. *)
  79. (*   Known bugs:
  80. *        1) Very long mismatches will cause memory overflow.  On Turbo,
  81. *           the user gets the obscure error message "Runtime error FF",
  82. *           or just a program hang.
  83. *        2) CP/M files without normal file terminator (no ^Z) will cause
  84. *           COMPARE to crash with the message I/O ERROR 99.
  85. *)
  86.  
  87. PROGRAM COMPARE (FILEA, FILEB, DIFFS {OUTPUT} );
  88.  
  89.   CONST
  90.     VERSION = '1.45';   (* 15 March 1985 *)
  91.  
  92.     LINELENGTH = 100;          (* MAXIMUM SIGNIFICANT INPUT LINE LENGTH *)
  93.     MINLINESFORMATCH = 4;      (* NUMBER OF CONSECUTIVE EQUIVALENT *)
  94.                                (*   LINES TO END A MIS-MATCH *)
  95.     MARKUNEQUALCOLUMNS = 5;    (* UNEQUAL LINES ARE PAIRED, AND COLUMNS *)
  96.                                (*   MARKED, IF MISMATCH < THIS LENGTH *)
  97.     MINREAD = 40;              (* try for multiple reads (=1 saves heap) *)
  98.     prespace = 7;              (* spaces before text on ReportFile lines *)
  99.     TAB = 9;                   (* ASCII tab for aligning ^ indicator *)
  100.     BarChar = '|';             (* separator between line number and text *)
  101.     Turbo = TRUE;              (* compiler-specific features; FALSE=ISO *)
  102.     ANSIcon  = TRUE;           (* console has ANSI driver *)
  103.     ANSIdiffs = TRUE ;         (* assume ANSI for redirected output *)
  104.     RIeoln = TRUE;             (* RI=chr(141) & CR=chr(13) both are eoln *)
  105.                                (* 8-bit ANSI feature; 15% run time penalty *)
  106.  
  107.   (* for conservative, device-independant output from this program, set:
  108.     ANSIoutput = FALSE;  RIeoln = FALSE; and enable PROCEDURE FORTRAN
  109.   *)
  110.  
  111. TYPE
  112.   LINEPOINTER = ^LINE;
  113.   LINEIMAGETYPE = PACKED ARRAY [1..LINELENGTH] OF CHAR;
  114.   LINE =                           (* SINGLE LINE BUFFER *)
  115.     PACKED RECORD
  116.       NEXTLINE : LINEPOINTER;
  117.       LENGTH : 0..LINELENGTH;
  118.       IMAGE : LINEIMAGETYPE
  119.       END;
  120.  
  121.     STREAM =                        (* BOOKKEEPING FOR EACH INPUT FILE *)
  122.       RECORD
  123.         NAME : CHAR;
  124.         CURSOR, HEAD, TAIL : LINEPOINTER;
  125.         CURSORLINENO, HEADLINENO, TAILLINENO : INTEGER;
  126.         ENDFILE : BOOLEAN
  127.       END;
  128.  
  129. VAR
  130.   FILEA, FILEB, DIFFS : TEXT;
  131.   A, B : STREAM;
  132.   MATCH : BOOLEAN;
  133.   ENDFILE : BOOLEAN;                 (* SET IF END OF STREAM A OR B *)
  134.  
  135.   FREELINES : LINEPOINTER;       (* FREE LIST OF LINE BUFFERS *)
  136.  
  137.   ANSIoutput,
  138.   SAME : BOOLEAN;                (* FALSE IF NO MIS-MATCHES OCCUR *)
  139.   LINESTOOLONG : BOOLEAN;        (* TRUE IF SOME LINES NOT COMPLETELY CHECKED*)
  140.  
  141.   (*$I ArgLib.pas    Turbo routines for Command Line Arguments *)
  142.   (* from ArgLib IMPORT: argcount, argv, resetOK  ArgStrType *)
  143.  
  144. PROCEDURE FORTRAN;
  145. (* Called at the beginning of each new output line. *)
  146. (* Write a blank if using a FORTRAN-convention output device *)
  147. BEGIN
  148.   { WRITE(DIFFS,' '); }  (* not used on ASCII devices *)
  149. END;
  150.  
  151. PROCEDURE UNDERLINE (ON: BOOLEAN);
  152. CONST ESC = 27;
  153. BEGIN
  154.   IF ANSIoutput THEN
  155.     IF ON THEN WRITE(DIFFS,CHR(ESC),'[4m')
  156.           ELSE WRITE(DIFFS,CHR(ESC),'[0m');
  157. END;
  158.  
  159. PROCEDURE BOLD  (ON: BOOLEAN);
  160. CONST ESC = 27;
  161. BEGIN
  162.   IF ANSIoutput THEN
  163.     IF ON THEN WRITE(DIFFS,CHR(ESC),'[1m')
  164.           ELSE WRITE(DIFFS,CHR(ESC),'[0m')
  165. END;
  166.  
  167. PROCEDURE NARROWPRINTING (ON: BOOLEAN);
  168. CONST ESC = 27;
  169. BEGIN
  170.   IF ANSIoutput THEN
  171.     IF ON THEN WRITE(DIFFS,CHR(ESC),'[2w') (* 12 columns/inch *)
  172.           ELSE WRITE(DIFFS,CHR(ESC),'[0w') (* normal print width *)
  173. END;
  174.  
  175. PROCEDURE COMPAREFILES;
  176.  
  177.   FUNCTION ENDSTREAM(VAR X : STREAM) : BOOLEAN;
  178.   BEGIN (* ENDSTREAM *)
  179.     ENDSTREAM := (X.CURSOR = NIL) AND X. ENDFILE
  180.   END;  (* ENDSTREAM *)
  181.  
  182. PROCEDURE MARK(VAR X : STREAM);
  183.  
  184.   (* CAUSES BEGINNING OF STREAM TO BE POSITIONED BEFORE *)
  185.   (* CURRENT STREAM CURSOR.  BUFFERS GET RECLAIMED, LINE *)
  186.   (* COUNTERS RESET, ETC. *)
  187.  
  188.   VAR
  189.     P : LINEPOINTER;
  190.  
  191. BEGIN (* MARK *)
  192.   WITH X DO
  193.     IF HEAD <> NIL THEN
  194.       BEGIN
  195.       WHILE HEAD <> CURSOR DO (* RECLAIM BUFFERS *)
  196.         BEGIN
  197.           WITH HEAD^ DO
  198.             BEGIN P := NEXTLINE;
  199.               NEXTLINE := FREELINES;  FREELINES := HEAD
  200.             END;
  201.           HEAD := P
  202.         END;
  203.       HEADLINENO := CURSORLINENO;
  204.       IF CURSOR = NIL THEN
  205.         BEGIN  TAIL := NIL;  TAILLINENO := CURSORLINENO END
  206.     END
  207. END;  (* MARK *)
  208.  
  209. PROCEDURE MOVECURSOR(VAR X : STREAM;  VAR FILEX : TEXT);
  210.  
  211.   (* FILEX IS THE INPUT FILE ASSOCIATED WITH STREAM X.  THE *)
  212.   (* CURSOR FOR X IS MOVED FORWARD ONE LINE, READING FROM X *)
  213.   (* IF NECESSARY, AND INCREMENTING THE LINE COUNT.  ENDFILE *)
  214.   (* IS SET IF EOF IS ENCOUNTERED ON EITHER STREAM. *)
  215.  
  216.   PROCEDURE READLINE;
  217.      (* Read from FILEX.  To save disk drive wear on small-buffer systems, *)
  218.      (* this will try to read MINREAD lines at a time.  The only           *)
  219.      (* disadvantage of a large MINREAD is running out of heap sooner on   *)
  220.      (* large mismatches.   *)
  221.      (* Example: CP/M with 128 byte buffer: This source code took 298 disk *)
  222.      (* 'clicks' with MINREAD=1 and 28 clicks with MINREAD=40.  *)
  223.      (* CDC version read into an unpacked TEMPLINE, eliminated here *)
  224.     CONST
  225.     ORDCR=13; MASK=127; (* ASCII values; to equate CR and RI *)
  226.     VAR
  227.       NEWLINE : LINEPOINTER;
  228.       C, C2 : 0..LINELENGTH;
  229.       CH : CHAR;
  230.       MOREREADS : INTEGER;
  231.   BEGIN (* READLINE *)
  232.     MOREREADS := MINREAD;
  233.     WHILE (NOT X.ENDFILE) AND (MOREREADS>0) DO
  234.       BEGIN
  235.     (* allocate space for the line *)
  236.         NEWLINE := FREELINES;
  237.         IF NEWLINE <> NIL THEN FREELINES := FREELINES^.NEXTLINE
  238.         ELSE (* need more from heap *)
  239.             BEGIN
  240.            (* Sould check for heap exhaustion here.  Even if check is ok here, *)
  241.            (* recursive calls may later cause stack/heap collision. *)
  242.            NEW(NEWLINE);
  243.            NEWLINE^.LENGTH := LINELENGTH; (* new: must blank fill *)
  244.         END;
  245.         C := 0;
  246.     WITH NEWLINE^ DO
  247.     IF (Turbo AND RIeoln)
  248.  
  249.     THEN
  250.             (* for SELECT wp: either CR or RI terminates line *)
  251.       BEGIN
  252.         IF NOT EOF(FILEX) THEN READ(FILEX,CH); (* first char of line *)
  253.             WHILE ((ORD(CH) AND MASK)<>ORDCR)
  254.                   AND (C < LINELENGTH)
  255.                   AND (NOT EOF(FILEX))   DO
  256.                 BEGIN  C := C + 1; IMAGE[C]:=CH; READ(FILEX,CH); END;
  257.             WHILE ((ORD(CH) AND MASK)<>ORDCR) AND (NOT EOF(FILEX)) DO
  258.                 BEGIN READ(FILEX,CH);   LINESTOOLONG := TRUE; END;
  259.             IF NOT EOF(FILEX) THEN READ(FILEX,CH); (* should be the LF *)
  260.       END
  261.  
  262.     ELSE
  263.             (* normal lines--terminated by standard Pascal EOLN *)
  264.       BEGIN
  265.             WHILE NOT EOLN(FILEX) AND (C < LINELENGTH) DO
  266.                 BEGIN  C := C + 1; READ(FILEX,IMAGE[C]); END;
  267.             IF NOT EOLN(FILEX) THEN  LINESTOOLONG := TRUE;
  268.             READLN(FILEX);
  269.       END;
  270.  
  271.         WHILE (NEWLINE^.IMAGE[C] = ' ') AND (C>1) DO C := C - 1;
  272.         WITH NEWLINE^ DO
  273.           IF C < LENGTH THEN
  274.             FOR C2 := C+1 TO LENGTH DO IMAGE[C2] := ' ';
  275.         NEWLINE^.LENGTH := C;
  276.         NEWLINE^.NEXTLINE := NIL;
  277.         IF X. TAIL = NIL THEN
  278.           BEGIN  X.HEAD :=NEWLINE;
  279.             X.TAILLINENO :=1;  X. HEADLINENO := 1
  280.           END
  281.         ELSE
  282.           BEGIN X.TAIL^.NEXTLINE := NEWLINE;
  283.             X.TAILLINENO := X. TAILLINENO + 1
  284.           END;
  285.         X.TAIL := NEWLINE;
  286.         X.ENDFILE := EOF(FILEX);
  287.         MOREREADS := MOREREADS - 1;
  288.       END
  289.   END;  (* READLINE *)
  290.  
  291. BEGIN (* MOVECURSOR *)
  292.   IF X. CURSOR <> NIL THEN
  293.     BEGIN
  294.       IF X. CURSOR = X.TAIL THEN READLINE;
  295.       X.CURSOR := X.CURSOR^.NEXTLINE;
  296.       IF X.CURSOR = NIL THEN ENDFILE := TRUE;
  297.       X.CURSORLINENO := X.CURSORLINENO + 1
  298.       END
  299.     ELSE
  300.       IF NOT X.ENDFILE THEN (* BEGINNING OF STREAM *)
  301.         BEGIN
  302.           READLINE; X.CURSOR := X.HEAD;
  303.           X.CURSORLINENO := X. HEADLINENO
  304.         END
  305.       ELSE (* END OF STREAM  *)
  306.         ENDFILE := TRUE;
  307. END;  (* MOVECURSOR *)
  308.  
  309. PROCEDURE BACKTRACK(VAR X : STREAM;  VAR XLINES : INTEGER);
  310.  
  311.   (* CAUSES THE CURRENT POSITION OF STREAM X TO BECOME THAT *)
  312.   (* OF THE LAST MARK OPERATION.  I.E., THE CURRENT LINE   *)
  313.   (* WHEN THE STREAM WAS MARKED LAST BECOMES THE NEW CURSOR.  *)
  314.   (* XLINES IS SET TO THE NUMBER OF LINES FROM THE NEW CURSOR  *)
  315.   (* TO THE OLD CURSOR, INCLUSIVE. *)
  316.  
  317. BEGIN (* BACKTRACK *)
  318.   XLINES := X.CURSORLINENO + 1 - X.HEADLINENO;
  319.   X.CURSOR := X.HEAD;  X.CURSORLINENO := X.HEADLINENO;
  320.   ENDFILE := ENDSTREAM(A) OR ENDSTREAM(B)
  321. END;  (* BACKTRACK *)
  322.  
  323. PROCEDURE COMPARELINES(VAR MATCH : BOOLEAN);
  324.  
  325.   (* COMPARE THE CURRENT LINES OF STREAMS A AND B, RETURNING *)
  326.   (* MATCH TO SIGNAL THEIR (NON-) EQUIVALENCE.  EOF ON BOTH STREAMS *)
  327.   (* IS CONSIDERED A MATCH, BUT EOF ON ONLY ONE STREAM IS A MISMATCH *)
  328.  
  329. BEGIN (* COMPARELINES *)
  330.   IF (A.CURSOR = NIL) OR (B.CURSOR =NIL) THEN
  331.     MATCH := ENDSTREAM(A) AND ENDSTREAM(B)
  332.   ELSE
  333.     BEGIN
  334.       MATCH := (A.CURSOR^.LENGTH = B.CURSOR^.LENGTH);
  335.       IF MATCH THEN
  336.         MATCH := (A.CURSOR^.IMAGE =B.CURSOR^.IMAGE)
  337.     END
  338. END;  (* COMPARELINES *)
  339.  
  340. PROCEDURE FINDMISMATCH;
  341. BEGIN (* FINDMISMATCH *)
  342.   (* NOT ENDFILE AND MATCH *)
  343.   REPEAT  (* COMPARENEXTLINES *)
  344.     MOVECURSOR(A, FILEA); MOVECURSOR(B,FILEB);
  345.     MARK(A); MARK(B);
  346.     COMPARELINES(MATCH)
  347.   UNTIL ENDFILE OR NOT MATCH;
  348. END;  (* FINDMISMATCH *)
  349.  
  350. PROCEDURE FINDMATCH;
  351.   VAR
  352.     ADVANCEB : BOOLEAN; (* TOGGLE ONE-LINE LOOKAHEAD BETWEEN STREAMS *)
  353.  
  354.   PROCEDURE SEARCH(VAR X : STREAM; (* STREAM TO SEARCH *)
  355.                    VAR FILEX : TEXT;
  356.                    VAR Y : STREAM; (*STREAM TO LOOKAHEAD *)
  357.                    VAR FILEY : TEXT);
  358.  
  359.     (* LOOK AHEAD ONE LINE ON STREAM Y, AND SEARCH FOR THAT LINE *)
  360.     (* BACKTRACKING ON STREAM X. *)
  361.  
  362.   VAR
  363.     COUNT : INTEGER;  (* NUMBER OF LINES BACKTRACKED ON X *)
  364.  
  365.   PROCEDURE CHECKFULLMATCH;
  366.     (* FROM THE CURRENT POSITIONS IN X AND Y, WHICH MATCH, *)
  367.     (* MAKE SURE THAT THE NEXT MINLINESFORMATCH-1 LINES ALSO *)
  368.     (* MATCH, OR ELSE SET MATCH := FALSE.  *)
  369.     VAR
  370.       N : INTEGER;
  371.       SAVEXCUR, SAVEYCUR : LINEPOINTER;
  372.       SAVEXLINE, SAVEYLINE : INTEGER;
  373.   BEGIN (* CHECKFULLMATCH *)
  374.     SAVEXCUR :=X.CURSOR;  SAVEYCUR := Y.CURSOR;
  375.     SAVEXLINE :=X.CURSORLINENO;  SAVEYLINE :=Y.CURSORLINENO;
  376.     COMPARELINES(MATCH);
  377.     N := MINLINESFORMATCH - 1;
  378.     WHILE MATCH AND (N <> 0) DO
  379.       BEGIN  MOVECURSOR(X, FILEX);  MOVECURSOR(Y, FILEY);
  380.         COMPARELINES(MATCH);  N := N - 1
  381.       END;
  382.     X.CURSOR := SAVEXCUR;  X.CURSORLINENO := SAVEXLINE;
  383.     Y.CURSOR := SAVEYCUR;  Y.CURSORLINENO := SAVEYLINE;
  384.   END;  (* CHECKFULLMATCH *)
  385.  
  386. BEGIN (* SEARCH *)
  387.   MOVECURSOR(Y, FILEY);  BACKTRACK(X, COUNT);
  388.   CHECKFULLMATCH;  COUNT := COUNT - 1;
  389.   WHILE (COUNT <> 0) AND NOT MATCH DO
  390.     BEGIN
  391.       MOVECURSOR(X, FILEX);  COUNT := COUNT - 1;
  392.       CHECKFULLMATCH
  393.     END
  394. END;  (* SEARCH *)
  395.  
  396. PROCEDURE PRINTMISMATCH;
  397.   VAR
  398.     EMPTYA, EMPTYB : BOOLEAN;
  399.  
  400.   PROCEDURE WRITEONELINE(NAME : CHAR;  L : INTEGER;  P : LINEPOINTER);
  401.   VAR I: INTEGER;
  402.     BEGIN  (* WRITEONELINE *)
  403.         FORTRAN;
  404.         WRITE(DIFFS,NAME, L:5,BarChar); BOLD(TRUE);
  405.         IF P^.LENGTH <> 0
  406.         THEN  FOR I:= 1 TO P^.LENGTH DO WRITE(DIFFS,P^.IMAGE[I]);
  407.         WRITELN(DIFFS);             BOLD(FALSE);
  408.   END;  (* WRITEONELINE  *)
  409.  
  410.   PROCEDURE WRITETEXT(VAR X : STREAM);
  411.     (* WRITE FROM X.HEAD TO ONE LINE BEFORE X.CURSOR *)
  412.     VAR
  413.       P, Q : LINEPOINTER;  LINENO : INTEGER;
  414.   BEGIN (* WRITETEXT *)
  415.     P:=X.HEAD;  Q:=X.CURSOR;   LINENO:=X.HEADLINENO;
  416.     WHILE (P <> NIL) AND (P <> Q) DO
  417.       BEGIN
  418.         WRITEONELINE( X.NAME, LINENO, P);
  419.         P := P^.NEXTLINE;
  420.         LINENO := LINENO + 1;
  421.       END;
  422.     IF P = NIL THEN WRITELN(DIFFS,' *** EOF ***');
  423.      (* WRITELN *)
  424.   END;  (* WRITETEXT *)
  425.  
  426.   PROCEDURE WRITEPAIRS( PA, PB : LINEPOINTER;  LA, LB : INTEGER);
  427.     (* THIS WRITES FROM THE HEAD TO THE CURSOR, LIKE PROCEDURE WRITETEXT. *)
  428.     (* UNLIKE PROCEDURE WRITETEXT, THIS WRITES FROM BOTH FILES AT ONCE,   *)
  429.     (* COMPARES COLUMNS WITHIN LINES, AND MARKS UNEQUAL COLUMNS     *)
  430.   VAR
  431.     TEMPA, TEMPB : LINEIMAGETYPE;
  432.     COL, MAXCOL  : INTEGER;
  433.   BEGIN  (* WRITEPAIRS *)
  434.     REPEAT
  435.       WRITEONELINE('A', LA, PA);   WRITEONELINE('B', LB, PB);
  436.       (* UNPACK(PA^.IMAGE,TEMPA,1);   UNPACK(PB^.IMAGE,TEMPB,1); *)
  437.       (*    IF  NO UNPACK ON YOUR COMPILER, USE THE FOLLOWING: *)
  438.       TEMPA := PA^.IMAGE;    TEMPB := PB^.IMAGE;
  439.       IF  PA^.LENGTH > PB^.LENGTH
  440.               THEN MAXCOL := PA^.LENGTH ELSE MAXCOL := PB^.LENGTH;
  441.       FORTRAN;
  442.       WRITE(DIFFS,' ': prespace);
  443.       FOR COL := 1 TO MAXCOL DO
  444.            IF TEMPA[COL] <> TEMPB[COL] THEN WRITE(DIFFS,'^')
  445.                ELSE BEGIN IF TEMPA[COL]=CHR(TAB) THEN WRITE(DIFFS,CHR(TAB))
  446.                                                  ELSE WRITE(DIFFS,' ');
  447.                     END;
  448.       WRITELN(DIFFS);(* WRITELN(DIFFS); *)
  449.       PA := PA^.NEXTLINE;  LA := LA + 1;
  450.       PB := PB^.NEXTLINE;  LB := LB + 1;
  451.     UNTIL (PA = A.CURSOR) OR (PA = NIL);
  452.   END;  (* WRITEPAIRS *)
  453.  
  454.   PROCEDURE WRITEBOLDPAIRS(PA, PB: LINEPOINTER; LA, LB: INTEGER);
  455.   (* Parallel to WRITEPAIRS, but uses ANSI screen control *)
  456.   VAR
  457.     MAXCOL, COL: INTEGER;
  458.     EQ: ARRAY[0..LINELENGTH] OF BOOLEAN;
  459.  
  460.       PROCEDURE WRITEbONELINE(NAME : CHAR; L : INTEGER; P : LINEPOINTER);
  461.       (* Parallel to WRITEONELINE *) (* imports: MAXCOL, EQ *)
  462.       VAR I: INTEGER;
  463.         BEGIN (* WRITEbONELINE *)
  464.           FORTRAN;
  465.           WRITE(DIFFS, NAME, L:5, BarChar);
  466.           FOR I := 1 TO MAXCOL DO
  467.             BEGIN
  468.               IF EQ[I-1] AND NOT EQ[I] THEN BOLD(TRUE)
  469.                 ELSE IF (NOT EQ[I-1]) AND EQ[I] THEN BOLD(FALSE);
  470.               WRITE(DIFFS,P^.IMAGE[I]);
  471.             END;
  472.           BOLD(FALSE); WRITELN(DIFFS);
  473.        END; (* WRITEbONELINE *)
  474.  
  475.   BEGIN (* WRITEBOLDPAIRS *)
  476.     EQ[0] := TRUE;
  477.     REPEAT
  478.       IF PA^.LENGTH > PB^.LENGTH
  479.         THEN MAXCOL := PA^.LENGTH ELSE MAXCOL := PB^.LENGTH;
  480.       FOR COL := 1 TO MAXCOL DO
  481.         EQ[COL] := PA^.IMAGE[COL] = PB^.IMAGE[COL];
  482.       WRITEbONELINE('A', LA, PA); WRITEbONELINE('B', LB, PB); WRITELN(DIFFS);
  483.       PA := PA^.NEXTLINE;  LA := LA + 1;
  484.       PB := PB^.NEXTLINE;  LB := LB + 1;
  485.     UNTIL (PA = A.CURSOR) OR (PA = NIL);
  486.   END (* WRITEBOLDPAIRS *);
  487.  
  488.   PROCEDURE WRITELINENO(VAR X : STREAM);
  489.     VAR
  490.       F, L : INTEGER;
  491.   BEGIN (* WRITELINENO *)
  492.     WRITE(DIFFS,' file',X.NAME,', ');
  493.     F := X.HEADLINENO; L := X.CURSORLINENO - 1;
  494.     WRITE(DIFFS,'line');
  495.     IF F = L THEN WRITE(DIFFS,' ', F:1)
  496.     ELSE WRITE(DIFFS,'s ', F:1, ' - ', L:1);
  497.     IF X.CURSOR = NIL THEN WRITE(DIFFS,' (before EOF)');
  498.   END;  (* WRITELINENO *)
  499.  
  500.   PROCEDURE PRINTEXTRATEXT(VAR X, Y : STREAM);
  501.  
  502.   BEGIN (* PRINTEXTRATEXT *)
  503.     WRITE(DIFFS,'EXTRA TEXT:  on file', X.NAME, ', ');
  504.  
  505.     IF Y.HEAD = NIL THEN
  506.       WRITELN(DIFFS,' before EOF on file', Y.NAME)
  507.     ELSE
  508.       WRITELN(DIFFS,' between lines ', Y.HEADLINENO-1:1, ' and ',
  509.               Y.HEADLINENO:1, ' of file', Y.NAME);
  510.     UNDERLINE(FALSE);
  511.     WRITELN(DIFFS);
  512.     WRITETEXT(X)
  513.   END;  (* PRINTEXTRATEXT *)
  514.  
  515.   BEGIN (* PRINTMISMATCH *)
  516.     WRITELN(DIFFS);
  517.     FORTRAN; BOLD(FALSE); UNDERLINE(TRUE);
  518.     (* write a divider if no underlining/bold available *)
  519.     IF NOT ANSIoutput
  520.     THEN  WRITELN(DIFFS,' ':prespace,
  521.      '*************************************************************');
  522.     EMPTYA := (A.HEAD = A.CURSOR);
  523.     EMPTYB := (B.HEAD = B.CURSOR);
  524.     IF EMPTYA OR EMPTYB THEN
  525.       IF EMPTYA THEN PRINTEXTRATEXT(B, A)
  526.       ELSE PRINTEXTRATEXT(A, B)
  527.     ELSE
  528.       BEGIN
  529.         FORTRAN;
  530.         WRITE(DIFFS,'MISMATCH:   ');
  531.         WRITELINENO(A);  WRITE(DIFFS,'  NOT EQUAL TO ');
  532.         WRITELINENO(B);  WRITELN(DIFFS,':');
  533.         UNDERLINE(FALSE);
  534.         WRITELN(DIFFS);
  535.         IF  (MARKUNEQUALCOLUMNS > (A.CURSORLINENO - A.HEADLINENO -1) )    AND
  536.             ((A.CURSORLINENO - A.HEADLINENO) = (B.CURSORLINENO - B.HEADLINENO))
  537.         THEN
  538.            BEGIN
  539.            IF ANSIoutput
  540.              THEN WRITEBOLDPAIRS(A.HEAD, B.HEAD, A.HEADLINENO, B.HEADLINENO)
  541.              ELSE WRITEPAIRS(A.HEAD, B.HEAD, A.HEADLINENO, B.HEADLINENO)
  542.            END
  543.         ELSE
  544.          BEGIN     WRITETEXT(A);
  545.         WRITELN(DIFFS,' ':prespace,'----------------');
  546.         WRITETEXT(B)
  547.           END;
  548.       END
  549.   END;  (* PRINTMISMATCH *)
  550.  
  551. BEGIN (* FINDMATCH *)
  552.   (* NOT MATCH *)
  553.   ADVANCEB := TRUE;
  554.   REPEAT
  555.     IF NOT ENDFILE THEN ADVANCEB := NOT ADVANCEB
  556.     ELSE ADVANCEB := ENDSTREAM(A);
  557.     IF ADVANCEB THEN SEARCH(A, FILEA, B, FILEB)
  558.       ELSE SEARCH(B, FILEB, A, FILEA)
  559.   UNTIL MATCH;
  560.   PRINTMISMATCH;
  561. END;  (* FINDMATCH *)
  562.  
  563. BEGIN (* COMPAREFILES *)
  564.   MATCH := TRUE;  (* I.E., BEGINNINGS-OF-FILES MATCH *)
  565.   REPEAT
  566.     IF MATCH THEN FINDMISMATCH ELSE BEGIN SAME := FALSE; FINDMATCH END
  567.   UNTIL ENDFILE AND MATCH;
  568.   (* MARK(A); MARK(B);  MARK END OF FILES, THEREBY DISPOSING BUFFERS *)
  569. END;  (* COMPAREFILES *)
  570.  
  571. PROCEDURE INITIALIZE;
  572. (* setup files, using names from command line *)
  573. (* IMPORT from file ArgLib.pas:  ArgStrType, argcount, argv, resetOK *)
  574. var ArgFileA,ArgFileB,ArgFileC: ArgStrType;
  575.     nFiles: integer;
  576.     ArgumentsOK: boolean;
  577.  
  578.   PROCEDURE INITSTREAM(NAMECHAR : CHAR; VAR X : STREAM;
  579.                        VAR FILEX : TEXT; Arg: ArgStrType);
  580.   BEGIN (* INITSTREAM *)
  581.     IF resetOK(FILEX, Arg)
  582.       THEN   X.ENDFILE := EOF(FILEX)
  583.       ELSE
  584.         BEGIN    BOLD(TRUE);
  585.           WRITE(DIFFS,' ERROR   Cannot find'); ArgumentsOK := FALSE;
  586.         END;
  587.     WITH X DO
  588.       BEGIN
  589.         NAME := NAMECHAR;
  590.         CURSOR :=NIL;  HEAD := NIL;  TAIL := NIL;
  591.         CURSORLINENO := 0; HEADLINENO := 0; TAILLINENO := 0;
  592.         BOLD(TRUE); WRITELN(DIFFS,' file', NAME,': ', Arg); BOLD(FALSE);
  593.       END;
  594. END;  (* INITSTREAM *)
  595.  
  596.  
  597. BEGIN (* INITIALIZE *)
  598.   nFiles := argcount;
  599.   if (nFiles < 2) or (nFiles > 3)
  600.    THEN
  601.      BEGIN
  602.         WRITELN({DIFFS,}'Usage:  COMPARE  FileA  FileB');
  603.         WRITELN({DIFFS,}'   or   COMPARE  FileA  FileB  Report-file');
  604.         ArgumentsOK:=FALSE;
  605.      END
  606.    ELSE
  607.     BEGIN
  608.      argv(1,ArgFileA); argv(2,ArgFileB);
  609.      ArgumentsOK := resetOK(FILEA,ArgFileA) AND resetOK(FILEB,ArgFileB);
  610.      IF ArgumentsOK AND (nFiles > 2)
  611.        THEN  BEGIN
  612.                argv(3,ArgFileC); Assign(DIFFS,ArgFileC);
  613.                ANSIoutput := ANSIdiffs;
  614.              END
  615.        ELSE BEGIN Assign(DIFFS,'CON:'); ANSIoutput:=ANSIcon; END;
  616.      REWRITE(DIFFS);
  617.      { PAGE(OUTPUT); }  FORTRAN;
  618.      WRITE(DIFFS,'Compare');
  619.      WRITE(DIFFS,' version ', VERSION,'  (Options:');
  620.      IF RIeoln THEN WRITE(DIFFS,' RI=CR,');
  621.      WRITELN(DIFFS,' rematch on ', MINLINESFORMATCH:1, ' lines.)');
  622.      INITSTREAM('A',A, FILEA,ArgFileA);
  623.      INITSTREAM('B',B, FILEB,ArgFileB);
  624.      ENDFILE := A.ENDFILE OR B.ENDFILE;
  625.      LINESTOOLONG := FALSE;
  626.      FREELINES := NIL;
  627.      IF ArgumentsOK THEN   NARROWPRINTING(TRUE);
  628.    END;
  629.  IF NOT ArgumentsOK then ENDFILE := TRUE;
  630. END; (*INITIALIZE*)
  631.  
  632.  
  633. BEGIN (*COMPARE*)
  634.   INITIALIZE;
  635.   IF NOT ENDFILE THEN
  636.     BEGIN  SAME := TRUE;
  637.       COMPAREFILES;
  638.       WRITELN(DIFFS);
  639.       IF SAME
  640.             THEN WRITELN(DIFFS,' ',(A.CURSORLINENO-1):1,
  641.                 ' lines read; no differences.')
  642.             ELSE WRITELN(DIFFS,' files are different.');
  643.       IF LINESTOOLONG THEN
  644.         BEGIN     WRITELN(DIFFS);
  645.          WRITELN(DIFFS,' WARNING:  Some lines were longer than ',
  646.                                 LINELENGTH:1, ' characters.');
  647.          WRITELN(DIFFS,' ':11,'They were not compared past that point.');
  648.         END;
  649.       NARROWPRINTING(FALSE);
  650.     END;
  651.   IF Turbo THEN CLOSE(DIFFS);  (* Turbo nonstandardly requires CLOSE *)
  652. END.  (* COMPARE *)
  653.