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