1 /* $Cambridge: exim/test/src/cf.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */
3 /************************************************
5 ************************************************/
7 /* A program to compare two files line by line.
11 It was originally written in C, but the C under
12 Panos is still a shambles (1986). Translated therefore
13 to BCPL -- this explains some of the odd style.
15 Modified to run on Archimedes, August 1987.
16 Modified to run under MSDOS, March 1989.
17 Modified to run under CINTERP interpreter, July 1989.
18 Modified to run under Unix, October 1989.
20 Translated back into C, March 1990! */
22 /* Copyright (c) 1986, 1987, 1989, 1990, 1994, 2001 by Philip Hazel */
24 /* Previously modified: October 1994*/
25 /* Last modified: September 2001 - a long-lived bug fixed! */
38 # define intptr_t long long int
41 /* ----- parameters ----- */
44 #define defaultstore 100000 /* default recovery buffer size */
45 #define minstore 500 /* minimum recovery buffer size */
47 /* ----- misc defines ----- */
58 #define EqString(s, t) (strcmp(s, t) == 0)
60 /* ----- line structure ----- */
69 /* ----- global variables ----- */
71 FILE *f_one; /* files */
75 int lines_one = 0; /* line counts */
78 int eof_one = FALSE; /* eof flags */
80 int exact = FALSE; /* TRUE => no strip spaces */
81 int echo = TRUE; /* TRUE => show mismatched lines */
82 int sync_count = 3; /* resync count */
83 int storesize = defaultstore; /* size of each buffer */
85 char *name_one = NULL; /* file names */
86 char *name_two = NULL;
89 char *bufbase_one; /* start buffer */
91 char *bufnext_one; /* next free byte */
93 char *buftop_one; /* end buffer */
96 line *rootline_one; /* mis-match point */
98 line *lastline_one; /* last in store */
100 line *pline_one; /* working line */
104 /*************************************************
106 *************************************************/
110 printf("PH's CMP v%d\n", version);
111 printf("Keywords:\n");
112 printf(" <file> ) files to compare\n");
113 printf(" <file> ) no keywords used\n");
114 printf("-to <file> output destination\n");
115 printf("-exact include trailing spaces & match tabs\n");
116 printf("-noecho don't echo differences (just give line numbers)\n");
117 printf("-s, -sync <n> set re-sync count, default 3\n");
118 printf("-buffer <n> buffer size (for each file) default 100000\n");
119 printf("-id give program version id\n");
120 printf("-h, -help give this help\n");
121 printf("\nExamples:\n");
122 printf("cmp old.f77 new.f77\n");
123 printf("cmp first second -noecho -sync 1\n");
124 printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
129 /************************************************
130 * Errors -- all serious *
131 ************************************************/
133 void moan(code, text)
137 fprintf(stderr, "\n** ");
141 fprintf(stderr, "Unable to open file \"%s\"", text);
144 fprintf(stderr, " - ");
147 else fprintf(stderr, "\n");
151 fprintf(stderr, "Buffer overflow for file \"%s\"\n", text);
155 fprintf(stderr, "Two file names must be given\n");
159 fprintf(stderr, "Unknown error %d\n", code);
163 fprintf(stderr, "** CMP abandoned\n");
169 /*************************************************
170 * Write line identification *
171 *************************************************/
173 void write_id(n1, n2, c, name, p1, p2)
175 char *name, *p1, *p2;
177 if (n2 < 0) n2 = -n2;
179 fprintf(f_out, "%cine", c);
180 if (n1 == n2) fprintf(f_out, " %d of \"%s\"%s", n1, name, p1);
181 else fprintf(f_out, "s %d-%d of \"%s\"%s", n1, n2, name, p2);
185 /*************************************************
186 * Write sequence of lines *
187 *************************************************/
189 void write_lines(s, t)
195 while (*p != '\n') fputc(*p++, f_out);
203 /*************************************************
204 * Write separator rule *
205 *************************************************/
210 while (l-- > 0) fprintf(f_out, "%c", s);
211 fprintf(f_out, "\n");
216 /*************************************************
217 * Write message on re-sync or eof *
218 *************************************************/
220 void write_message(tline_one, tline_two)
221 line *tline_one, *tline_two;
223 int s1 = rootline_one->number;
224 int t1 = tline_one->number;
225 int s2 = rootline_two->number;
226 int t2 = tline_two->number;
227 if (echo) rule('=', 15);
231 write_id(s2, t2, 'L', name_two, " occurs ", " occur ");
232 if (s1 < 0) fprintf(f_out, "at the end");
233 else fprintf(f_out, "before line %d", s1);
234 fprintf(f_out, " of \"%s\".\n", name_one);
238 write_lines(rootline_two, tline_two);
244 write_id(s1, t1, 'L', name_one, " occurs ", " occur ");
245 if (s2 < 0) fprintf(f_out, "at the end");
246 else fprintf(f_out, "before line %d", s2);
247 fprintf(f_out, " of \"%s\".\n", name_two);
251 write_lines(rootline_one, tline_one);
255 else if (t1 < 0 && t2 < 0)
257 fprintf(f_out, "From line %d of \"%s\" and line %d of \"%s\" ",
258 rootline_one->number, name_one, rootline_two->number, name_two);
259 fprintf(f_out, "the files are different.\n");
263 if (-t1-s1 < 21) write_lines(rootline_one, tline_one);
264 else fprintf(f_out, "... <more than 20 lines> ...\n");
266 if (-t2-s2 < 21) write_lines(rootline_two, tline_two);
267 else fprintf(f_out, "... <more than 20 lines> ...\n");
273 write_id(s1, t1, 'L', name_one, " does ", " do ");
274 fprintf(f_out, "not match ");
275 write_id(s2, t2, 'l', name_two, ".\n", ".\n");
279 write_lines(rootline_one, tline_one);
281 write_lines(rootline_two, tline_two);
289 /*************************************************
290 * Advance to next line in store *
291 *************************************************/
293 /* A separate procedure exists for each file, for
294 simplicity and efficiency. */
296 int nextline_one(pvoid)
298 if (pline_one == NULL || pline_one->next == NULL) return FALSE;
299 pline_one = pline_one->next;
303 int nextline_two(pvoid)
305 if (pline_two == NULL || pline_two->next == NULL) return FALSE;
306 pline_two = pline_two->next;
311 /*************************************************
312 * Read a line into store *
313 *************************************************/
315 /* A separate procedure exists for each file, for
316 simplicity and efficiency. */
318 void readline_one(pvoid)
321 int c = fgetc(f_one);
322 line *nextline = (line *)bufnext_one;
324 bufnext_one = nextline->text;
325 if (bufnext_one >= buftop_one) moan(2, name_one);
327 nextline->next = NULL;
333 nextline->number = -lines_one;
337 nextline->number = lines_one;
340 if (c == EOF) c = '\n';
344 while (bufnext_one > nextline->text)
345 { if (bufnext_one[-1] == ' ') bufnext_one--; else break; }
346 *(bufnext_one++) = '\n';
347 if (bufnext_one >= buftop_one) moan(2, name_one);
350 if (c == '\t' && !exact)
351 do { *(bufnext_one++) = ' '; count++; } while ((count & 7) != 0);
352 else { *(bufnext_one++) = c; count++; }
353 if (bufnext_one >= buftop_one) moan(2, name_one);
358 if (lastline_one != NULL) lastline_one->next = nextline;
359 lastline_one = nextline;
360 pline_one = nextline;
362 bufnext_one = (char *) (((intptr_t)bufnext_one+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
367 void readline_two(pvoid)
370 int c = fgetc(f_two);
371 line *nextline = (line *)bufnext_two;
373 bufnext_two = nextline->text;
374 if (bufnext_two >= buftop_two) moan(2, name_two);
376 nextline->next = NULL;
382 nextline->number = -lines_two;
386 nextline->number = lines_two;
389 if (c == EOF) c = '\n';
393 while (bufnext_two > nextline->text)
394 { if (bufnext_two[-1] == ' ') bufnext_two--; else break; }
395 *(bufnext_two++) = '\n';
396 if (bufnext_two >= buftop_two) moan(2, name_two);
399 if (c == '\t' && !exact)
400 do { *(bufnext_two++) = ' '; count++; } while ((count & 7) != 0);
401 else { *(bufnext_two++) = c; count++; }
402 if (bufnext_two >= buftop_two) moan(2, name_two);
407 if (lastline_two != NULL) lastline_two->next = nextline;
408 lastline_two = nextline;
409 pline_two = nextline;
411 bufnext_two = (char *) (((intptr_t)bufnext_two+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
416 /**************************************************
417 * Compare two lines *
418 **************************************************/
420 int compare_lines(a, b)
428 if (n1 < 0 && n2 < 0) return TRUE;
429 if (n1 < 0 || n2 < 0) return FALSE;
433 if (*s == '\n') return TRUE;
441 /*************************************************
442 * Re-synchronizing code *
443 *************************************************/
449 line *tline_one = pline_one;
450 line *tline_two = pline_two;
452 if (eof_one || eof_two) matched = FALSE; else
454 for (i = 1; i < sync_count; i++)
456 if (!nextline_one()) readline_one();
457 if (!nextline_two()) readline_two();
458 if (!compare_lines(pline_one, pline_two)) { matched = FALSE; break; }
459 if (eof_one || eof_two) { matched = FALSE; break; }
463 if (matched) write_message(tline_one, tline_two); else
465 pline_one = tline_one;
466 pline_two = tline_two;
474 /*************************************************
475 * Main compare code *
476 *************************************************/
482 /* Big main loop - exit by return or unmatched at eof */
486 /* First minor loop, while in step */
488 while (matched && !eof_one && !eof_two)
490 /* Advance or read next lines */
494 bufnext_one = bufbase_one;
501 bufnext_two = bufbase_two;
506 /* Compare and check for end of file */
508 matched = compare_lines(pline_one, pline_two);
510 } /* End first minor loop */
512 if (matched) return; /* successful end of file */
514 /* There has been a mis-match */
517 rootline_one = pline_one; /* Fail point */
518 rootline_two = pline_two;
520 /* Second minor loop, trying to regain sync */
522 while (!eof_one || !eof_two)
524 /* Advance one and scan all of two */
528 line *zline = pline_two;
529 if (!nextline_one()) readline_one();
530 pline_two = rootline_two;
533 if (compare_lines(pline_one, pline_two))
538 if (pline_two == zline) break;
539 pline_two = pline_two->next;
544 /* Advance two and scan all of one */
548 line *zline = pline_one;
549 if (!nextline_two()) readline_two();
550 pline_one = rootline_one;
553 if (compare_lines(pline_one, pline_two))
558 if (pline_one == zline) break;
559 pline_one = pline_one->next;
564 } /* End second minor loop */
566 } /* End of major loop */
568 write_message(lastline_one, lastline_two);
574 /*************************************************
576 *************************************************/
584 int arg_help = FALSE;
588 /* Scan argument strings */
592 char *arg = argv[argp];
593 char **lv_name = (name_one == NULL)? &name_one:&name_two; /* default for positional */
594 int *lv_value = NULL;
598 { /* keyed argument */
599 if (EqString(arg,"-help") || EqString(arg, "-h"))
600 { arg_help = TRUE; value = FALSE; }
601 else if (EqString(arg, "-id"))
602 { arg_id = TRUE; value = FALSE; }
603 else if (EqString(arg, "-exact"))
604 { exact = TRUE; value = FALSE; }
605 else if (EqString(arg, "-noecho"))
606 { echo = FALSE; value = FALSE; }
607 else if (EqString(arg, "-to")) lv_name = &to_name;
608 else if (EqString(arg, "-sync") || EqString(arg, "-s"))
609 lv_value = &sync_count;
610 else if (EqString(arg, "-buffer")) lv_value = &storesize;
611 else { printf("Unknown keyword %s\n", arg); exit(99); }
613 if (++argp >= argc && value)
614 { printf("Value for keyword %s missing\n", arg); exit(99); }
617 /* Deal with keys that take values */
621 if (lv_value == &sync_count || lv_value == &storesize)
625 char *argval = argv[argp++];
627 while ((ch = argval[i++]) != 0)
629 if ('0' <= ch && ch <= '9') *lv_value = 10*(*lv_value) + ch - '0'; else
631 printf("Number expected after \"%s\" but \"%s\" read\n",
638 else if (*lv_name != NULL)
640 printf("Keyword expected but \"%s\" read", arg);
641 printf(" - use \"cmp -h\" for help\n");
644 else *lv_name = argv[argp++];
648 /* Deal with help and id */
650 if (arg_id && !arg_help)
652 printf("PH's CMP v%d\n", version);
662 /* Deal with file names */
664 if (name_one == NULL || name_two == NULL) moan(3, "");
668 f_out = fopen(to_name, "w");
669 if (f_out == NULL) moan(1, to_name);
672 /* Further general initialization */
674 if (storesize < minstore) storesize = defaultstore;
675 f_one = fopen(name_one, "r");
676 if (f_one == NULL) moan(1, name_one);
677 f_two = fopen(name_two, "r");
678 if (f_two == NULL) moan(1, name_two);
680 bufbase_one = (char *)malloc(storesize);
681 buftop_one = bufbase_one + storesize;
682 bufbase_two = (char *)malloc(storesize);
683 buftop_two = bufbase_two + storesize;
691 if (return_code == 0)
692 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
695 if (echo) rule('=', 15);
696 fprintf(f_out, "%d difference", return_code);
697 if (return_code != 1) fprintf(f_out, "s");
698 fprintf(f_out, " found.\n");
701 fprintf(f_out, "\"%s\" contains %d line", name_one, lines_one);
702 if (lines_one != 1) fprintf(f_out, "s");
705 fprintf(f_out, "; \"%s\" contains %d line", name_two, lines_two);
706 if (lines_two != 1) fprintf(f_out, "s");
707 fprintf(f_out, ".\n");
715 if (f_out != stdout) fclose(f_out);
720 /* End of PH-Compare. */