1 /************************************************
3 ************************************************/
5 /* A program to compare two files line by line.
9 It was originally written in C, but the C under
10 Panos is still a shambles (1986). Translated therefore
11 to BCPL -- this explains some of the odd style.
13 Modified to run on Archimedes, August 1987.
14 Modified to run under MSDOS, March 1989.
15 Modified to run under CINTERP interpreter, July 1989.
16 Modified to run under Unix, October 1989.
18 Translated back into C, March 1990! */
20 /* Copyright (c) 1986, 1987, 1989, 1990, 1994, 2001 by Philip Hazel */
22 /* Previously modified: October 1994*/
23 /* Last modified: September 2001 - a long-lived bug fixed! */
36 # define intptr_t long long int
39 /* ----- parameters ----- */
42 #define defaultstore 100000 /* default recovery buffer size */
43 #define minstore 500 /* minimum recovery buffer size */
44 #define SHOWMAX 20 /* maximum number of diff lines to display */
46 /* ----- misc defines ----- */
57 #define EqString(s, t) (strcmp(s, t) == 0)
59 /* ----- line structure ----- */
68 /* ----- global variables ----- */
70 FILE *f_one; /* files */
74 int lines_one = 0; /* line counts */
77 int eof_one = FALSE; /* eof flags */
79 int exact = FALSE; /* TRUE => no strip spaces */
80 int echo = TRUE; /* TRUE => show mismatched lines */
81 int sync_count = 3; /* resync count */
82 int storesize = defaultstore; /* size of each buffer */
84 char *name_one = NULL; /* file names */
85 char *name_two = NULL;
88 char *bufbase_one; /* start buffer */
90 char *bufnext_one; /* next free byte */
92 char *buftop_one; /* end buffer */
95 line *rootline_one; /* mis-match point */
97 line *lastline_one; /* last in store */
99 line *pline_one; /* working line */
103 /*************************************************
105 *************************************************/
109 printf("PH's CMP v%d\n", version);
110 printf("Keywords:\n");
111 printf(" <file> ) files to compare\n");
112 printf(" <file> ) no keywords used\n");
113 printf("-to <file> output destination\n");
114 printf("-exact include trailing spaces & match tabs\n");
115 printf("-noecho don't echo differences (just give line numbers)\n");
116 printf("-s, -sync <n> set re-sync count, default 3\n");
117 printf("-buffer <n> buffer size (for each file) default 100000\n");
118 printf("-id give program version id\n");
119 printf("-h, -help give this help\n");
120 printf("\nExamples:\n");
121 printf("cmp old.f77 new.f77\n");
122 printf("cmp first second -noecho -sync 1\n");
123 printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
128 /************************************************
129 * Errors -- all serious *
130 ************************************************/
132 void moan(code, text)
136 fprintf(stderr, "\n** ");
140 fprintf(stderr, "Unable to open file \"%s\"", text);
143 fprintf(stderr, " - ");
146 else fprintf(stderr, "\n");
150 fprintf(stderr, "Buffer overflow for file \"%s\"\n", text);
154 fprintf(stderr, "Two file names must be given\n");
158 fprintf(stderr, "Unknown error %d\n", code);
162 fprintf(stderr, "** CMP abandoned\n");
168 /*************************************************
169 * Write line identification *
170 *************************************************/
172 void write_id(n1, n2, c, name, p1, p2)
174 char *name, *p1, *p2;
176 if (n2 < 0) n2 = -n2;
178 fprintf(f_out, "%cine", c);
179 if (n1 == n2) fprintf(f_out, " %d of \"%s\"%s", n1, name, p1);
180 else fprintf(f_out, "s %d-%d of \"%s\"%s", n1, n2, name, p2);
184 /*************************************************
185 * Write sequence of lines *
186 *************************************************/
188 void write_lines(s, t)
194 while (*p != '\n') fputc(*p++, f_out);
202 /*************************************************
203 * Write separator rule *
204 *************************************************/
209 while (l-- > 0) fprintf(f_out, "%c", s);
210 fprintf(f_out, "\n");
215 /*************************************************
216 * Write message on re-sync or eof *
217 *************************************************/
219 void write_message(tline_one, tline_two)
220 line *tline_one, *tline_two;
222 int s1 = rootline_one->number;
223 int t1 = tline_one->number;
224 int s2 = rootline_two->number;
225 int t2 = tline_two->number;
226 if (echo) rule('=', 15);
230 write_id(s2, t2, 'L', name_two, " occurs ", " occur ");
231 if (s1 < 0) fprintf(f_out, "at the end");
232 else fprintf(f_out, "before line %d", s1);
233 fprintf(f_out, " of \"%s\".\n", name_one);
237 write_lines(rootline_two, tline_two);
243 write_id(s1, t1, 'L', name_one, " occurs ", " occur ");
244 if (s2 < 0) fprintf(f_out, "at the end");
245 else fprintf(f_out, "before line %d", s2);
246 fprintf(f_out, " of \"%s\".\n", name_two);
250 write_lines(rootline_one, tline_one);
254 else if (t1 < 0 && t2 < 0)
256 fprintf(f_out, "From line %d of \"%s\" and line %d of \"%s\" ",
257 rootline_one->number, name_one, rootline_two->number, name_two);
258 fprintf(f_out, "the files are different.\n");
262 if (-t1-s1 < SHOWMAX+1) write_lines(rootline_one, tline_one);
263 else fprintf(f_out, "... <more than %d lines> ...\n", SHOWMAX);
265 if (-t2-s2 < SHOWMAX+1) write_lines(rootline_two, tline_two);
266 else fprintf(f_out, "... <more than %d lines> ...\n", SHOWMAX);
272 write_id(s1, t1, 'L', name_one, " does ", " do ");
273 fprintf(f_out, "not match ");
274 write_id(s2, t2, 'l', name_two, ".\n", ".\n");
278 write_lines(rootline_one, tline_one);
280 write_lines(rootline_two, tline_two);
288 /*************************************************
289 * Advance to next line in store *
290 *************************************************/
292 /* A separate procedure exists for each file, for
293 simplicity and efficiency. */
295 int nextline_one(pvoid)
297 if (pline_one == NULL || pline_one->next == NULL) return FALSE;
298 pline_one = pline_one->next;
302 int nextline_two(pvoid)
304 if (pline_two == NULL || pline_two->next == NULL) return FALSE;
305 pline_two = pline_two->next;
310 /*************************************************
311 * Read a line into store *
312 *************************************************/
314 /* A separate procedure exists for each file, for
315 simplicity and efficiency. */
317 void readline_one(pvoid)
320 int c = fgetc(f_one);
321 line *nextline = (line *)bufnext_one;
323 bufnext_one = nextline->text;
324 if (bufnext_one >= buftop_one) moan(2, name_one);
326 nextline->next = NULL;
332 nextline->number = -lines_one;
336 nextline->number = lines_one;
339 if (c == EOF) c = '\n';
343 while (bufnext_one > nextline->text)
344 { if (bufnext_one[-1] == ' ') bufnext_one--; else break; }
345 *(bufnext_one++) = '\n';
346 if (bufnext_one >= buftop_one) moan(2, name_one);
349 if (c == '\t' && !exact)
350 do { *(bufnext_one++) = ' '; count++; } while ((count & 7) != 0);
351 else { *(bufnext_one++) = c; count++; }
352 if (bufnext_one >= buftop_one) moan(2, name_one);
357 if (lastline_one != NULL) lastline_one->next = nextline;
358 lastline_one = nextline;
359 pline_one = nextline;
361 bufnext_one = (char *) (((intptr_t)bufnext_one+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
366 void readline_two(pvoid)
369 int c = fgetc(f_two);
370 line *nextline = (line *)bufnext_two;
372 bufnext_two = nextline->text;
373 if (bufnext_two >= buftop_two) moan(2, name_two);
375 nextline->next = NULL;
381 nextline->number = -lines_two;
385 nextline->number = lines_two;
388 if (c == EOF) c = '\n';
392 while (bufnext_two > nextline->text)
393 { if (bufnext_two[-1] == ' ') bufnext_two--; else break; }
394 *(bufnext_two++) = '\n';
395 if (bufnext_two >= buftop_two) moan(2, name_two);
398 if (c == '\t' && !exact)
399 do { *(bufnext_two++) = ' '; count++; } while ((count & 7) != 0);
400 else { *(bufnext_two++) = c; count++; }
401 if (bufnext_two >= buftop_two) moan(2, name_two);
406 if (lastline_two != NULL) lastline_two->next = nextline;
407 lastline_two = nextline;
408 pline_two = nextline;
410 bufnext_two = (char *) (((intptr_t)bufnext_two+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
415 /**************************************************
416 * Compare two lines *
417 **************************************************/
419 int compare_lines(a, b)
427 if (n1 < 0 && n2 < 0) return TRUE;
428 if (n1 < 0 || n2 < 0) return FALSE;
432 if (*s == '\n') return TRUE;
440 /*************************************************
441 * Re-synchronizing code *
442 *************************************************/
448 line *tline_one = pline_one;
449 line *tline_two = pline_two;
451 if (eof_one || eof_two) matched = FALSE; else
453 for (i = 1; i < sync_count; i++)
455 if (!nextline_one()) readline_one();
456 if (!nextline_two()) readline_two();
457 if (!compare_lines(pline_one, pline_two)) { matched = FALSE; break; }
458 if (eof_one || eof_two) { matched = FALSE; break; }
462 if (matched) write_message(tline_one, tline_two); else
464 pline_one = tline_one;
465 pline_two = tline_two;
473 /*************************************************
474 * Main compare code *
475 *************************************************/
481 /* Big main loop - exit by return or unmatched at eof */
485 /* First minor loop, while in step */
487 while (matched && !eof_one && !eof_two)
489 /* Advance or read next lines */
493 bufnext_one = bufbase_one;
500 bufnext_two = bufbase_two;
505 /* Compare and check for end of file */
507 matched = compare_lines(pline_one, pline_two);
509 } /* End first minor loop */
511 if (matched) return; /* successful end of file */
513 /* There has been a mis-match */
516 rootline_one = pline_one; /* Fail point */
517 rootline_two = pline_two;
519 /* Second minor loop, trying to regain sync */
521 while (!eof_one || !eof_two)
523 /* Advance one and scan all of two */
527 line *zline = pline_two;
528 if (!nextline_one()) readline_one();
529 pline_two = rootline_two;
532 if (compare_lines(pline_one, pline_two))
537 if (pline_two == zline) break;
538 pline_two = pline_two->next;
543 /* Advance two and scan all of one */
547 line *zline = pline_one;
548 if (!nextline_two()) readline_two();
549 pline_one = rootline_one;
552 if (compare_lines(pline_one, pline_two))
557 if (pline_one == zline) break;
558 pline_one = pline_one->next;
563 } /* End second minor loop */
565 } /* End of major loop */
567 write_message(lastline_one, lastline_two);
573 /*************************************************
575 *************************************************/
583 int arg_help = FALSE;
587 /* Scan argument strings */
591 char *arg = argv[argp];
592 char **lv_name = (name_one == NULL)? &name_one:&name_two; /* default for positional */
593 int *lv_value = NULL;
597 { /* keyed argument */
598 if (EqString(arg,"-help") || EqString(arg, "-h"))
599 { arg_help = TRUE; value = FALSE; }
600 else if (EqString(arg, "-id"))
601 { arg_id = TRUE; value = FALSE; }
602 else if (EqString(arg, "-exact"))
603 { exact = TRUE; value = FALSE; }
604 else if (EqString(arg, "-noecho"))
605 { echo = FALSE; value = FALSE; }
606 else if (EqString(arg, "-to")) lv_name = &to_name;
607 else if (EqString(arg, "-sync") || EqString(arg, "-s"))
608 lv_value = &sync_count;
609 else if (EqString(arg, "-buffer")) lv_value = &storesize;
610 else { printf("Unknown keyword %s\n", arg); exit(99); }
612 if (++argp >= argc && value)
613 { printf("Value for keyword %s missing\n", arg); exit(99); }
616 /* Deal with keys that take values */
620 if (lv_value == &sync_count || lv_value == &storesize)
624 char *argval = argv[argp++];
626 while ((ch = argval[i++]) != 0)
628 if ('0' <= ch && ch <= '9') *lv_value = 10*(*lv_value) + ch - '0'; else
630 printf("Number expected after \"%s\" but \"%s\" read\n",
637 else if (*lv_name != NULL)
639 printf("Keyword expected but \"%s\" read", arg);
640 printf(" - use \"cmp -h\" for help\n");
643 else *lv_name = argv[argp++];
647 /* Deal with help and id */
649 if (arg_id && !arg_help)
651 printf("PH's CMP v%d\n", version);
661 /* Deal with file names */
663 if (name_one == NULL || name_two == NULL) moan(3, "");
667 f_out = fopen(to_name, "w");
668 if (f_out == NULL) moan(1, to_name);
671 /* Further general initialization */
673 if (storesize < minstore) storesize = defaultstore;
674 f_one = fopen(name_one, "r");
675 if (f_one == NULL) moan(1, name_one);
676 f_two = fopen(name_two, "r");
677 if (f_two == NULL) moan(1, name_two);
679 bufbase_one = (char *)malloc(storesize);
680 buftop_one = bufbase_one + storesize;
681 bufbase_two = (char *)malloc(storesize);
682 buftop_two = bufbase_two + storesize;
690 if (return_code == 0)
691 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
694 if (echo) rule('=', 15);
695 fprintf(f_out, "%d difference", return_code);
696 if (return_code != 1) fprintf(f_out, "s");
697 fprintf(f_out, " found.\n");
700 fprintf(f_out, "\"%s\" contains %d line", name_one, lines_one);
701 if (lines_one != 1) fprintf(f_out, "s");
704 fprintf(f_out, "; \"%s\" contains %d line", name_two, lines_two);
705 if (lines_two != 1) fprintf(f_out, "s");
706 fprintf(f_out, ".\n");
714 if (f_out != stdout) fclose(f_out);
719 /* End of PH-Compare. */