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