Testsuite: bounce_message_file and warn_message_file
[exim.git] / test / src / cf.c
1 /************************************************
2 * PH-Compare *
3 ************************************************/
4
5 /* A program to compare two files line by line.
6
7 History:
8
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.
12
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.
17
18 Translated 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>
32 #include <stdint.h>
33 #endif
34
35 #ifndef intptr_t
36 # define intptr_t long long int
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 */
44 #define SHOWMAX 20 /* maximum number of diff lines to display */
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
61 typedef struct line {
62 struct line *next;
63 int number;
64 char text[999999];
65 } line;
66
67
68 /* ----- global variables ----- */
69
70 FILE *f_one; /* files */
71 FILE *f_two;
72 FILE *f_out;
73
74 int lines_one = 0; /* line counts */
75 int lines_two = 0;
76 int return_code = 0;
77 int eof_one = FALSE; /* eof flags */
78 int eof_two = FALSE;
79 int exact = FALSE; /* TRUE => no strip spaces */
80 int echo = TRUE; /* TRUE => show mismatched lines */
81 int sync_count = 3; /* resync count */
82 int storesize = defaultstore; /* size of each buffer */
83
84 char *name_one = NULL; /* file names */
85 char *name_two = NULL;
86 char *to_name = NULL;
87
88 char *bufbase_one; /* start buffer */
89 char *bufbase_two;
90 char *bufnext_one; /* next free byte */
91 char *bufnext_two;
92 char *buftop_one; /* end buffer */
93 char *buftop_two;
94
95 line *rootline_one; /* mis-match point */
96 line *rootline_two;
97 line *lastline_one; /* last in store */
98 line *lastline_two;
99 line *pline_one; /* working line */
100 line *pline_two;
101
102
103 /*************************************************
104 * Help Information *
105 *************************************************/
106
107 void givehelp(pvoid)
108 {
109 printf("PH's CMP v%d\n", version);
110 printf("Keywords:\n");
111 printf(" <file> ) files to compare\n");
112 printf(" <file> ) no keywords used\n");
113 printf("-to <file> output destination\n");
114 printf("-exact include trailing spaces & match tabs\n");
115 printf("-noecho don't echo differences (just give line numbers)\n");
116 printf("-s, -sync <n> set re-sync count, default 3\n");
117 printf("-buffer <n> buffer size (for each file) default 100000\n");
118 printf("-id give program version id\n");
119 printf("-h, -help give this help\n");
120 printf("\nExamples:\n");
121 printf("cmp old.f77 new.f77\n");
122 printf("cmp first second -noecho -sync 1\n");
123 printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
124 }
125
126
127
128 /************************************************
129 * Errors -- all serious *
130 ************************************************/
131
132 void moan(code, text)
133 int code;
134 char *text;
135 {
136 fprintf(stderr, "\n** ");
137 switch (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
162 fprintf(stderr, "** CMP abandoned\n");
163 exit(99);
164 }
165
166
167
168 /*************************************************
169 * Write line identification *
170 *************************************************/
171
172 void write_id(n1, n2, c, name, p1, p2)
173 int n1, n2, c;
174 char *name, *p1, *p2;
175 {
176 if (n2 < 0) n2 = -n2;
177 n2 -= 1;
178 fprintf(f_out, "%cine", c);
179 if (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
188 void write_lines(s, t)
189 line *s, *t;
190 {
191 while (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
206 void rule(s, l)
207 int s, l;
208 {
209 while (l-- > 0) fprintf(f_out, "%c", s);
210 fprintf(f_out, "\n");
211 }
212
213
214
215 /*************************************************
216 * Write message on re-sync or eof *
217 *************************************************/
218
219 void write_message(tline_one, tline_two)
220 line *tline_one, *tline_two;
221 {
222 int s1 = rootline_one->number;
223 int t1 = tline_one->number;
224 int s2 = rootline_two->number;
225 int t2 = tline_two->number;
226 if (echo) rule('=', 15);
227
228 if (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
241 else 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
254 else 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);
262 if (-t1-s1 < SHOWMAX+1) write_lines(rootline_one, tline_one);
263 else fprintf(f_out, "... <more than %d lines> ...\n", SHOWMAX);
264 rule('-', 10);
265 if (-t2-s2 < SHOWMAX+1) write_lines(rootline_two, tline_two);
266 else fprintf(f_out, "... <more than %d lines> ...\n", SHOWMAX);
267 }
268 }
269
270 else
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
293 simplicity and efficiency. */
294
295 int nextline_one(pvoid)
296 {
297 if (pline_one == NULL || pline_one->next == NULL) return FALSE;
298 pline_one = pline_one->next;
299 return TRUE;
300 }
301
302 int nextline_two(pvoid)
303 {
304 if (pline_two == NULL || pline_two->next == NULL) return FALSE;
305 pline_two = pline_two->next;
306 return TRUE;
307 }
308
309
310 /*************************************************
311 * Read a line into store *
312 *************************************************/
313
314 /* A separate procedure exists for each file, for
315 simplicity and efficiency. */
316
317 void readline_one(pvoid)
318 {
319 int count = 0;
320 int c = fgetc(f_one);
321 line *nextline = (line *)bufnext_one;
322
323 bufnext_one = nextline->text;
324 if (bufnext_one >= buftop_one) moan(2, name_one);
325
326 nextline->next = NULL;
327
328 lines_one ++;
329 if (c == EOF)
330 {
331 eof_one = TRUE;
332 nextline->number = -lines_one;
333 }
334 else
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
357 if (lastline_one != NULL) lastline_one->next = nextline;
358 lastline_one = nextline;
359 pline_one = nextline;
360
361 bufnext_one = (char *) (((intptr_t)bufnext_one+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
362 }
363
364
365
366 void readline_two(pvoid)
367 {
368 int count = 0;
369 int c = fgetc(f_two);
370 line *nextline = (line *)bufnext_two;
371
372 bufnext_two = nextline->text;
373 if (bufnext_two >= buftop_two) moan(2, name_two);
374
375 nextline->next = NULL;
376
377 lines_two ++;
378 if (c == EOF)
379 {
380 eof_two = TRUE;
381 nextline->number = -lines_two;
382 }
383 else
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
406 if (lastline_two != NULL) lastline_two->next = nextline;
407 lastline_two = nextline;
408 pline_two = nextline;
409
410 bufnext_two = (char *) (((intptr_t)bufnext_two+ sizeof (intptr_t) - 1) & (-(sizeof (intptr_t))));
411 }
412
413
414
415 /**************************************************
416 * Compare two lines *
417 **************************************************/
418
419 int compare_lines(a, b)
420 line *a, *b;
421 {
422 int n1 = a->number;
423 int n2 = b->number;
424 char *s = a->text;
425 char *t = b->text;
426
427 if (n1 < 0 && n2 < 0) return TRUE;
428 if (n1 < 0 || n2 < 0) return FALSE;
429
430 while (*s == *t)
431 {
432 if (*s == '\n') return TRUE;
433 s++; t++;
434 }
435
436 return FALSE;
437 }
438
439
440 /*************************************************
441 * Re-synchronizing code *
442 *************************************************/
443
444 int resync(pvoid)
445 {
446 int i;
447 int matched = TRUE;
448 line *tline_one = pline_one;
449 line *tline_two = pline_two;
450
451 if (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
462 if (matched) write_message(tline_one, tline_two); else
463 {
464 pline_one = tline_one;
465 pline_two = tline_two;
466 }
467
468 return matched;
469 }
470
471
472
473 /*************************************************
474 * Main compare code *
475 *************************************************/
476
477 void compare(pvoid)
478 {
479 int matched = TRUE;
480
481 /* Big main loop - exit by return or unmatched at eof */
482
483 while (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
567 write_message(lastline_one, lastline_two);
568 }
569
570
571
572
573 /*************************************************
574 * Entry Point *
575 *************************************************/
576
577 int main(argc, argv)
578 int argc;
579 char **argv;
580 {
581 int argp = 1;
582 int arg_id = FALSE;
583 int arg_help = FALSE;
584
585 f_out = stdout;
586
587 /* Scan argument strings */
588
589 while (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
649 if (arg_id && !arg_help)
650 {
651 printf("PH's CMP v%d\n", version);
652 exit(0);
653 }
654
655 if (arg_help)
656 {
657 givehelp();
658 exit(0);
659 }
660
661 /* Deal with file names */
662
663 if (name_one == NULL || name_two == NULL) moan(3, "");
664
665 if (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
673 if (storesize < minstore) storesize = defaultstore;
674 f_one = fopen(name_one, "r");
675 if (f_one == NULL) moan(1, name_one);
676 f_two = fopen(name_two, "r");
677 if (f_two == NULL) moan(1, name_two);
678
679 bufbase_one = (char *)malloc(storesize);
680 buftop_one = bufbase_one + storesize;
681 bufbase_two = (char *)malloc(storesize);
682 buftop_two = bufbase_two + storesize;
683
684 /* Do the job */
685
686 compare();
687
688 /* Final messages */
689
690 if (return_code == 0)
691 fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
692 else
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
709 free(bufbase_one);
710 free(bufbase_two);
711
712 fclose(f_one);
713 fclose(f_two);
714 if (f_out != stdout) fclose(f_out);
715
716 return return_code;
717 }
718
719 /* End of PH-Compare. */