Docs for "G" modifier on numbers in ${if comparisons.
[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. */