Debugging: avoid expansion problem with foolish-length list output
[users/heiko/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. */