Bring cvs-1.12.9 into the CVS repository
[dragonfly.git] / contrib / cvs-1.12.9 / diff / diff.c
1 /* GNU DIFF entry routine.
2    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
3
4 This file is part of GNU DIFF.
5
6 GNU DIFF is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU DIFF is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 */
17
18 /* GNU DIFF was written by Mike Haertel, David Hayes,
19    Richard Stallman, Len Tower, and Paul Eggert.  */
20
21 #define GDIFF_MAIN
22 #include "diff.h"
23 #include <signal.h>
24 #include "getopt.h"
25
26 #ifdef HAVE_FNMATCH
27 # include <fnmatch.h> /* This is supposed to be available on Posix systems */
28 #else /* HAVE_FNMATCH */
29 # include "fnmatch.h" /* Our substitute */
30 #endif /* HAVE_FNMATCH */
31
32 #ifndef DEFAULT_WIDTH
33 #define DEFAULT_WIDTH 130
34 #endif
35
36 #ifndef GUTTER_WIDTH_MINIMUM
37 #define GUTTER_WIDTH_MINIMUM 3
38 #endif
39
40 /* diff.c has a real initialize_main function. */
41 #ifdef initialize_main
42 #undef initialize_main
43 #endif
44
45 static char const *filetype PARAMS((struct stat const *));
46 static char *option_list PARAMS((char **, int));
47 static int add_exclude_file PARAMS((char const *));
48 static int ck_atoi PARAMS((char const *, int *));
49 static int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
50 static int specify_format PARAMS((char **, char *));
51 static void add_exclude PARAMS((char const *));
52 static void add_regexp PARAMS((struct regexp_list **, char const *));
53 static void specify_style PARAMS((enum output_style));
54 static int try_help PARAMS((char const *));
55 static void check_output PARAMS((FILE *));
56 static void usage PARAMS((void));
57 static void initialize_main PARAMS((int *, char ***));
58
59 /* Nonzero for -r: if comparing two directories,
60    compare their common subdirectories recursively.  */
61
62 static int recursive;
63
64 /* For debugging: don't do discard_confusing_lines.  */
65
66 int no_discards;
67
68 #if HAVE_SETMODE
69 /* I/O mode: nonzero only if using binary input/output.  */
70 static int binary_I_O;
71 #endif
72
73 /* Return a string containing the command options with which diff was invoked.
74    Spaces appear between what were separate ARGV-elements.
75    There is a space at the beginning but none at the end.
76    If there were no options, the result is an empty string.
77
78    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
79    the length of that vector.  */
80
81 static char *
82 option_list (optionvec, count)
83      char **optionvec;  /* Was `vector', but that collides on Alliant.  */
84      int count;
85 {
86   int i;
87   size_t length = 0;
88   char *result;
89
90   for (i = 0; i < count; i++)
91     length += strlen (optionvec[i]) + 1;
92
93   result = xmalloc (length + 1);
94   result[0] = 0;
95
96   for (i = 0; i < count; i++)
97     {
98       strcat (result, " ");
99       strcat (result, optionvec[i]);
100     }
101
102   return result;
103 }
104 \f
105 /* Convert STR to a positive integer, storing the result in *OUT.
106    If STR is not a valid integer, return -1 (otherwise 0). */
107 static int
108 ck_atoi (str, out)
109      char const *str;
110      int *out;
111 {
112   char const *p;
113   for (p = str; *p; p++)
114     if (*p < '0' || *p > '9')
115       return -1;
116
117   *out = atoi (optarg);
118   return 0;
119 }
120 \f
121 /* Keep track of excluded file name patterns.  */
122
123 static char const **exclude;
124 static int exclude_alloc, exclude_count;
125
126 int
127 excluded_filename (f)
128      char const *f;
129 {
130   int i;
131   for (i = 0;  i < exclude_count;  i++)
132     if (fnmatch (exclude[i], f, 0) == 0)
133       return 1;
134   return 0;
135 }
136
137 static void
138 add_exclude (pattern)
139      char const *pattern;
140 {
141   if (exclude_alloc <= exclude_count)
142     exclude = (char const **)
143               (exclude_alloc == 0
144                ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
145                : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
146
147   exclude[exclude_count++] = pattern;
148 }
149
150 static int
151 add_exclude_file (name)
152      char const *name;
153 {
154   struct file_data f;
155   char *p, *q, *lim;
156
157   f.name = optarg;
158   f.desc = (strcmp (optarg, "-") == 0
159             ? STDIN_FILENO
160             : open (optarg, O_RDONLY, 0));
161   if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
162     return -1;
163
164   sip (&f, 1);
165   slurp (&f);
166
167   for (p = f.buffer, lim = p + f.buffered_chars;  p < lim;  p = q)
168     {
169       q = (char *) memchr (p, '\n', lim - p);
170       if (!q)
171         q = lim;
172       *q++ = 0;
173       add_exclude (p);
174     }
175
176   return close (f.desc);
177 }
178 \f
179 /* The numbers 129- that appear in the fourth element of some entries
180    tell the big switch in `diff_run' how to process those options.  */
181
182 static struct option const longopts[] =
183 {
184   {"ignore-blank-lines", 0, 0, 'B'},
185   {"context", 2, 0, 'C'},
186   {"ifdef", 1, 0, 'D'},
187   {"show-function-line", 1, 0, 'F'},
188   {"speed-large-files", 0, 0, 'H'},
189   {"ignore-matching-lines", 1, 0, 'I'},
190   {"label", 1, 0, 'L'},
191   {"file-label", 1, 0, 'L'},    /* An alias, no longer recommended */
192   {"new-file", 0, 0, 'N'},
193   {"entire-new-file", 0, 0, 'N'},       /* An alias, no longer recommended */
194   {"unidirectional-new-file", 0, 0, 'P'},
195   {"starting-file", 1, 0, 'S'},
196   {"initial-tab", 0, 0, 'T'},
197   {"width", 1, 0, 'W'},
198   {"text", 0, 0, 'a'},
199   {"ascii", 0, 0, 'a'},         /* An alias, no longer recommended */
200   {"ignore-space-change", 0, 0, 'b'},
201   {"minimal", 0, 0, 'd'},
202   {"ed", 0, 0, 'e'},
203   {"forward-ed", 0, 0, 'f'},
204   {"ignore-case", 0, 0, 'i'},
205   {"paginate", 0, 0, 'l'},
206   {"print", 0, 0, 'l'},         /* An alias, no longer recommended */
207   {"rcs", 0, 0, 'n'},
208   {"show-c-function", 0, 0, 'p'},
209   {"brief", 0, 0, 'q'},
210   {"recursive", 0, 0, 'r'},
211   {"report-identical-files", 0, 0, 's'},
212   {"expand-tabs", 0, 0, 't'},
213   {"version", 0, 0, 'v'},
214   {"ignore-all-space", 0, 0, 'w'},
215   {"exclude", 1, 0, 'x'},
216   {"exclude-from", 1, 0, 'X'},
217   {"side-by-side", 0, 0, 'y'},
218   {"unified", 2, 0, 'U'},
219   {"left-column", 0, 0, 129},
220   {"suppress-common-lines", 0, 0, 130},
221   {"sdiff-merge-assist", 0, 0, 131},
222   {"old-line-format", 1, 0, 132},
223   {"new-line-format", 1, 0, 133},
224   {"unchanged-line-format", 1, 0, 134},
225   {"line-format", 1, 0, 135},
226   {"old-group-format", 1, 0, 136},
227   {"new-group-format", 1, 0, 137},
228   {"unchanged-group-format", 1, 0, 138},
229   {"changed-group-format", 1, 0, 139},
230   {"horizon-lines", 1, 0, 140},
231   {"help", 0, 0, 141},
232   {"binary", 0, 0, 142},
233   {0, 0, 0, 0}
234 };
235
236
237
238 int
239 diff_run (argc, argv, out, callbacks_arg)
240      int argc;
241      char *argv[];
242      const char *out;
243      const struct diff_callbacks *callbacks_arg;
244 {
245   int val;
246   int c;
247   int prev = -1;
248   int width = DEFAULT_WIDTH;
249   int show_c_function = 0;
250   int optind_old;
251   int opened_file = 0;
252
253   callbacks = callbacks_arg;
254
255   /* Do our initializations.  */
256   initialize_main (&argc, &argv);
257   optind_old = optind;
258   optind = 0;
259
260   /* Set the jump buffer, so that diff may abort execution without
261      terminating the process. */
262   val = setjmp (diff_abort_buf);
263   if (val != 0)
264     {
265       optind = optind_old;
266       if (opened_file)
267         fclose (outfile);
268       return val;
269     }
270
271   /* Decode the options.  */
272   while ((c = getopt_long (argc, argv,
273                            "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
274                            longopts, 0)) != EOF)
275     {
276       switch (c)
277         {
278           /* All digits combine in decimal to specify the context-size.  */
279         case '1':
280         case '2':
281         case '3':
282         case '4':
283         case '5':
284         case '6':
285         case '7':
286         case '8':
287         case '9':
288         case '0':
289           if (context == -1)
290             context = 0;
291           /* If a context length has already been specified,
292              more digits allowed only if they follow right after the others.
293              Reject two separate runs of digits, or digits after -C.  */
294           else if (prev < '0' || prev > '9')
295             fatal ("context length specified twice");
296
297           context = context * 10 + c - '0';
298           break;
299
300         case 'a':
301           /* Treat all files as text files; never treat as binary.  */
302           always_text_flag = 1;
303           break;
304
305         case 'b':
306           /* Ignore changes in amount of white space.  */
307           ignore_space_change_flag = 1;
308           ignore_some_changes = 1;
309           ignore_some_line_changes = 1;
310           break;
311
312         case 'B':
313           /* Ignore changes affecting only blank lines.  */
314           ignore_blank_lines_flag = 1;
315           ignore_some_changes = 1;
316           break;
317
318         case 'C':               /* +context[=lines] */
319         case 'U':               /* +unified[=lines] */
320           if (optarg)
321             {
322               if (context >= 0)
323                 fatal ("context length specified twice");
324
325               if (ck_atoi (optarg, &context))
326                 fatal ("invalid context length argument");
327             }
328
329           /* Falls through.  */
330         case 'c':
331           /* Make context-style output.  */
332           specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
333           break;
334
335         case 'd':
336           /* Don't discard lines.  This makes things slower (sometimes much
337              slower) but will find a guaranteed minimal set of changes.  */
338           no_discards = 1;
339           break;
340
341         case 'D':
342           /* Make merged #ifdef output.  */
343           specify_style (OUTPUT_IFDEF);
344           {
345             int i, err = 0;
346             static char const C_ifdef_group_formats[] =
347               "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
348             char *b = xmalloc (sizeof (C_ifdef_group_formats)
349                                + 7 * strlen(optarg) - 14 /* 7*"%s" */
350                                - 8 /* 5*"%%" + 3*"%c" */);
351             sprintf (b, C_ifdef_group_formats,
352                      optarg, optarg, 0,
353                      optarg, optarg, 0, 0,
354                      optarg, optarg, optarg);
355             for (i = 0; i < 4; i++)
356               {
357                 err |= specify_format (&group_format[i], b);
358                 b += strlen (b) + 1;
359               }
360             if (err)
361               diff_error ("conflicting #ifdef formats", 0, 0);
362           }
363           break;
364
365         case 'e':
366           /* Make output that is a valid `ed' script.  */
367           specify_style (OUTPUT_ED);
368           break;
369
370         case 'f':
371           /* Make output that looks vaguely like an `ed' script
372              but has changes in the order they appear in the file.  */
373           specify_style (OUTPUT_FORWARD_ED);
374           break;
375
376         case 'F':
377           /* Show, for each set of changes, the previous line that
378              matches the specified regexp.  Currently affects only
379              context-style output.  */
380           add_regexp (&function_regexp_list, optarg);
381           break;
382
383         case 'h':
384           /* Split the files into chunks of around 1500 lines
385              for faster processing.  Usually does not change the result.
386
387              This currently has no effect.  */
388           break;
389
390         case 'H':
391           /* Turn on heuristics that speed processing of large files
392              with a small density of changes.  */
393           heuristic = 1;
394           break;
395
396         case 'i':
397           /* Ignore changes in case.  */
398           ignore_case_flag = 1;
399           ignore_some_changes = 1;
400           ignore_some_line_changes = 1;
401           break;
402
403         case 'I':
404           /* Ignore changes affecting only lines that match the
405              specified regexp.  */
406           add_regexp (&ignore_regexp_list, optarg);
407           ignore_some_changes = 1;
408           break;
409
410         case 'l':
411           /* Pass the output through `pr' to paginate it.  */
412           paginate_flag = 1;
413 #if !defined(SIGCHLD) && defined(SIGCLD)
414 #define SIGCHLD SIGCLD
415 #endif
416 #ifdef SIGCHLD
417           /* Pagination requires forking and waiting, and
418              System V fork+wait does not work if SIGCHLD is ignored.  */
419           signal (SIGCHLD, SIG_DFL);
420 #endif
421           break;
422
423         case 'L':
424           /* Specify file labels for `-c' output headers.  */
425           if (!file_label[0])
426             file_label[0] = optarg;
427           else if (!file_label[1])
428             file_label[1] = optarg;
429           else
430             fatal ("too many file label options");
431           break;
432
433         case 'n':
434           /* Output RCS-style diffs, like `-f' except that each command
435              specifies the number of lines affected.  */
436           specify_style (OUTPUT_RCS);
437           break;
438
439         case 'N':
440           /* When comparing directories, if a file appears only in one
441              directory, treat it as present but empty in the other.  */
442           entire_new_file_flag = 1;
443           break;
444
445         case 'p':
446           /* Make context-style output and show name of last C function.  */
447           show_c_function = 1;
448           add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
449           break;
450
451         case 'P':
452           /* When comparing directories, if a file appears only in
453              the second directory of the two,
454              treat it as present but empty in the other.  */
455           unidirectional_new_file_flag = 1;
456           break;
457
458         case 'q':
459           no_details_flag = 1;
460           break;
461
462         case 'r':
463           /* When comparing directories,
464              recursively compare any subdirectories found.  */
465           recursive = 1;
466           break;
467
468         case 's':
469           /* Print a message if the files are the same.  */
470           print_file_same_flag = 1;
471           break;
472
473         case 'S':
474           /* When comparing directories, start with the specified
475              file name.  This is used for resuming an aborted comparison.  */
476           dir_start_file = optarg;
477           break;
478
479         case 't':
480           /* Expand tabs to spaces in the output so that it preserves
481              the alignment of the input files.  */
482           tab_expand_flag = 1;
483           break;
484
485         case 'T':
486           /* Use a tab in the output, rather than a space, before the
487              text of an input line, so as to keep the proper alignment
488              in the input line without changing the characters in it.  */
489           tab_align_flag = 1;
490           break;
491
492         case 'u':
493           /* Output the context diff in unidiff format.  */
494           specify_style (OUTPUT_UNIFIED);
495           break;
496
497         case 'v':
498           if (callbacks && callbacks->write_stdout)
499             {
500               (*callbacks->write_stdout) ("diff - GNU diffutils version ");
501               (*callbacks->write_stdout) (diff_version_string);
502               (*callbacks->write_stdout) ("\n");
503             }
504           else
505             printf ("diff - GNU diffutils version %s\n", diff_version_string);
506           return 0;
507
508         case 'w':
509           /* Ignore horizontal white space when comparing lines.  */
510           ignore_all_space_flag = 1;
511           ignore_some_changes = 1;
512           ignore_some_line_changes = 1;
513           break;
514
515         case 'x':
516           add_exclude (optarg);
517           break;
518
519         case 'X':
520           if (add_exclude_file (optarg) != 0)
521             pfatal_with_name (optarg);
522           break;
523
524         case 'y':
525           /* Use side-by-side (sdiff-style) columnar output. */
526           specify_style (OUTPUT_SDIFF);
527           break;
528
529         case 'W':
530           /* Set the line width for OUTPUT_SDIFF.  */
531           if (ck_atoi (optarg, &width) || width <= 0)
532             fatal ("column width must be a positive integer");
533           break;
534
535         case 129:
536           sdiff_left_only = 1;
537           break;
538
539         case 130:
540           sdiff_skip_common_lines = 1;
541           break;
542
543         case 131:
544           /* sdiff-style columns output. */
545           specify_style (OUTPUT_SDIFF);
546           sdiff_help_sdiff = 1;
547           break;
548
549         case 132:
550         case 133:
551         case 134:
552           specify_style (OUTPUT_IFDEF);
553           if (specify_format (&line_format[c - 132], optarg) != 0)
554             diff_error ("conflicting line format", 0, 0);
555           break;
556
557         case 135:
558           specify_style (OUTPUT_IFDEF);
559           {
560             int i, err = 0;
561             for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
562               err |= specify_format (&line_format[i], optarg);
563             if (err)
564               diff_error ("conflicting line format", 0, 0);
565           }
566           break;
567
568         case 136:
569         case 137:
570         case 138:
571         case 139:
572           specify_style (OUTPUT_IFDEF);
573           if (specify_format (&group_format[c - 136], optarg) != 0)
574             diff_error ("conflicting group format", 0, 0);
575           break;
576
577         case 140:
578           if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0)
579             fatal ("horizon must be a nonnegative integer");
580           break;
581
582         case 141:
583           usage ();
584           if (! callbacks || ! callbacks->write_stdout)
585             check_output (stdout);
586           return 0;
587
588         case 142:
589           /* Use binary I/O when reading and writing data.
590              On Posix hosts, this has no effect.  */
591 #if HAVE_SETMODE
592           binary_I_O = 1;
593 #  if 0
594           /* Because this code is leftover from pre-library days,
595              there is no way to set stdout back to the default mode
596              when we are done.  As it turns out, I think the only
597              parts of CVS that pass out == NULL, and thus cause diff
598              to write to stdout, are "cvs diff" and "cvs rdiff".  So
599              I'm not going to worry about this too much yet.  */
600           setmode (STDOUT_FILENO, O_BINARY);
601 #  else
602           if (out == NULL)
603             error (0, 0, "warning: did not set stdout to binary mode");
604 #  endif
605 #endif
606           break;
607
608         default:
609           return try_help (0);
610         }
611       prev = c;
612     }
613
614   if (argc - optind != 2)
615     return try_help (argc - optind < 2 ? "missing operand" : "extra operand");
616
617   {
618     /*
619      *  We maximize first the half line width, and then the gutter width,
620      *  according to the following constraints:
621      *  1.  Two half lines plus a gutter must fit in a line.
622      *  2.  If the half line width is nonzero:
623      *      a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
624      *      b.  If tabs are not expanded to spaces,
625      *          a half line plus a gutter is an integral number of tabs,
626      *          so that tabs in the right column line up.
627      */
628     int t = tab_expand_flag ? 1 : TAB_WIDTH;
629     int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t)  *  t;
630     sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)),
631     sdiff_column2_offset = sdiff_half_width ? off : width;
632   }
633
634   if (show_c_function && output_style != OUTPUT_UNIFIED)
635     specify_style (OUTPUT_CONTEXT);
636
637   if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
638     context = 0;
639   else if (context == -1)
640     /* Default amount of context for -c.  */
641     context = 3;
642
643   if (output_style == OUTPUT_IFDEF)
644     {
645       /* Format arrays are char *, not char const *,
646          because integer formats are temporarily modified.
647          But it is safe to assign a constant like "%=" to a format array,
648          since "%=" does not format any integers.  */
649       int i;
650       for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
651         if (!line_format[i])
652           line_format[i] = "%l\n";
653       if (!group_format[OLD])
654         group_format[OLD]
655           = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<";
656       if (!group_format[NEW])
657         group_format[NEW]
658           = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>";
659       if (!group_format[UNCHANGED])
660         group_format[UNCHANGED] = "%=";
661       if (!group_format[CHANGED])
662         group_format[CHANGED] = concat (group_format[OLD],
663                                         group_format[NEW], "");
664     }
665
666   no_diff_means_no_output =
667     (output_style == OUTPUT_IFDEF ?
668       (!*group_format[UNCHANGED]
669        || (strcmp (group_format[UNCHANGED], "%=") == 0
670            && !*line_format[UNCHANGED]))
671      : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1);
672
673   switch_string = option_list (argv + 1, optind - 1);
674
675   if (callbacks && callbacks->write_output)
676     {
677       if (out != NULL)
678         {
679           diff_error ("write callback with output file", 0, 0);
680           return 2;
681         }
682     }
683   else
684     {
685       if (out == NULL)
686         outfile = stdout;
687       else
688         {
689 #if HAVE_SETMODE
690           /* A diff which is full of ^Z and such isn't going to work
691              very well in text mode.  */
692           if (binary_I_O)
693             outfile = fopen (out, "wb");
694           else
695 #endif
696             outfile = fopen (out, "w");
697           if (outfile == NULL)
698             {
699               perror_with_name ("could not open output file");
700               return 2;
701             }
702           opened_file = 1;
703         }
704     }
705
706   val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
707
708   /* Print any messages that were saved up for last.  */
709   print_message_queue ();
710
711   free (switch_string);
712
713   optind = optind_old;
714
715   if (! callbacks || ! callbacks->write_output)
716     check_output (outfile);
717
718   if (opened_file)
719     if (fclose (outfile) != 0)
720         perror_with_name ("close error on output file");
721
722   return val;
723 }
724
725 /* Add the compiled form of regexp PATTERN to REGLIST.  */
726
727 static void
728 add_regexp (reglist, pattern)
729      struct regexp_list **reglist;
730      char const *pattern;
731 {
732   struct regexp_list *r;
733   char const *m;
734
735   r = (struct regexp_list *) xmalloc (sizeof (*r));
736   bzero (r, sizeof (*r));
737   r->buf.fastmap = xmalloc (256);
738   m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
739   if (m != 0)
740     diff_error ("%s: %s", pattern, m);
741
742   /* Add to the start of the list, since it's easier than the end.  */
743   r->next = *reglist;
744   *reglist = r;
745 }
746
747 static int
748 try_help (reason)
749      char const *reason;
750 {
751   if (reason)
752     diff_error ("%s", reason, 0);
753   diff_error ("Try `%s --help' for more information.", diff_program_name, 0);
754   return 2;
755 }
756
757 static void
758 check_output (file)
759     FILE *file;
760 {
761   if (ferror (file) || fflush (file) != 0)
762     fatal ("write error");
763 }
764
765 static char const * const option_help[] = {
766 "-i  --ignore-case  Consider upper- and lower-case to be the same.",
767 "-w  --ignore-all-space  Ignore all white space.",
768 "-b  --ignore-space-change  Ignore changes in the amount of white space.",
769 "-B  --ignore-blank-lines  Ignore changes whose lines are all blank.",
770 "-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.",
771 #if HAVE_SETMODE
772 "--binary  Read and write data in binary mode.",
773 #endif
774 "-a  --text  Treat all files as text.\n",
775 "-c  -C NUM  --context[=NUM]  Output NUM (default 2) lines of copied context.",
776 "-u  -U NUM  --unified[=NUM]  Output NUM (default 2) lines of unified context.",
777 "  -NUM  Use NUM context lines.",
778 "  -L LABEL  --label LABEL  Use LABEL instead of file name.",
779 "  -p  --show-c-function  Show which C function each change is in.",
780 "  -F RE  --show-function-line=RE  Show the most recent line matching RE.",
781 "-q  --brief  Output only whether files differ.",
782 "-e  --ed  Output an ed script.",
783 "-n  --rcs  Output an RCS format diff.",
784 "-y  --side-by-side  Output in two columns.",
785 "  -W NUM  --width=NUM  Output at most NUM (default 130) characters per line.",
786 "  --left-column  Output only the left column of common lines.",
787 "  --suppress-common-lines  Do not output common lines.",
788 "-DNAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs.",
789 "--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT.",
790 "--line-format=LFMT  Similar, but format all input lines with LFMT.",
791 "--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT.",
792 "  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'.",
793 "  GFMT may contain:",
794 "    %<  lines from FILE1",
795 "    %>  lines from FILE2",
796 "    %=  lines common to FILE1 and FILE2",
797 "    %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER",
798 "      LETTERs are as follows for new group, lower case for old group:",
799 "        F  first line number",
800 "        L  last line number",
801 "        N  number of lines = L-F+1",
802 "        E  F-1",
803 "        M  L+1",
804 "  LFMT may contain:",
805 "    %L  contents of line",
806 "    %l  contents of line, excluding any trailing newline",
807 "    %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number",
808 "  Either GFMT or LFMT may contain:",
809 "    %%  %",
810 "    %c'C'  the single character C",
811 "    %c'\\OOO'  the character with octal code OOO\n",
812 "-l  --paginate  Pass the output through `pr' to paginate it.",
813 "-t  --expand-tabs  Expand tabs to spaces in output.",
814 "-T  --initial-tab  Make tabs line up by prepending a tab.\n",
815 "-r  --recursive  Recursively compare any subdirectories found.",
816 "-N  --new-file  Treat absent files as empty.",
817 "-P  --unidirectional-new-file  Treat absent first files as empty.",
818 "-s  --report-identical-files  Report when two files are the same.",
819 "-x PAT  --exclude=PAT  Exclude files that match PAT.",
820 "-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE.",
821 "-S FILE  --starting-file=FILE  Start with FILE when comparing directories.\n",
822 "--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix.",
823 "-d  --minimal  Try hard to find a smaller set of changes.",
824 "-H  --speed-large-files  Assume large files and many scattered small changes.\n",
825 "-v  --version  Output version info.",
826 "--help  Output this help.",
827 0
828 };
829
830 static void
831 usage ()
832 {
833   char const * const *p;
834
835   if (callbacks && callbacks->write_stdout)
836     {
837       (*callbacks->write_stdout) ("Usage: ");
838       (*callbacks->write_stdout) (diff_program_name);
839       (*callbacks->write_stdout) (" [OPTION]... FILE1 FILE2\n\n");
840       for (p = option_help;  *p;  p++)
841         {
842           (*callbacks->write_stdout) ("  ");
843           (*callbacks->write_stdout) (*p);
844           (*callbacks->write_stdout) ("\n");
845         }
846       (*callbacks->write_stdout)
847         ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
848     }
849   else
850     {
851       printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", diff_program_name);
852       for (p = option_help;  *p;  p++)
853         printf ("  %s\n", *p);
854       printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
855     }
856 }
857
858 static int
859 specify_format (var, value)
860      char **var;
861      char *value;
862 {
863   int err = *var ? strcmp (*var, value) : 0;
864   *var = value;
865   return err;
866 }
867
868 static void
869 specify_style (style)
870      enum output_style style;
871 {
872   if (output_style != OUTPUT_NORMAL
873       && output_style != style)
874     diff_error ("conflicting specifications of output style", 0, 0);
875   output_style = style;
876 }
877 \f
878 static char const *
879 filetype (st)
880      struct stat const *st;
881 {
882   /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats.
883      To keep diagnostics grammatical, the returned string must start
884      with a consonant.  */
885
886   if (S_ISREG (st->st_mode))
887     {
888       if (st->st_size == 0)
889         return "regular empty file";
890       /* Posix.2 section 5.14.2 seems to suggest that we must read the file
891          and guess whether it's C, Fortran, etc., but this is somewhat useless
892          and doesn't reflect historical practice.  We're allowed to guess
893          wrong, so we don't bother to read the file.  */
894       return "regular file";
895     }
896   if (S_ISDIR (st->st_mode)) return "directory";
897
898   /* other Posix.1 file types */
899 #ifdef S_ISBLK
900   if (S_ISBLK (st->st_mode)) return "block special file";
901 #endif
902 #ifdef S_ISCHR
903   if (S_ISCHR (st->st_mode)) return "character special file";
904 #endif
905 #ifdef S_ISFIFO
906   if (S_ISFIFO (st->st_mode)) return "fifo";
907 #endif
908
909   /* other Posix.1b file types */
910 #ifdef S_TYPEISMQ
911   if (S_TYPEISMQ (st)) return "message queue";
912 #endif
913 #ifdef S_TYPEISSEM
914   if (S_TYPEISSEM (st)) return "semaphore";
915 #endif
916 #ifdef S_TYPEISSHM
917   if (S_TYPEISSHM (st)) return "shared memory object";
918 #endif
919
920   /* other popular file types */
921   /* S_ISLNK is impossible with `fstat' and `stat'.  */
922 #ifdef S_ISSOCK
923   if (S_ISSOCK (st->st_mode)) return "socket";
924 #endif
925
926   return "weird file";
927 }
928 \f
929 /* Compare two files (or dirs) with specified names
930    DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
931    (if DIR0 is 0, then the name is just NAME0, etc.)
932    This is self-contained; it opens the files and closes them.
933
934    Value is 0 if files are the same, 1 if different,
935    2 if there is a problem opening them.  */
936
937 static int
938 compare_files (dir0, name0, dir1, name1, depth)
939      char const *dir0, *dir1;
940      char const *name0, *name1;
941      int depth;
942 {
943   struct file_data inf[2];
944   register int i;
945   int val;
946   int same_files;
947   int failed = 0;
948   char *free0 = 0, *free1 = 0;
949
950   /* If this is directory comparison, perhaps we have a file
951      that exists only in one of the directories.
952      If so, just print a message to that effect.  */
953
954   if (! ((name0 != 0 && name1 != 0)
955          || (unidirectional_new_file_flag && name1 != 0)
956          || entire_new_file_flag))
957     {
958       char const *name = name0 == 0 ? name1 : name0;
959       char const *dir = name0 == 0 ? dir1 : dir0;
960       message ("Only in %s: %s\n", dir, name);
961       /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
962       return 1;
963     }
964
965   bzero (inf, sizeof (inf));
966
967   /* Mark any nonexistent file with -1 in the desc field.  */
968   /* Mark unopened files (e.g. directories) with -2. */
969
970   inf[0].desc = name0 == 0 ? -1 : -2;
971   inf[1].desc = name1 == 0 ? -1 : -2;
972
973   /* Now record the full name of each file, including nonexistent ones.  */
974
975   if (name0 == 0)
976     name0 = name1;
977   if (name1 == 0)
978     name1 = name0;
979
980   inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
981   inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
982
983   /* Stat the files.  Record whether they are directories.  */
984
985   for (i = 0; i <= 1; i++)
986     {
987       if (inf[i].desc != -1)
988         {
989           int stat_result;
990
991           if (i && filename_cmp (inf[i].name, inf[0].name) == 0)
992             {
993               inf[i].stat = inf[0].stat;
994               stat_result = 0;
995             }
996           else if (strcmp (inf[i].name, "-") == 0)
997             {
998               inf[i].desc = STDIN_FILENO;
999               stat_result = fstat (STDIN_FILENO, &inf[i].stat);
1000               if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode))
1001                 {
1002                   off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
1003                   if (pos == -1)
1004                     stat_result = -1;
1005                   else
1006                     {
1007                       if (pos <= inf[i].stat.st_size)
1008                         inf[i].stat.st_size -= pos;
1009                       else
1010                         inf[i].stat.st_size = 0;
1011                       /* Posix.2 4.17.6.1.4 requires current time for stdin.  */
1012                       time (&inf[i].stat.st_mtime);
1013                     }
1014                 }
1015             }
1016           else
1017             stat_result = stat (inf[i].name, &inf[i].stat);
1018
1019           if (stat_result != 0)
1020             {
1021               perror_with_name (inf[i].name);
1022               failed = 1;
1023             }
1024           else
1025             {
1026               inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
1027               if (inf[1 - i].desc == -1)
1028                 {
1029                   inf[1 - i].dir_p = inf[i].dir_p;
1030                   inf[1 - i].stat.st_mode = inf[i].stat.st_mode;
1031                 }
1032             }
1033         }
1034     }
1035
1036   if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p)
1037     {
1038       /* If one is a directory, and it was specified in the command line,
1039          use the file in that dir with the other file's basename.  */
1040
1041       int fnm_arg = inf[0].dir_p;
1042       int dir_arg = 1 - fnm_arg;
1043       char const *fnm = inf[fnm_arg].name;
1044       char const *dir = inf[dir_arg].name;
1045       char const *p = filename_lastdirchar (fnm);
1046       char const *filename = inf[dir_arg].name
1047         = dir_file_pathname (dir, p ? p + 1 : fnm);
1048
1049       if (strcmp (fnm, "-") == 0)
1050         fatal ("can't compare - to a directory");
1051
1052       if (stat (filename, &inf[dir_arg].stat) != 0)
1053         {
1054           perror_with_name (filename);
1055           failed = 1;
1056         }
1057       else
1058         inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
1059     }
1060
1061   if (failed)
1062     {
1063
1064       /* If either file should exist but does not, return 2.  */
1065
1066       val = 2;
1067
1068     }
1069   else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1
1070                          && 0 < same_file (&inf[0].stat, &inf[1].stat))
1071            && no_diff_means_no_output)
1072     {
1073       /* The two named files are actually the same physical file.
1074          We know they are identical without actually reading them.  */
1075
1076       val = 0;
1077     }
1078   else if (inf[0].dir_p & inf[1].dir_p)
1079     {
1080       if (output_style == OUTPUT_IFDEF)
1081         fatal ("-D option not supported with directories");
1082
1083       /* If both are directories, compare the files in them.  */
1084
1085       if (depth > 0 && !recursive)
1086         {
1087           /* But don't compare dir contents one level down
1088              unless -r was specified.  */
1089           message ("Common subdirectories: %s and %s\n",
1090                    inf[0].name, inf[1].name);
1091           val = 0;
1092         }
1093       else
1094         {
1095           val = diff_dirs (inf, compare_files, depth);
1096         }
1097
1098     }
1099   else if ((inf[0].dir_p | inf[1].dir_p)
1100            || (depth > 0
1101                && (! S_ISREG (inf[0].stat.st_mode)
1102                    || ! S_ISREG (inf[1].stat.st_mode))))
1103     {
1104       /* Perhaps we have a subdirectory that exists only in one directory.
1105          If so, just print a message to that effect.  */
1106
1107       if (inf[0].desc == -1 || inf[1].desc == -1)
1108         {
1109           if ((inf[0].dir_p | inf[1].dir_p)
1110               && recursive
1111               && (entire_new_file_flag
1112                   || (unidirectional_new_file_flag && inf[0].desc == -1)))
1113             val = diff_dirs (inf, compare_files, depth);
1114           else
1115             {
1116               char const *dir = (inf[0].desc == -1) ? dir1 : dir0;
1117               /* See Posix.2 section 4.17.6.1.1 for this format.  */
1118               message ("Only in %s: %s\n", dir, name0);
1119               val = 1;
1120             }
1121         }
1122       else
1123         {
1124           /* We have two files that are not to be compared.  */
1125
1126           /* See Posix.2 section 4.17.6.1.1 for this format.  */
1127           message5 ("File %s is a %s while file %s is a %s\n",
1128                     inf[0].name, filetype (&inf[0].stat),
1129                     inf[1].name, filetype (&inf[1].stat));
1130
1131           /* This is a difference.  */
1132           val = 1;
1133         }
1134     }
1135   else if ((no_details_flag & ~ignore_some_changes)
1136            && inf[0].stat.st_size != inf[1].stat.st_size
1137            && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode))
1138            && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode)))
1139     {
1140       message ("Files %s and %s differ\n", inf[0].name, inf[1].name);
1141       val = 1;
1142     }
1143   else
1144     {
1145       /* Both exist and neither is a directory.  */
1146
1147       /* Open the files and record their descriptors.  */
1148
1149       if (inf[0].desc == -2)
1150         if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
1151           {
1152             perror_with_name (inf[0].name);
1153             failed = 1;
1154           }
1155       if (inf[1].desc == -2)
1156         {
1157           if (same_files)
1158             inf[1].desc = inf[0].desc;
1159           else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
1160             {
1161               perror_with_name (inf[1].name);
1162               failed = 1;
1163             }
1164         }
1165
1166 #if HAVE_SETMODE
1167       if (binary_I_O)
1168         for (i = 0; i <= 1; i++)
1169           if (0 <= inf[i].desc)
1170             setmode (inf[i].desc, O_BINARY);
1171 #endif
1172
1173       /* Compare the files, if no error was found.  */
1174
1175       val = failed ? 2 : diff_2_files (inf, depth);
1176
1177       /* Close the file descriptors.  */
1178
1179       if (inf[0].desc >= 0 && close (inf[0].desc) != 0)
1180         {
1181           perror_with_name (inf[0].name);
1182           val = 2;
1183         }
1184       if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc
1185           && close (inf[1].desc) != 0)
1186         {
1187           perror_with_name (inf[1].name);
1188           val = 2;
1189         }
1190     }
1191
1192   /* Now the comparison has been done, if no error prevented it,
1193      and VAL is the value this function will return.  */
1194
1195   if (val == 0 && !inf[0].dir_p)
1196     {
1197       if (print_file_same_flag)
1198         message ("Files %s and %s are identical\n",
1199                  inf[0].name, inf[1].name);
1200     }
1201   else
1202     flush_output ();
1203
1204   if (free0)
1205     free (free0);
1206   if (free1)
1207     free (free1);
1208
1209   return val;
1210 }
1211
1212 /* Initialize status variables and flag variables used in libdiff,
1213    to permit repeated calls to diff_run. */
1214
1215 static void
1216 initialize_main (argcp, argvp)
1217     int *argcp;
1218     char ***argvp;
1219 {
1220   /* These variables really must be reset each time diff_run is called. */
1221   output_style = OUTPUT_NORMAL;
1222   context = -1;
1223   file_label[0] = NULL;
1224   file_label[1] = NULL;
1225   diff_program_name = (*argvp)[0];
1226   outfile = NULL;
1227
1228   /* Reset these also, just for safety's sake. (If one invocation turns
1229      on ignore_case_flag, it must be turned off before diff_run is called
1230      again.  But it is possible to make many diffs before encountering
1231      such a problem. */
1232   recursive = 0;
1233   no_discards = 0;
1234 #if HAVE_SETMODE
1235   binary_I_O = 0;
1236 #endif
1237   no_diff_means_no_output = 0;
1238   always_text_flag = 0;
1239   horizon_lines = 0;
1240   ignore_space_change_flag = 0;
1241   ignore_all_space_flag = 0;
1242   ignore_blank_lines_flag = 0;
1243   ignore_some_line_changes = 0;
1244   ignore_some_changes = 0;
1245   ignore_case_flag = 0;
1246   function_regexp_list = NULL;
1247   ignore_regexp_list = NULL;
1248   no_details_flag = 0;
1249   print_file_same_flag = 0;
1250   tab_align_flag = 0;
1251   tab_expand_flag = 0;
1252   dir_start_file = NULL;
1253   entire_new_file_flag = 0;
1254   unidirectional_new_file_flag = 0;
1255   paginate_flag = 0;
1256   bzero (group_format, sizeof (group_format));
1257   bzero (line_format, sizeof (line_format));
1258   sdiff_help_sdiff = 0;
1259   sdiff_left_only = 0;
1260   sdiff_skip_common_lines = 0;
1261   sdiff_half_width = 0;
1262   sdiff_column2_offset = 0;
1263   switch_string = NULL;
1264   heuristic = 0;
1265   bzero (files, sizeof (files));
1266 }