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