CVSing the test suite.
[exim.git] / test / src / cf.c
1 /* $Cambridge: exim/test/src/cf.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */
2
3 /************************************************
4 * PH-Compare *
5 ************************************************/
6
7 /* A program to compare two files line by line.
8
9 History:
10
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.
14
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.
19
20 Translated back into C, March 1990! */
21
22 /* Copyright (c) 1986, 1987, 1989, 1990, 1994, 2001 by Philip Hazel */
23
24 /* Previously modified: October 1994*/
25 /* Last modified: September 2001 - a long-lived bug fixed! */
26
27
28 #include <stdio.h>
29 #include <errno.h>
30
31 #ifdef __STDC__
32 #include <string.h>
33 #include <stdlib.h>
34 #endif
35
36 /* ----- parameters ----- */
37
38 #define version 8
39 #define defaultstore 100000 /* default recovery buffer size */
40 #define minstore 500 /* minimum recovery buffer size */
41
42 /* ----- misc defines ----- */
43
44 #define FALSE 0
45 #define TRUE 1
46
47 #ifdef __STDC__
48 #define pvoid void
49 #else
50 #define pvoid
51 #endif
52
53 #define EqString(s, t) (strcmp(s, t) == 0)
54
55 /* ----- line structure ----- */
56
57 typedef struct line {
58 struct line *next;
59 int number;
60 char text[999999];
61 } line;
62
63
64 /* ----- global variables ----- */
65
66 FILE *f_one; /* files */
67 FILE *f_two;
68 FILE *f_out;
69
70 int lines_one = 0; /* line counts */
71 int lines_two = 0;
72 int return_code = 0;
73 int eof_one = FALSE; /* eof flags */
74 int eof_two = FALSE;
75 int exact = FALSE; /* TRUE => no strip spaces */
76 int echo = TRUE; /* TRUE => show mismatched lines */
77 int sync_count = 3; /* resync count */
78 int storesize = defaultstore; /* size of each buffer */
79
80 char *name_one = NULL; /* file names */
81 char *name_two = NULL;
82 char *to_name = NULL;
83
84 char *bufbase_one; /* start buffer */
85 char *bufbase_two;
86 char *bufnext_one; /* next free byte */
87 char *bufnext_two;
88 char *buftop_one; /* end buffer */
89 char *buftop_two;
90
91 line *rootline_one; /* mis-match point */
92 line *rootline_two;
93 line *lastline_one; /* last in store */
94 line *lastline_two;
95 line *pline_one; /* working line */
96 line *pline_two;
97
98
99 /*************************************************
100 * Help Information *
101 *************************************************/
102
103 void givehelp(pvoid)
104 {
105 printf("PH's CMP v%d\n", version);
106 printf("Keywords:\n");
107 printf(" <file> ) files to compare\n");
108 printf(" <file> ) no keywords used\n");
109 printf("-to <file> output destination\n");
110 printf("-exact include trailing spaces & match tabs\n");
111 printf("-noecho don't echo differences (just give line numbers)\n");
112 printf("-s, -sync <n> set re-sync count, default 3\n");
113 printf("-buffer <n> buffer size (for each file) default 100000\n");
114 printf("-id give program version id\n");
115 printf("-h, -help give this help\n");
116 printf("\nExamples:\n");
117 printf("cmp old.f77 new.f77\n");
118 printf("cmp first second -noecho -sync 1\n");
119 printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
120 }
121
122
123
124 /************************************************
125 * Errors -- all serious *
126 ************************************************/
127
128 void moan(code, text)
129 int code;
130 char *text;
131 {
132 fprintf(stderr, "\n** ");
133 switch (code)
134 {
135 case 1:
136 fprintf(stderr, "Unable to open file \"%s\"", text);
137 if (errno)
138 {
139 fprintf(stderr, " - ");
140 perror(NULL);
141 }
142 else fprintf(stderr, "\n");
143 break;
144
145 case 2:
146 fprintf(stderr, "Buffer overflow for file \"%s\"\n", text);
147 break;
148
149 case 3:
150 fprintf(stderr, "Two file names must be given\n");
151 break;
152
153 default:
154 fprintf(stderr, "Unknown error %d\n", code);
155 break;
156 }
157
158 fprintf(stderr, "** CMP abandoned\n");
159 exit(99);
160 }
161
162
163
164 /*************************************************
165 * Write line identification *
166 *************************************************/
167
168 void write_id(n1, n2, c, name, p1, p2)
169 int n1, n2, c;
170 char *name, *p1, *p2;
171 {
172 if (n2 < 0) n2 = -n2;
173 n2 -= 1;
174 fprintf(f_out, "%cine", c);
175 if (n1 == n2) fprintf(f_out, " %d of \"%s\"%s", n1, name, p1);
176 else fprintf(f_out, "s %d-%d of \"%s\"%s", n1, n2, name, p2);
177 }
178
179
180 /*************************************************
181 * Write sequence of lines *
182 *************************************************/
183
184 void write_lines(s, t)
185 line *s, *t;
186 {
187 while (s != t)
188 {
189 char *p = s->text;
190 while (*p != '\n') fputc(*p++, f_out);
191 fputc('\n', f_out);
192 s = s->next;
193 }
194 }
195
196
197
198 /*************************************************
199 * Write separator rule *
200 *************************************************/
201
202 void rule(s, l)
203 int s, l;
204 {
205 while (l-- > 0) fprintf(f_out, "%c", s);
206 fprintf(f_out, "\n");
207 }
208
209
210
211 /*************************************************
212 * Write message on re-sync or eof *
213 *************************************************/
214
215 void write_message(tline_one, tline_two)
216 line *tline_one, *tline_two;
217 {
218 int s1 = rootline_one->number;
219 int t1 = tline_one->number;
220 int s2 = rootline_two->number;
221 int t2 = tline_two->number;
222 if (echo) rule('=', 15);
223
224 if (s1 == t1)
225 {
226 write_id(s2, t2, 'L', name_two, " occurs ", " occur ");
227 if (s1 < 0) fprintf(f_out, "at the end");
228 else fprintf(f_out, "before line %d", s1);
229 fprintf(f_out, " of \"%s\".\n", name_one);
230 if (echo)
231 {
232 rule('-', 10);
233 write_lines(rootline_two, tline_two);
234 }
235 }
236
237 else if (s2 == t2)
238 {
239 write_id(s1, t1, 'L', name_one, " occurs ", " occur ");
240 if (s2 < 0) fprintf(f_out, "at the end");
241 else fprintf(f_out, "before line %d", s2);
242 fprintf(f_out, " of \"%s\".\n", name_two);
243 if (echo)
244 {
245 rule('-', 10);
246 write_lines(rootline_one, tline_one);
247 }
248 }
249
250 else if (t1 < 0 && t2 < 0)
251 {
252 fprintf(f_out, "From line %d of \"%s\" and line %d of \"%s\" ",
253 rootline_one->number, name_one, rootline_two->number, name_two);
254 fprintf(f_out, "the files are different.\n");
255 if (echo)
256 {
257 rule('-', 10);
258 if (-t1-s1 < 21) write_lines(rootline_one, tline_one);
259 else fprintf(f_out, "... <more than 20 lines> ...\n");
260 rule('-', 10);
261 if (-t2-s2 < 21) write_lines(rootline_two, tline_two);
262 else fprintf(f_out, "... <more than 20 lines> ...\n");
263 }
264 }
265
266 else
267 {
268 write_id(s1, t1, 'L', name_one, " does ", " do ");
269 fprintf(f_out, "not match ");
270 write_id(s2, t2, 'l', name_two, ".\n", ".\n");
271 if (echo)
272 {
273 rule('-', 10);
274 write_lines(rootline_one, tline_one);
275 rule('-', 10);
276 write_lines(rootline_two, tline_two);
277 }
278 }
279 }
280
281
282
283
284 /*************************************************
285 * Advance to next line in store *
286 *************************************************/
287
288 /* A separate procedure exists for each file, for
289 simplicity and efficiency. */
290
291 int nextline_one(pvoid)
292 {
293 if (pline_one == NULL || pline_one->next == NULL) return FALSE;
294 pline_one = pline_one->next;
295 return TRUE;
296 }
297
298 int nextline_two(pvoid)
299 {
300 if (pline_two == NULL || pline_two->next == NULL) return FALSE;
301 pline_two = pline_two->next;
302 return TRUE;
303 }
304
305
306 /*************************************************
307 * Read a line into store *
308 *************************************************/
309
310 /* A separate procedure exists for each file, for
311 simplicity and efficiency. */
312
313 void readline_one(pvoid)
314 {
315 int count = 0;
316 int c = fgetc(f_one);
317 line *nextline = (line *)bufnext_one;
318
319 bufnext_one = nextline->text;
320 if (bufnext_one >= buftop_one) moan(2, name_one);
321
322 nextline->next = NULL;
323
324 lines_one ++;
325 if (c == EOF)
326 {
327 eof_one = TRUE;
328 nextline->number = -lines_one;
329 }
330 else
331 {
332 nextline->number = lines_one;
333 for (;;)
334 {
335 if (c == EOF) c = '\n';
336 if (c == '\n')
337 {
338 if (!exact)
339 while (bufnext_one > nextline->text)
340 { if (bufnext_one[-1] == ' ') bufnext_one--; else break; }
341 *(bufnext_one++) = '\n';
342 if (bufnext_one >= buftop_one) moan(2, name_one);
343 break;
344 }
345 if (c == '\t' && !exact)
346 do { *(bufnext_one++) = ' '; count++; } while ((count & 7) != 0);
347 else { *(bufnext_one++) = c; count++; }
348 if (bufnext_one >= buftop_one) moan(2, name_one);
349 c = fgetc(f_one);
350 }
351 }
352
353 if (lastline_one != NULL) lastline_one->next = nextline;
354 lastline_one = nextline;
355 pline_one = nextline;
356
357 bufnext_one = (char *) (((int)bufnext_one+3) & (-4));
358 }
359
360
361
362 void readline_two(pvoid)
363 {
364 int count = 0;
365 int c = fgetc(f_two);
366 line *nextline = (line *)bufnext_two;
367
368 bufnext_two = nextline->text;
369 if (bufnext_two >= buftop_two) moan(2, name_two);
370
371 nextline->next = NULL;
372
373 lines_two ++;
374 if (c == EOF)
375 {
376 eof_two = TRUE;
377 nextline->number = -lines_two;
378 }
379 else
380 {
381 nextline->number = lines_two;
382 for (;;)
383 {
384 if (c == EOF) c = '\n';
385 if (c == '\n')
386 {
387 if (!exact)
388 while (bufnext_two > nextline->text)
389 { if (bufnext_two[-1] == ' ') bufnext_two--; else break; }
390 *(bufnext_two++) = '\n';
391 if (bufnext_two >= buftop_two) moan(2, name_two);
392 break;
393 }
394 if (c == '\t' && !exact)
395 do { *(bufnext_two++) = ' '; count++; } while ((count & 7) != 0);
396 else { *(bufnext_two++) = c; count++; }
397 if (bufnext_two >= buftop_two) moan(2, name_two);
398 c = fgetc(f_two);
399 }
400 }
401
402 if (lastline_two != NULL) lastline_two->next = nextline;
403 lastline_two = nextline;
404 pline_two = nextline;
405
406 bufnext_two = (char *) (((int)bufnext_two+3) & (-4));
407 }
408
409
410
411 /**************************************************
412 * Compare two lines *
413 **************************************************/
414
415 int compare_lines(a, b)
416 line *a, *b;
417 {
418 int n1 = a->number;
419 int n2 = b->number;
420 char *s = a->text;
421 char *t = b->text;
422
423 if (n1 < 0 && n2 < 0) return TRUE;
424 if (n1 < 0 || n2 < 0) return FALSE;
425
426 while (*s == *t)
427 {
428 if (*s == '\n') return TRUE;
429 s++; t++;
430 }
431
432 return FALSE;
433 }
434
435
436 /*************************************************
437 * Re-synchronizing code *
438 *************************************************/
439
440 int resync(pvoid)
441 {
442 int i;
443 int matched = TRUE;
444 line *tline_one = pline_one;
445 line *tline_two = pline_two;
446
447 if (eof_one || eof_two) matched = FALSE; else
448 {
449 for (i = 1; i < sync_count; i++)
450 {
451 if (!nextline_one()) readline_one();
452 if (!nextline_two()) readline_two();
453 if (!compare_lines(pline_one, pline_two)) { matched = FALSE; break; }
454 if (eof_one || eof_two) { matched = FALSE; break; }
455 }
456 }
457
458 if (matched) write_message(tline_one, tline_two); else
459 {
460 pline_one = tline_one;
461 pline_two = tline_two;
462 }
463
464 return matched;
465 }
466
467
468
469 /*************************************************
470 * Main compare code *
471 *************************************************/
472
473 void compare(pvoid)
474 {
475 int matched = TRUE;
476
477 /* Big main loop - exit by return or unmatched at eof */
478
479 while (matched)
480 {
481 /* First minor loop, while in step */
482
483 while (matched && !eof_one && !eof_two)
484 {
485 /* Advance or read next lines */
486
487 if (!nextline_one())
488 {
489 bufnext_one = bufbase_one;
490 lastline_one = NULL;
491 readline_one();
492 }
493
494 if (!nextline_two())
495 {
496 bufnext_two = bufbase_two;
497 lastline_two = NULL;
498 readline_two();
499 }
500
501 /* Compare and check for end of file */
502
503 matched = compare_lines(pline_one, pline_two);
504
505 } /* End first minor loop */
506
507 if (matched) return; /* successful end of file */
508
509 /* There has been a mis-match */
510
511 return_code++;
512 rootline_one = pline_one; /* Fail point */
513 rootline_two = pline_two;
514
515 /* Second minor loop, trying to regain sync */
516
517 while (!eof_one || !eof_two)
518 {
519 /* Advance one and scan all of two */
520
521 if (!eof_one)
522 {
523 line *zline = pline_two;
524 if (!nextline_one()) readline_one();
525 pline_two = rootline_two;
526 for (;;)
527 {
528 if (compare_lines(pline_one, pline_two))
529 {
530 matched = resync();
531 if (matched) break;
532 }
533 if (pline_two == zline) break;
534 pline_two = pline_two->next;
535 }
536 if (matched) break;
537 }
538
539 /* Advance two and scan all of one */
540
541 if (!eof_two)
542 {
543 line *zline = pline_one;
544 if (!nextline_two()) readline_two();
545 pline_one = rootline_one;
546 for (;;)
547 {
548 if (compare_lines(pline_one, pline_two))
549 {
550 matched = resync();
551 if (matched) break;
552 }
553 if (pline_one == zline) break;
554 pline_one = pline_one->next;
555 }
556 if (matched) break;
557 }
558
559 } /* End second minor loop */
560
561 } /* End of major loop */
562
563 write_message(lastline_one, lastline_two);
564 }
565
566
567
568
569 /*************************************************
570 * Entry Point *
571 *************************************************/
572
573 int main(argc, argv)
574 int argc;
575 char **argv;
576 {
577 int argp = 1;
578 int arg_id = FALSE;
579 int arg_help = FALSE;
580
581 f_out = stdout;
582
583 /* Scan argument strings */
584
585 while (argp < argc)
586 {
587 char *arg = argv[argp];
588 char **lv_name = (name_one == NULL)? &name_one:&name_two; /* default for positional */
589 int *lv_value = NULL;
590 int value = TRUE;
591
592 if (arg[0] == '-')
593 { /* keyed argument */
594 if (EqString(arg,"-help") || EqString(arg, "-h"))
595 { arg_help = TRUE; value = FALSE; }
596 else if (EqString(arg, "-id"))
597 { arg_id = TRUE; value = FALSE; }
598 else if (EqString(arg, "-exact"))
599 { exact = TRUE; value = FALSE; }
600 else if (EqString(arg, "-noecho"))
601 { echo = FALSE; value = FALSE; }
602 else if (EqString(arg, "-to")) lv_name = &to_name;
603 else if (EqString(arg, "-sync") || EqString(arg, "-s"))
604 lv_value = &sync_count;
605 else if (EqString(arg, "-buffer")) lv_value = &storesize;
606 else { printf("Unknown keyword %s\n", arg); exit(99); }
607
608 if (++argp >= argc && value)
609 { printf("Value for keyword %s missing\n", arg); exit(99); }
610 }
611
612 /* Deal with keys that take values */
613
614 if (value)
615 {
616 if (lv_value == &sync_count || lv_value == &storesize)
617 {
618 int ch;
619 int i = 0;
620 char *argval = argv[argp++];
621 *lv_value = 0;
622 while ((ch = argval[i++]) != 0)
623 {
624 if ('0' <= ch && ch <= '9') *lv_value = 10*(*lv_value) + ch - '0'; else
625 {
626 printf("Number expected after \"%s\" but \"%s\" read\n",
627 arg, argval);
628 exit(99);
629 }
630 }
631 }
632
633 else if (*lv_name != NULL)
634 {
635 printf("Keyword expected but \"%s\" read", arg);
636 printf(" - use \"cmp -h\" for help\n");
637 exit(99);
638 }
639 else *lv_name = argv[argp++];
640 }
641 }
642
643 /* Deal with help and id */
644
645 if (arg_id && !arg_help)
646 {
647 printf("PH's CMP v%d\n", version);
648 exit(0);
649 }
650
651 if (arg_help)
652 {
653 givehelp();
654 exit(0);
655 }
656
657 /* Deal with file names */
658
659 if (name_one == NULL || name_two == NULL) moan(3, "");
660
661 if (to_name != NULL)
662 {
663 f_out = fopen(to_name, "w");
664 if (f_out == NULL) moan(1, to_name);
665 }
666
667 /* Further general initialization */
668
669 if (storesize < minstore) storesize = defaultstore;
670 f_one = fopen(name_one, "r");
671 if (f_one == NULL) moan(1, name_one);
672 f_two = fopen(name_two, "r");
673 if (f_two == NULL) moan(1, name_two);
674
675 bufbase_one = (char *)malloc(storesize);
676 buftop_one = bufbase_one + storesize;
677 bufbase_two = (char *)malloc(storesize);
678 buftop_two = bufbase_two + storesize;
679
680 /* Do the job */
681
682 compare();
683
684 /* Final messages */
685
686 if (return_code == 0)
687 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
688 else
689 {
690 if (echo) rule('=', 15);
691 fprintf(f_out, "%d difference", return_code);
692 if (return_code != 1) fprintf(f_out, "s");
693 fprintf(f_out, " found.\n");
694
695 lines_one -= 1;
696 fprintf(f_out, "\"%s\" contains %d line", name_one, lines_one);
697 if (lines_one != 1) fprintf(f_out, "s");
698
699 lines_two -= 1;
700 fprintf(f_out, "; \"%s\" contains %d line", name_two, lines_two);
701 if (lines_two != 1) fprintf(f_out, "s");
702 fprintf(f_out, ".\n");
703 }
704
705 free(bufbase_one);
706 free(bufbase_two);
707
708 fclose(f_one);
709 fclose(f_two);
710 if (f_out != stdout) fclose(f_out);
711
712 return return_code;
713 }
714
715 /* End of PH-Compare. */