With a few minor exceptions for tests that had additional concerns and tests I don...
[exim.git] / test / src / cf.c
CommitLineData
c55a77db
PH
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
9History:
10
11It was originally written in C, but the C under
12Panos is still a shambles (1986). Translated therefore
13to BCPL -- this explains some of the odd style.
14
15Modified to run on Archimedes, August 1987.
16Modified to run under MSDOS, March 1989.
17Modified to run under CINTERP interpreter, July 1989.
18Modified to run under Unix, October 1989.
19
20Translated 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
57typedef struct line {
58 struct line *next;
59 int number;
60 char text[999999];
61} line;
62
63
64/* ----- global variables ----- */
65
66FILE *f_one; /* files */
67FILE *f_two;
68FILE *f_out;
69
70int lines_one = 0; /* line counts */
71int lines_two = 0;
72int return_code = 0;
73int eof_one = FALSE; /* eof flags */
74int eof_two = FALSE;
75int exact = FALSE; /* TRUE => no strip spaces */
76int echo = TRUE; /* TRUE => show mismatched lines */
77int sync_count = 3; /* resync count */
78int storesize = defaultstore; /* size of each buffer */
79
80char *name_one = NULL; /* file names */
81char *name_two = NULL;
82char *to_name = NULL;
83
84char *bufbase_one; /* start buffer */
85char *bufbase_two;
86char *bufnext_one; /* next free byte */
87char *bufnext_two;
88char *buftop_one; /* end buffer */
89char *buftop_two;
90
91line *rootline_one; /* mis-match point */
92line *rootline_two;
93line *lastline_one; /* last in store */
94line *lastline_two;
95line *pline_one; /* working line */
96line *pline_two;
97
98
99/*************************************************
100* Help Information *
101*************************************************/
102
103void givehelp(pvoid)
104{
105printf("PH's CMP v%d\n", version);
106printf("Keywords:\n");
107printf(" <file> ) files to compare\n");
108printf(" <file> ) no keywords used\n");
109printf("-to <file> output destination\n");
110printf("-exact include trailing spaces & match tabs\n");
111printf("-noecho don't echo differences (just give line numbers)\n");
112printf("-s, -sync <n> set re-sync count, default 3\n");
113printf("-buffer <n> buffer size (for each file) default 100000\n");
114printf("-id give program version id\n");
115printf("-h, -help give this help\n");
116printf("\nExamples:\n");
117printf("cmp old.f77 new.f77\n");
118printf("cmp first second -noecho -sync 1\n");
119printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
120}
121
122
123
124/************************************************
125* Errors -- all serious *
126************************************************/
127
128void moan(code, text)
129int code;
130char *text;
131{
132fprintf(stderr, "\n** ");
133switch (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
158fprintf(stderr, "** CMP abandoned\n");
159exit(99);
160}
161
162
163
164/*************************************************
165* Write line identification *
166*************************************************/
167
168void write_id(n1, n2, c, name, p1, p2)
169int n1, n2, c;
170char *name, *p1, *p2;
171{
172if (n2 < 0) n2 = -n2;
173n2 -= 1;
174fprintf(f_out, "%cine", c);
175if (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
184void write_lines(s, t)
185line *s, *t;
186{
187while (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
202void rule(s, l)
203int s, l;
204{
205while (l-- > 0) fprintf(f_out, "%c", s);
206fprintf(f_out, "\n");
207}
208
209
210
211/*************************************************
212* Write message on re-sync or eof *
213*************************************************/
214
215void write_message(tline_one, tline_two)
216line *tline_one, *tline_two;
217{
218int s1 = rootline_one->number;
219int t1 = tline_one->number;
220int s2 = rootline_two->number;
221int t2 = tline_two->number;
222if (echo) rule('=', 15);
223
224if (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
237else 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
250else 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
266else
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
289simplicity and efficiency. */
290
291int nextline_one(pvoid)
292{
293if (pline_one == NULL || pline_one->next == NULL) return FALSE;
294pline_one = pline_one->next;
295return TRUE;
296}
297
298int nextline_two(pvoid)
299{
300if (pline_two == NULL || pline_two->next == NULL) return FALSE;
301pline_two = pline_two->next;
302return TRUE;
303}
304
305
306/*************************************************
307* Read a line into store *
308*************************************************/
309
310/* A separate procedure exists for each file, for
311simplicity and efficiency. */
312
313void readline_one(pvoid)
314{
315int count = 0;
316int c = fgetc(f_one);
317line *nextline = (line *)bufnext_one;
318
319bufnext_one = nextline->text;
320if (bufnext_one >= buftop_one) moan(2, name_one);
321
322nextline->next = NULL;
323
324lines_one ++;
325if (c == EOF)
326 {
327 eof_one = TRUE;
328 nextline->number = -lines_one;
329 }
330else
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
353if (lastline_one != NULL) lastline_one->next = nextline;
354lastline_one = nextline;
355pline_one = nextline;
356
357bufnext_one = (char *) (((int)bufnext_one+3) & (-4));
358}
359
360
361
362void readline_two(pvoid)
363{
364int count = 0;
365int c = fgetc(f_two);
366line *nextline = (line *)bufnext_two;
367
368bufnext_two = nextline->text;
369if (bufnext_two >= buftop_two) moan(2, name_two);
370
371nextline->next = NULL;
372
373lines_two ++;
374if (c == EOF)
375 {
376 eof_two = TRUE;
377 nextline->number = -lines_two;
378 }
379else
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
402if (lastline_two != NULL) lastline_two->next = nextline;
403lastline_two = nextline;
404pline_two = nextline;
405
406bufnext_two = (char *) (((int)bufnext_two+3) & (-4));
407}
408
409
410
411/**************************************************
412* Compare two lines *
413**************************************************/
414
415int compare_lines(a, b)
416line *a, *b;
417{
418int n1 = a->number;
419int n2 = b->number;
420char *s = a->text;
421char *t = b->text;
422
423if (n1 < 0 && n2 < 0) return TRUE;
424if (n1 < 0 || n2 < 0) return FALSE;
425
426while (*s == *t)
427 {
428 if (*s == '\n') return TRUE;
429 s++; t++;
430 }
431
432return FALSE;
433}
434
435
436/*************************************************
437* Re-synchronizing code *
438*************************************************/
439
440int resync(pvoid)
441{
442int i;
443int matched = TRUE;
444line *tline_one = pline_one;
445line *tline_two = pline_two;
446
447if (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
458if (matched) write_message(tline_one, tline_two); else
459 {
460 pline_one = tline_one;
461 pline_two = tline_two;
462 }
463
464return matched;
465}
466
467
468
469/*************************************************
470* Main compare code *
471*************************************************/
472
473void compare(pvoid)
474{
475int matched = TRUE;
476
477/* Big main loop - exit by return or unmatched at eof */
478
479while (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
563write_message(lastline_one, lastline_two);
564}
565
566
567
568
569/*************************************************
570* Entry Point *
571*************************************************/
572
573int main(argc, argv)
574int argc;
575char **argv;
576{
577int argp = 1;
578int arg_id = FALSE;
579int arg_help = FALSE;
580
581f_out = stdout;
582
583/* Scan argument strings */
584
585while (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
645if (arg_id && !arg_help)
646 {
647 printf("PH's CMP v%d\n", version);
648 exit(0);
649 }
650
651if (arg_help)
652 {
653 givehelp();
654 exit(0);
655 }
656
657/* Deal with file names */
658
659if (name_one == NULL || name_two == NULL) moan(3, "");
660
661if (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
669if (storesize < minstore) storesize = defaultstore;
670f_one = fopen(name_one, "r");
671if (f_one == NULL) moan(1, name_one);
672f_two = fopen(name_two, "r");
673if (f_two == NULL) moan(1, name_two);
674
675bufbase_one = (char *)malloc(storesize);
676buftop_one = bufbase_one + storesize;
677bufbase_two = (char *)malloc(storesize);
678buftop_two = bufbase_two + storesize;
679
680/* Do the job */
681
682compare();
683
684/* Final messages */
685
686if (return_code == 0)
687 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
688else
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
705free(bufbase_one);
706free(bufbase_two);
707
708fclose(f_one);
709fclose(f_two);
710if (f_out != stdout) fclose(f_out);
711
712return return_code;
713}
714
715/* End of PH-Compare. */