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