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 */
45 /* ----- misc defines ----- */
56 #define EqString(s, t) (strcmp(s, t) == 0)
58 /* ----- line structure ----- */
67 /* ----- global variables ----- */
69 FILE *f_one; /* files */
73 int lines_one = 0; /* line counts */
76 int eof_one = FALSE; /* eof flags */
78 int exact = FALSE; /* TRUE => no strip spaces */
79 int echo = TRUE; /* TRUE => show mismatched lines */
80 int sync_count = 3; /* resync count */
81 int storesize = defaultstore; /* size of each buffer */
83 char *name_one = NULL; /* file names */
84 char *name_two = NULL;
87 char *bufbase_one; /* start buffer */
89 char *bufnext_one; /* next free byte */
91 char *buftop_one; /* end buffer */
94 line *rootline_one; /* mis-match point */
96 line *lastline_one; /* last in store */
98 line *pline_one; /* working line */
102 /*************************************************
104 *************************************************/
108 printf("PH's CMP v%d\n", version);
109 printf("Keywords:\n");
110 printf(" <file> ) files to compare\n");
111 printf(" <file> ) no keywords used\n");
112 printf("-to <file> output destination\n");
113 printf("-exact include trailing spaces & match tabs\n");
114 printf("-noecho don't echo differences (just give line numbers)\n");
115 printf("-s, -sync <n> set re-sync count, default 3\n");
116 printf("-buffer <n> buffer size (for each file) default 100000\n");
117 printf("-id give program version id\n");
118 printf("-h, -help give this help\n");
119 printf("\nExamples:\n");
120 printf("cmp old.f77 new.f77\n");
121 printf("cmp first second -noecho -sync 1\n");
122 printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
127 /************************************************
128 * Errors -- all serious *
129 ************************************************/
131 void moan(code, text)
135 fprintf(stderr, "\n** ");
139 fprintf(stderr, "Unable to open file \"%s\"", text);
142 fprintf(stderr, " - ");
145 else fprintf(stderr, "\n");
149 fprintf(stderr, "Buffer overflow for file \"%s\"\n", text);
153 fprintf(stderr, "Two file names must be given\n");
157 fprintf(stderr, "Unknown error %d\n", code);
161 fprintf(stderr, "** CMP abandoned\n");
167 /*************************************************
168 * Write line identification *
169 *************************************************/
171 void write_id(n1, n2, c, name, p1, p2)
173 char *name, *p1, *p2;
175 if (n2 < 0) n2 = -n2;
177 fprintf(f_out, "%cine", c);
178 if (n1 == n2) fprintf(f_out, " %d of \"%s\"%s", n1, name, p1);
179 else fprintf(f_out, "s %d-%d of \"%s\"%s", n1, n2, name, p2);
183 /*************************************************
184 * Write sequence of lines *
185 *************************************************/
187 void write_lines(s, t)
193 while (*p != '\n') fputc(*p++, f_out);
201 /*************************************************
202 * Write separator rule *
203 *************************************************/
208 while (l-- > 0) fprintf(f_out, "%c", s);
209 fprintf(f_out, "\n");
214 /*************************************************
215 * Write message on re-sync or eof *
216 *************************************************/
218 void write_message(tline_one, tline_two)
219 line *tline_one, *tline_two;
221 int s1 = rootline_one->number;
222 int t1 = tline_one->number;
223 int s2 = rootline_two->number;
224 int t2 = tline_two->number;
225 if (echo) rule('=', 15);
229 write_id(s2, t2, 'L', name_two, " occurs ", " occur ");
230 if (s1 < 0) fprintf(f_out, "at the end");
231 else fprintf(f_out, "before line %d", s1);
232 fprintf(f_out, " of \"%s\".\n", name_one);
236 write_lines(rootline_two, tline_two);
242 write_id(s1, t1, 'L', name_one, " occurs ", " occur ");
243 if (s2 < 0) fprintf(f_out, "at the end");
244 else fprintf(f_out, "before line %d", s2);
245 fprintf(f_out, " of \"%s\".\n", name_two);
249 write_lines(rootline_one, tline_one);
253 else if (t1 < 0 && t2 < 0)
255 fprintf(f_out, "From line %d of \"%s\" and line %d of \"%s\" ",
256 rootline_one->number, name_one, rootline_two->number, name_two);
257 fprintf(f_out, "the files are different.\n");
261 if (-t1-s1 < 21) write_lines(rootline_one, tline_one);
262 else fprintf(f_out, "... <more than 20 lines> ...\n");
264 if (-t2-s2 < 21) write_lines(rootline_two, tline_two);
265 else fprintf(f_out, "... <more than 20 lines> ...\n");
271 write_id(s1, t1, 'L', name_one, " does ", " do ");
272 fprintf(f_out, "not match ");
273 write_id(s2, t2, 'l', name_two, ".\n", ".\n");
277 write_lines(rootline_one, tline_one);
279 write_lines(rootline_two, tline_two);
287 /*************************************************
288 * Advance to next line in store *
289 *************************************************/
291 /* A separate procedure exists for each file, for
292 simplicity and efficiency. */
294 int nextline_one(pvoid)
296 if (pline_one == NULL || pline_one->next == NULL) return FALSE;
297 pline_one = pline_one->next;
301 int nextline_two(pvoid)
303 if (pline_two == NULL || pline_two->next == NULL) return FALSE;
304 pline_two = pline_two->next;
309 /*************************************************
310 * Read a line into store *
311 *************************************************/
313 /* A separate procedure exists for each file, for
314 simplicity and efficiency. */
316 void readline_one(pvoid)
319 int c = fgetc(f_one);
320 line *nextline = (line *)bufnext_one;
322 bufnext_one = nextline->text;
323 if (bufnext_one >= buftop_one) moan(2, name_one);
325 nextline->next = NULL;
331 nextline->number = -lines_one;
335 nextline->number = lines_one;
338 if (c == EOF) c = '\n';
342 while (bufnext_one > nextline->text)
343 { if (bufnext_one[-1] == ' ') bufnext_one--; else break; }
344 *(bufnext_one++) = '\n';
345 if (bufnext_one >= buftop_one) moan(2, name_one);
348 if (c == '\t' && !exact)
349 do { *(bufnext_one++) = ' '; count++; } while ((count & 7) != 0);
350 else { *(bufnext_one++) = c; count++; }
351 if (bufnext_one >= buftop_one) moan(2, name_one);
356 if (lastline_one != NULL) lastline_one->next = nextline;
357 lastline_one = nextline;
358 pline_one = nextline;
360 bufnext_one = (char *) (((intptr_t)bufnext_one+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
365 void readline_two(pvoid)
368 int c = fgetc(f_two);
369 line *nextline = (line *)bufnext_two;
371 bufnext_two = nextline->text;
372 if (bufnext_two >= buftop_two) moan(2, name_two);
374 nextline->next = NULL;
380 nextline->number = -lines_two;
384 nextline->number = lines_two;
387 if (c == EOF) c = '\n';
391 while (bufnext_two > nextline->text)
392 { if (bufnext_two[-1] == ' ') bufnext_two--; else break; }
393 *(bufnext_two++) = '\n';
394 if (bufnext_two >= buftop_two) moan(2, name_two);
397 if (c == '\t' && !exact)
398 do { *(bufnext_two++) = ' '; count++; } while ((count & 7) != 0);
399 else { *(bufnext_two++) = c; count++; }
400 if (bufnext_two >= buftop_two) moan(2, name_two);
405 if (lastline_two != NULL) lastline_two->next = nextline;
406 lastline_two = nextline;
407 pline_two = nextline;
409 bufnext_two = (char *) (((intptr_t)bufnext_two+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
414 /**************************************************
415 * Compare two lines *
416 **************************************************/
418 int compare_lines(a, b)
426 if (n1 < 0 && n2 < 0) return TRUE;
427 if (n1 < 0 || n2 < 0) return FALSE;
431 if (*s == '\n') return TRUE;
439 /*************************************************
440 * Re-synchronizing code *
441 *************************************************/
447 line *tline_one = pline_one;
448 line *tline_two = pline_two;
450 if (eof_one || eof_two) matched = FALSE; else
452 for (i = 1; i < sync_count; i++)
454 if (!nextline_one()) readline_one();
455 if (!nextline_two()) readline_two();
456 if (!compare_lines(pline_one, pline_two)) { matched = FALSE; break; }
457 if (eof_one || eof_two) { matched = FALSE; break; }
461 if (matched) write_message(tline_one, tline_two); else
463 pline_one = tline_one;
464 pline_two = tline_two;
472 /*************************************************
473 * Main compare code *
474 *************************************************/
480 /* Big main loop - exit by return or unmatched at eof */
484 /* First minor loop, while in step */
486 while (matched && !eof_one && !eof_two)
488 /* Advance or read next lines */
492 bufnext_one = bufbase_one;
499 bufnext_two = bufbase_two;
504 /* Compare and check for end of file */
506 matched = compare_lines(pline_one, pline_two);
508 } /* End first minor loop */
510 if (matched) return; /* successful end of file */
512 /* There has been a mis-match */
515 rootline_one = pline_one; /* Fail point */
516 rootline_two = pline_two;
518 /* Second minor loop, trying to regain sync */
520 while (!eof_one || !eof_two)
522 /* Advance one and scan all of two */
526 line *zline = pline_two;
527 if (!nextline_one()) readline_one();
528 pline_two = rootline_two;
531 if (compare_lines(pline_one, pline_two))
536 if (pline_two == zline) break;
537 pline_two = pline_two->next;
542 /* Advance two and scan all of one */
546 line *zline = pline_one;
547 if (!nextline_two()) readline_two();
548 pline_one = rootline_one;
551 if (compare_lines(pline_one, pline_two))
556 if (pline_one == zline) break;
557 pline_one = pline_one->next;
562 } /* End second minor loop */
564 } /* End of major loop */
566 write_message(lastline_one, lastline_two);
572 /*************************************************
574 *************************************************/
582 int arg_help = FALSE;
586 /* Scan argument strings */
590 char *arg = argv[argp];
591 char **lv_name = (name_one == NULL)? &name_one:&name_two; /* default for positional */
592 int *lv_value = NULL;
596 { /* keyed argument */
597 if (EqString(arg,"-help") || EqString(arg, "-h"))
598 { arg_help = TRUE; value = FALSE; }
599 else if (EqString(arg, "-id"))
600 { arg_id = TRUE; value = FALSE; }
601 else if (EqString(arg, "-exact"))
602 { exact = TRUE; value = FALSE; }
603 else if (EqString(arg, "-noecho"))
604 { echo = FALSE; value = FALSE; }
605 else if (EqString(arg, "-to")) lv_name = &to_name;
606 else if (EqString(arg, "-sync") || EqString(arg, "-s"))
607 lv_value = &sync_count;
608 else if (EqString(arg, "-buffer")) lv_value = &storesize;
609 else { printf("Unknown keyword %s\n", arg); exit(99); }
611 if (++argp >= argc && value)
612 { printf("Value for keyword %s missing\n", arg); exit(99); }
615 /* Deal with keys that take values */
619 if (lv_value == &sync_count || lv_value == &storesize)
623 char *argval = argv[argp++];
625 while ((ch = argval[i++]) != 0)
627 if ('0' <= ch && ch <= '9') *lv_value = 10*(*lv_value) + ch - '0'; else
629 printf("Number expected after \"%s\" but \"%s\" read\n",
636 else if (*lv_name != NULL)
638 printf("Keyword expected but \"%s\" read", arg);
639 printf(" - use \"cmp -h\" for help\n");
642 else *lv_name = argv[argp++];
646 /* Deal with help and id */
648 if (arg_id && !arg_help)
650 printf("PH's CMP v%d\n", version);
660 /* Deal with file names */
662 if (name_one == NULL || name_two == NULL) moan(3, "");
666 f_out = fopen(to_name, "w");
667 if (f_out == NULL) moan(1, to_name);
670 /* Further general initialization */
672 if (storesize < minstore) storesize = defaultstore;
673 f_one = fopen(name_one, "r");
674 if (f_one == NULL) moan(1, name_one);
675 f_two = fopen(name_two, "r");
676 if (f_two == NULL) moan(1, name_two);
678 bufbase_one = (char *)malloc(storesize);
679 buftop_one = bufbase_one + storesize;
680 bufbase_two = (char *)malloc(storesize);
681 buftop_two = bufbase_two + storesize;
689 if (return_code == 0)
690 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
693 if (echo) rule('=', 15);
694 fprintf(f_out, "%d difference", return_code);
695 if (return_code != 1) fprintf(f_out, "s");
696 fprintf(f_out, " found.\n");
699 fprintf(f_out, "\"%s\" contains %d line", name_one, lines_one);
700 if (lines_one != 1) fprintf(f_out, "s");
703 fprintf(f_out, "; \"%s\" contains %d line", name_two, lines_two);
704 if (lines_two != 1) fprintf(f_out, "s");
705 fprintf(f_out, ".\n");
713 if (f_out != stdout) fclose(f_out);
718 /* End of PH-Compare. */