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