Remove the version number from the diffutils dir.
[dragonfly.git] / contrib / diffutils / src / diff.c
1 /* diff - compare files line by line
2
3    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002,
4    2004 Free Software Foundation, Inc.
5
6    This file is part of GNU DIFF.
7
8    GNU DIFF is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12
13    GNU DIFF is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16    See the GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with GNU DIFF; see the file COPYING.
20    If not, write to the Free Software Foundation,
21    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22
23 #define GDIFF_MAIN
24 #include "diff.h"
25 #include "paths.h"
26 #include <c-stack.h>
27 #include <dirname.h>
28 #include <error.h>
29 #include <exclude.h>
30 #include <exit.h>
31 #include <exitfail.h>
32 #include <file-type.h>
33 #include <fnmatch.h>
34 #include <getopt.h>
35 #include <hard-locale.h>
36 #include <posixver.h>
37 #include <prepargs.h>
38 #include <quotesys.h>
39 #include <setmode.h>
40 #include <version-etc.h>
41 #include <xalloc.h>
42
43 #ifndef GUTTER_WIDTH_MINIMUM
44 # define GUTTER_WIDTH_MINIMUM 3
45 #endif
46
47 struct regexp_list
48 {
49   char *regexps;        /* chars representing disjunction of the regexps */
50   size_t len;           /* chars used in `regexps' */
51   size_t size;          /* size malloc'ed for `regexps'; 0 if not malloc'ed */
52   bool multiple_regexps;/* Does `regexps' represent a disjunction?  */
53   struct re_pattern_buffer *buf;
54 };
55
56 static int compare_files (struct comparison const *, char const *, char const *);
57 static void add_regexp (struct regexp_list *, char const *);
58 static void summarize_regexp_list (struct regexp_list *);
59 static void specify_style (enum output_style);
60 static void specify_value (char const **, char const *, char const *);
61 static void try_help (char const *, char const *) __attribute__((noreturn));
62 static void check_stdout (void);
63 static void usage (void);
64
65 /* If comparing directories, compare their common subdirectories
66    recursively.  */
67 static bool recursive;
68
69 /* In context diffs, show previous lines that match these regexps.  */
70 static struct regexp_list function_regexp_list;
71
72 /* Ignore changes affecting only lines that match these regexps.  */
73 static struct regexp_list ignore_regexp_list;
74
75 #if HAVE_SETMODE_DOS
76 /* Use binary I/O when reading and writing data (--binary).
77    On POSIX hosts, this has no effect.  */
78 static bool binary;
79 #else
80 enum { binary = true };
81 #endif
82
83 /* When comparing directories, if a file appears only in one
84    directory, treat it as present but empty in the other (-N).
85    Then `patch' would create the file with appropriate contents.  */
86 static bool new_file;
87
88 /* When comparing directories, if a file appears only in the second
89    directory of the two, treat it as present but empty in the other
90    (--unidirectional-new-file).
91    Then `patch' would create the file with appropriate contents.  */
92 static bool unidirectional_new_file;
93
94 /* Report files compared that are the same (-s).
95    Normally nothing is output when that happens.  */
96 static bool report_identical_files;
97
98
99 /* Return a string containing the command options with which diff was invoked.
100    Spaces appear between what were separate ARGV-elements.
101    There is a space at the beginning but none at the end.
102    If there were no options, the result is an empty string.
103
104    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
105    the length of that vector.  */
106
107 static char *
108 option_list (char **optionvec, int count)
109 {
110   int i;
111   size_t size = 1;
112   char *result;
113   char *p;
114
115   for (i = 0; i < count; i++)
116     size += 1 + quote_system_arg ((char *) 0, optionvec[i]);
117
118   p = result = xmalloc (size);
119
120   for (i = 0; i < count; i++)
121     {
122       *p++ = ' ';
123       p += quote_system_arg (p, optionvec[i]);
124     }
125
126   *p = 0;
127   return result;
128 }
129
130
131 /* Return an option value suitable for add_exclude.  */
132
133 static int
134 exclude_options (void)
135 {
136   return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
137 }
138 \f
139 static char const shortopts[] =
140 "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y";
141
142 /* Values for long options that do not have single-letter equivalents.  */
143 enum
144 {
145   BINARY_OPTION = CHAR_MAX + 1,
146   FROM_FILE_OPTION,
147   HELP_OPTION,
148   HORIZON_LINES_OPTION,
149   IGNORE_FILE_NAME_CASE_OPTION,
150   INHIBIT_HUNK_MERGE_OPTION,
151   LEFT_COLUMN_OPTION,
152   LINE_FORMAT_OPTION,
153   NO_IGNORE_FILE_NAME_CASE_OPTION,
154   NORMAL_OPTION,
155   SDIFF_MERGE_ASSIST_OPTION,
156   STRIP_TRAILING_CR_OPTION,
157   SUPPRESS_COMMON_LINES_OPTION,
158   TABSIZE_OPTION,
159   TO_FILE_OPTION,
160
161   /* These options must be in sequence.  */
162   UNCHANGED_LINE_FORMAT_OPTION,
163   OLD_LINE_FORMAT_OPTION,
164   NEW_LINE_FORMAT_OPTION,
165
166   /* These options must be in sequence.  */
167   UNCHANGED_GROUP_FORMAT_OPTION,
168   OLD_GROUP_FORMAT_OPTION,
169   NEW_GROUP_FORMAT_OPTION,
170   CHANGED_GROUP_FORMAT_OPTION
171 };
172
173 static char const group_format_option[][sizeof "--unchanged-group-format"] =
174   {
175     "--unchanged-group-format",
176     "--old-group-format",
177     "--new-group-format",
178     "--changed-group-format"
179   };
180
181 static char const line_format_option[][sizeof "--unchanged-line-format"] =
182   {
183     "--unchanged-line-format",
184     "--old-line-format",
185     "--new-line-format"
186   };
187
188 static struct option const longopts[] =
189 {
190   {"binary", 0, 0, BINARY_OPTION},
191   {"brief", 0, 0, 'q'},
192   {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
193   {"context", 2, 0, 'C'},
194   {"ed", 0, 0, 'e'},
195   {"exclude", 1, 0, 'x'},
196   {"exclude-from", 1, 0, 'X'},
197   {"expand-tabs", 0, 0, 't'},
198   {"forward-ed", 0, 0, 'f'},
199   {"from-file", 1, 0, FROM_FILE_OPTION},
200   {"help", 0, 0, HELP_OPTION},
201   {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
202   {"ifdef", 1, 0, 'D'},
203   {"ignore-all-space", 0, 0, 'w'},
204   {"ignore-blank-lines", 0, 0, 'B'},
205   {"ignore-case", 0, 0, 'i'},
206   {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
207   {"ignore-matching-lines", 1, 0, 'I'},
208   {"ignore-space-change", 0, 0, 'b'},
209   {"ignore-tab-expansion", 0, 0, 'E'},
210   {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
211   {"initial-tab", 0, 0, 'T'},
212   {"label", 1, 0, 'L'},
213   {"left-column", 0, 0, LEFT_COLUMN_OPTION},
214   {"line-format", 1, 0, LINE_FORMAT_OPTION},
215   {"minimal", 0, 0, 'd'},
216   {"new-file", 0, 0, 'N'},
217   {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
218   {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
219   {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
220   {"normal", 0, 0, NORMAL_OPTION},
221   {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
222   {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
223   {"paginate", 0, 0, 'l'},
224   {"rcs", 0, 0, 'n'},
225   {"recursive", 0, 0, 'r'},
226   {"report-identical-files", 0, 0, 's'},
227   {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
228   {"show-c-function", 0, 0, 'p'},
229   {"show-function-line", 1, 0, 'F'},
230   {"side-by-side", 0, 0, 'y'},
231   {"speed-large-files", 0, 0, 'H'},
232   {"starting-file", 1, 0, 'S'},
233   {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
234   {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
235   {"tabsize", 1, 0, TABSIZE_OPTION},
236   {"text", 0, 0, 'a'},
237   {"to-file", 1, 0, TO_FILE_OPTION},
238   {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
239   {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
240   {"unidirectional-new-file", 0, 0, 'P'},
241   {"unified", 2, 0, 'U'},
242   {"version", 0, 0, 'v'},
243   {"width", 1, 0, 'W'},
244   {0, 0, 0, 0}
245 };
246
247 int
248 main (int argc, char **argv)
249 {
250   int exit_status = EXIT_SUCCESS;
251   int c;
252   int i;
253   int prev = -1;
254   lin ocontext = -1;
255   bool explicit_context = false;
256   size_t width = 0;
257   bool show_c_function = false;
258   char const *from_file = 0;
259   char const *to_file = 0;
260   uintmax_t numval;
261   char *numend;
262
263   /* Do our initializations.  */
264   exit_failure = 2;
265   initialize_main (&argc, &argv);
266   program_name = argv[0];
267   setlocale (LC_ALL, "");
268   bindtextdomain (PACKAGE, LOCALEDIR);
269   textdomain (PACKAGE);
270   c_stack_action (0);
271   function_regexp_list.buf = &function_regexp;
272   ignore_regexp_list.buf = &ignore_regexp;
273   re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
274   excluded = new_exclude ();
275
276   /* Decode the options.  */
277
278   while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1)
279     {
280       switch (c)
281         {
282         case 0:
283           break;
284
285         case '0':
286         case '1':
287         case '2':
288         case '3':
289         case '4':
290         case '5':
291         case '6':
292         case '7':
293         case '8':
294         case '9':
295           if (! ISDIGIT (prev))
296             ocontext = c - '0';
297           else if (LIN_MAX / 10 < ocontext
298                    || ((ocontext = 10 * ocontext + c - '0') < 0))
299             ocontext = LIN_MAX;
300           break;
301
302         case 'a':
303           text = true;
304           break;
305
306         case 'b':
307           if (ignore_white_space < IGNORE_SPACE_CHANGE)
308             ignore_white_space = IGNORE_SPACE_CHANGE;
309           break;
310
311         case 'B':
312           ignore_blank_lines = true;
313           break;
314
315         case 'C':
316         case 'U':
317           {
318             if (optarg)
319               {
320                 numval = strtoumax (optarg, &numend, 10);
321                 if (*numend)
322                   try_help ("invalid context length `%s'", optarg);
323                 if (LIN_MAX < numval)
324                   numval = LIN_MAX;
325               }
326             else
327               numval = 3;
328
329             specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
330             if (context < numval)
331               context = numval;
332             explicit_context = true;
333           }
334           break;
335
336         case 'c':
337           specify_style (OUTPUT_CONTEXT);
338           if (context < 3)
339             context = 3;
340           break;
341
342         case 'd':
343           minimal = true;
344           break;
345
346         case 'D':
347           specify_style (OUTPUT_IFDEF);
348           {
349             static char const C_ifdef_group_formats[] =
350               "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
351             char *b = xmalloc (sizeof C_ifdef_group_formats
352                                + 7 * strlen (optarg) - 14 /* 7*"%s" */
353                                - 8 /* 5*"%%" + 3*"%c" */);
354             sprintf (b, C_ifdef_group_formats,
355                      0,
356                      optarg, optarg, 0,
357                      optarg, optarg, 0,
358                      optarg, optarg, optarg);
359             for (i = 0; i < sizeof group_format / sizeof *group_format; i++)
360               {
361                 specify_value (&group_format[i], b, "-D");
362                 b += strlen (b) + 1;
363               }
364           }
365           break;
366
367         case 'e':
368           specify_style (OUTPUT_ED);
369           break;
370
371         case 'E':
372           if (ignore_white_space < IGNORE_TAB_EXPANSION)
373             ignore_white_space = IGNORE_TAB_EXPANSION;
374           break;
375
376         case 'f':
377           specify_style (OUTPUT_FORWARD_ED);
378           break;
379
380         case 'F':
381           add_regexp (&function_regexp_list, optarg);
382           break;
383
384         case 'h':
385           /* Split the files into chunks for faster processing.
386              Usually does not change the result.
387
388              This currently has no effect.  */
389           break;
390
391         case 'H':
392           speed_large_files = true;
393           break;
394
395         case 'i':
396           ignore_case = true;
397           break;
398
399         case 'I':
400           add_regexp (&ignore_regexp_list, optarg);
401           break;
402
403         case 'l':
404           if (!pr_program[0])
405             try_help ("pagination not supported on this host", 0);
406           paginate = true;
407 #ifdef SIGCHLD
408           /* Pagination requires forking and waiting, and
409              System V fork+wait does not work if SIGCHLD is ignored.  */
410           signal (SIGCHLD, SIG_DFL);
411 #endif
412           break;
413
414         case 'L':
415           if (!file_label[0])
416             file_label[0] = optarg;
417           else if (!file_label[1])
418             file_label[1] = optarg;
419           else
420             fatal ("too many file label options");
421           break;
422
423         case 'n':
424           specify_style (OUTPUT_RCS);
425           break;
426
427         case 'N':
428           new_file = true;
429           break;
430
431         case 'p':
432           show_c_function = true;
433           add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
434           break;
435
436         case 'P':
437           unidirectional_new_file = true;
438           break;
439
440         case 'q':
441           brief = true;
442           break;
443
444         case 'r':
445           recursive = true;
446           break;
447
448         case 's':
449           report_identical_files = true;
450           break;
451
452         case 'S':
453           specify_value (&starting_file, optarg, "-S");
454           break;
455
456         case 't':
457           expand_tabs = true;
458           break;
459
460         case 'T':
461           initial_tab = true;
462           break;
463
464         case 'u':
465           specify_style (OUTPUT_UNIFIED);
466           if (context < 3)
467             context = 3;
468           break;
469
470         case 'v':
471           version_etc (stdout, "diff", PACKAGE_NAME, PACKAGE_VERSION,
472                        "Paul Eggert", "Mike Haertel", "David Hayes",
473                        "Richard Stallman", "Len Tower", (char *) 0);
474           check_stdout ();
475           return EXIT_SUCCESS;
476
477         case 'w':
478           ignore_white_space = IGNORE_ALL_SPACE;
479           break;
480
481         case 'x':
482           add_exclude (excluded, optarg, exclude_options ());
483           break;
484
485         case 'X':
486           if (add_exclude_file (add_exclude, excluded, optarg,
487                                 exclude_options (), '\n'))
488             pfatal_with_name (optarg);
489           break;
490
491         case 'y':
492           specify_style (OUTPUT_SDIFF);
493           break;
494
495         case 'W':
496           numval = strtoumax (optarg, &numend, 10);
497           if (! (0 < numval && numval <= SIZE_MAX) || *numend)
498             try_help ("invalid width `%s'", optarg);
499           if (width != numval)
500             {
501               if (width)
502                 fatal ("conflicting width options");
503               width = numval;
504             }
505           break;
506
507         case BINARY_OPTION:
508 #if HAVE_SETMODE_DOS
509           binary = true;
510           set_binary_mode (STDOUT_FILENO, true);
511 #endif
512           break;
513
514         case FROM_FILE_OPTION:
515           specify_value (&from_file, optarg, "--from-file");
516           break;
517
518         case HELP_OPTION:
519           usage ();
520           check_stdout ();
521           return EXIT_SUCCESS;
522
523         case HORIZON_LINES_OPTION:
524           numval = strtoumax (optarg, &numend, 10);
525           if (*numend)
526             try_help ("invalid horizon length `%s'", optarg);
527           horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
528           break;
529
530         case IGNORE_FILE_NAME_CASE_OPTION:
531           ignore_file_name_case = true;
532           break;
533
534         case INHIBIT_HUNK_MERGE_OPTION:
535           /* This option is obsolete, but accept it for backward
536              compatibility.  */
537           break;
538
539         case LEFT_COLUMN_OPTION:
540           left_column = true;
541           break;
542
543         case LINE_FORMAT_OPTION:
544           specify_style (OUTPUT_IFDEF);
545           for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
546             specify_value (&line_format[i], optarg, "--line-format");
547           break;
548
549         case NO_IGNORE_FILE_NAME_CASE_OPTION:
550           ignore_file_name_case = false;
551           break;
552
553         case NORMAL_OPTION:
554           specify_style (OUTPUT_NORMAL);
555           break;
556
557         case SDIFF_MERGE_ASSIST_OPTION:
558           specify_style (OUTPUT_SDIFF);
559           sdiff_merge_assist = true;
560           break;
561
562         case STRIP_TRAILING_CR_OPTION:
563           strip_trailing_cr = true;
564           break;
565
566         case SUPPRESS_COMMON_LINES_OPTION:
567           suppress_common_lines = true;
568           break;
569
570         case TABSIZE_OPTION:
571           numval = strtoumax (optarg, &numend, 10);
572           if (! (0 < numval && numval <= SIZE_MAX) || *numend)
573             try_help ("invalid tabsize `%s'", optarg);
574           if (tabsize != numval)
575             {
576               if (tabsize)
577                 fatal ("conflicting tabsize options");
578               tabsize = numval;
579             }
580           break;
581
582         case TO_FILE_OPTION:
583           specify_value (&to_file, optarg, "--to-file");
584           break;
585
586         case UNCHANGED_LINE_FORMAT_OPTION:
587         case OLD_LINE_FORMAT_OPTION:
588         case NEW_LINE_FORMAT_OPTION:
589           specify_style (OUTPUT_IFDEF);
590           c -= UNCHANGED_LINE_FORMAT_OPTION;
591           specify_value (&line_format[c], optarg, line_format_option[c]);
592           break;
593
594         case UNCHANGED_GROUP_FORMAT_OPTION:
595         case OLD_GROUP_FORMAT_OPTION:
596         case NEW_GROUP_FORMAT_OPTION:
597         case CHANGED_GROUP_FORMAT_OPTION:
598           specify_style (OUTPUT_IFDEF);
599           c -= UNCHANGED_GROUP_FORMAT_OPTION;
600           specify_value (&group_format[c], optarg, group_format_option[c]);
601           break;
602
603         default:
604           try_help (0, 0);
605         }
606       prev = c;
607     }
608
609   if (output_style == OUTPUT_UNSPECIFIED)
610     {
611       if (show_c_function)
612         {
613           specify_style (OUTPUT_CONTEXT);
614           if (ocontext < 0)
615             context = 3;
616         }
617       else
618         specify_style (OUTPUT_NORMAL);
619     }
620
621   if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
622     {
623 #ifdef ST_MTIM_NSEC
624       time_format = "%Y-%m-%d %H:%M:%S.%N %z";
625 #else
626       time_format = "%Y-%m-%d %H:%M:%S %z";
627 #endif
628     }
629   else
630     {
631       /* See POSIX 1003.1-2001 for this format.  */
632       time_format = "%a %b %e %T %Y";
633     }
634
635   if (0 <= ocontext)
636     {
637       bool modern_usage = 200112 <= posix2_version ();
638
639       if ((output_style == OUTPUT_CONTEXT
640            || output_style == OUTPUT_UNIFIED)
641           && (context < ocontext
642               || (ocontext < context && ! explicit_context)))
643         {
644           if (modern_usage)
645             {
646               error (0, 0,
647                      _("`-%ld' option is obsolete; use `-%c %ld'"),
648                      (long int) ocontext,
649                      output_style == OUTPUT_CONTEXT ? 'C' : 'U',
650                      (long int) ocontext);
651               try_help (0, 0);
652             }
653           context = ocontext;
654         }
655       else
656         {
657           if (modern_usage)
658             {
659               error (0, 0, _("`-%ld' option is obsolete; omit it"),
660                      (long int) ocontext);
661               try_help (0, 0);
662             }
663         }
664     }
665
666   if (! tabsize)
667     tabsize = 8;
668   if (! width)
669     width = 130;
670
671   {
672     /* Maximize first the half line width, and then the gutter width,
673        according to the following constraints:
674
675         1.  Two half lines plus a gutter must fit in a line.
676         2.  If the half line width is nonzero:
677             a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
678             b.  If tabs are not expanded to spaces,
679                 a half line plus a gutter is an integral number of tabs,
680                 so that tabs in the right column line up.  */
681
682     intmax_t t = expand_tabs ? 1 : tabsize;
683     intmax_t w = width;
684     intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t)  *  t;
685     sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
686     sdiff_column2_offset = sdiff_half_width ? off : w;
687   }
688
689   /* Make the horizon at least as large as the context, so that
690      shift_boundaries has more freedom to shift the first and last hunks.  */
691   if (horizon_lines < context)
692     horizon_lines = context;
693
694   summarize_regexp_list (&function_regexp_list);
695   summarize_regexp_list (&ignore_regexp_list);
696
697   if (output_style == OUTPUT_IFDEF)
698     {
699       for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
700         if (!line_format[i])
701           line_format[i] = "%l\n";
702       if (!group_format[OLD])
703         group_format[OLD]
704           = group_format[CHANGED] ? group_format[CHANGED] : "%<";
705       if (!group_format[NEW])
706         group_format[NEW]
707           = group_format[CHANGED] ? group_format[CHANGED] : "%>";
708       if (!group_format[UNCHANGED])
709         group_format[UNCHANGED] = "%=";
710       if (!group_format[CHANGED])
711         group_format[CHANGED] = concat (group_format[OLD],
712                                         group_format[NEW], "");
713     }
714
715   no_diff_means_no_output =
716     (output_style == OUTPUT_IFDEF ?
717       (!*group_format[UNCHANGED]
718        || (strcmp (group_format[UNCHANGED], "%=") == 0
719            && !*line_format[UNCHANGED]))
720      : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
721
722   files_can_be_treated_as_binary =
723     (brief & binary
724      & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
725           | (ignore_regexp_list.regexps || ignore_white_space)));
726
727   switch_string = option_list (argv + 1, optind - 1);
728
729   if (from_file)
730     {
731       if (to_file)
732         fatal ("--from-file and --to-file both specified");
733       else
734         for (; optind < argc; optind++)
735           {
736             int status = compare_files ((struct comparison *) 0,
737                                         from_file, argv[optind]);
738             if (exit_status < status)
739               exit_status = status;
740           }
741     }
742   else
743     {
744       if (to_file)
745         for (; optind < argc; optind++)
746           {
747             int status = compare_files ((struct comparison *) 0,
748                                         argv[optind], to_file);
749             if (exit_status < status)
750               exit_status = status;
751           }
752       else
753         {
754           if (argc - optind != 2)
755             {
756               if (argc - optind < 2)
757                 try_help ("missing operand after `%s'", argv[argc - 1]);
758               else
759                 try_help ("extra operand `%s'", argv[optind + 2]);
760             }
761
762           exit_status = compare_files ((struct comparison *) 0,
763                                        argv[optind], argv[optind + 1]);
764         }
765     }
766
767   /* Print any messages that were saved up for last.  */
768   print_message_queue ();
769
770   check_stdout ();
771   exit (exit_status);
772   return exit_status;
773 }
774
775 /* Append to REGLIST the regexp PATTERN.  */
776
777 static void
778 add_regexp (struct regexp_list *reglist, char const *pattern)
779 {
780   size_t patlen = strlen (pattern);
781   char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
782
783   if (m != 0)
784     error (0, 0, "%s: %s", pattern, m);
785   else
786     {
787       char *regexps = reglist->regexps;
788       size_t len = reglist->len;
789       bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
790       size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
791       size_t size = reglist->size;
792
793       if (size <= newlen)
794         {
795           if (!size)
796             size = 1;
797
798           do size *= 2;
799           while (size <= newlen);
800
801           reglist->size = size;
802           reglist->regexps = regexps = xrealloc (regexps, size);
803         }
804       if (multiple_regexps)
805         {
806           regexps[len++] = '\\';
807           regexps[len++] = '|';
808         }
809       memcpy (regexps + len, pattern, patlen + 1);
810     }
811 }
812
813 /* Ensure that REGLIST represents the disjunction of its regexps.
814    This is done here, rather than earlier, to avoid O(N^2) behavior.  */
815
816 static void
817 summarize_regexp_list (struct regexp_list *reglist)
818 {
819   if (reglist->regexps)
820     {
821       /* At least one regexp was specified.  Allocate a fastmap for it.  */
822       reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
823       if (reglist->multiple_regexps)
824         {
825           /* Compile the disjunction of the regexps.
826              (If just one regexp was specified, it is already compiled.)  */
827           char const *m = re_compile_pattern (reglist->regexps, reglist->len,
828                                               reglist->buf);
829           if (m != 0)
830             error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
831         }
832     }
833 }
834
835 static void
836 try_help (char const *reason_msgid, char const *operand)
837 {
838   if (reason_msgid)
839     error (0, 0, _(reason_msgid), operand);
840   error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
841          program_name);
842   abort ();
843 }
844
845 static void
846 check_stdout (void)
847 {
848   if (ferror (stdout))
849     fatal ("write failed");
850   else if (fclose (stdout) != 0)
851     pfatal_with_name (_("standard output"));
852 }
853
854 static char const * const option_help_msgid[] = {
855   N_("Compare files line by line."),
856   "",
857   N_("-i  --ignore-case  Ignore case differences in file contents."),
858   N_("--ignore-file-name-case  Ignore case when comparing file names."),
859   N_("--no-ignore-file-name-case  Consider case when comparing file names."),
860   N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
861   N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
862   N_("-w  --ignore-all-space  Ignore all white space."),
863   N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
864   N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
865   N_("--strip-trailing-cr  Strip trailing carriage return on input."),
866 #if HAVE_SETMODE_DOS
867   N_("--binary  Read and write data in binary mode."),
868 #endif
869   N_("-a  --text  Treat all files as text."),
870   "",
871   N_("-c  -C NUM  --context[=NUM]  Output NUM (default 3) lines of copied context.\n\
872 -u  -U NUM  --unified[=NUM]  Output NUM (default 3) lines of unified context.\n\
873   --label LABEL  Use LABEL instead of file name.\n\
874   -p  --show-c-function  Show which C function each change is in.\n\
875   -F RE  --show-function-line=RE  Show the most recent line matching RE."),
876   N_("-q  --brief  Output only whether files differ."),
877   N_("-e  --ed  Output an ed script."),
878   N_("--normal  Output a normal diff."),
879   N_("-n  --rcs  Output an RCS format diff."),
880   N_("-y  --side-by-side  Output in two columns.\n\
881   -W NUM  --width=NUM  Output at most NUM (default 130) print columns.\n\
882   --left-column  Output only the left column of common lines.\n\
883   --suppress-common-lines  Do not output common lines."),
884   N_("-D NAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs."),
885   N_("--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT."),
886   N_("--line-format=LFMT  Similar, but format all input lines with LFMT."),
887   N_("--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT."),
888   N_("  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'."),
889   N_("  GFMT may contain:\n\
890     %<  lines from FILE1\n\
891     %>  lines from FILE2\n\
892     %=  lines common to FILE1 and FILE2\n\
893     %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER\n\
894       LETTERs are as follows for new group, lower case for old group:\n\
895         F  first line number\n\
896         L  last line number\n\
897         N  number of lines = L-F+1\n\
898         E  F-1\n\
899         M  L+1"),
900   N_("  LFMT may contain:\n\
901     %L  contents of line\n\
902     %l  contents of line, excluding any trailing newline\n\
903     %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number"),
904   N_("  Either GFMT or LFMT may contain:\n\
905     %%  %\n\
906     %c'C'  the single character C\n\
907     %c'\\OOO'  the character with octal code OOO"),
908   "",
909   N_("-l  --paginate  Pass the output through `pr' to paginate it."),
910   N_("-t  --expand-tabs  Expand tabs to spaces in output."),
911   N_("-T  --initial-tab  Make tabs line up by prepending a tab."),
912   N_("--tabsize=NUM  Tab stops are every NUM (default 8) print columns."),
913   "",
914   N_("-r  --recursive  Recursively compare any subdirectories found."),
915   N_("-N  --new-file  Treat absent files as empty."),
916   N_("--unidirectional-new-file  Treat absent first files as empty."),
917   N_("-s  --report-identical-files  Report when two files are the same."),
918   N_("-x PAT  --exclude=PAT  Exclude files that match PAT."),
919   N_("-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE."),
920   N_("-S FILE  --starting-file=FILE  Start with FILE when comparing directories."),
921   N_("--from-file=FILE1  Compare FILE1 to all operands.  FILE1 can be a directory."),
922   N_("--to-file=FILE2  Compare all operands to FILE2.  FILE2 can be a directory."),
923   "",
924   N_("--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix."),
925   N_("-d  --minimal  Try hard to find a smaller set of changes."),
926   N_("--speed-large-files  Assume large files and many scattered small changes."),
927   "",
928   N_("-v  --version  Output version info."),
929   N_("--help  Output this help."),
930   "",
931   N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
932   N_("If --from-file or --to-file is given, there are no restrictions on FILES."),
933   N_("If a FILE is `-', read standard input."),
934   N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
935   "",
936   N_("Report bugs to <bug-gnu-utils@gnu.org>."),
937   0
938 };
939
940 static void
941 usage (void)
942 {
943   char const * const *p;
944
945   printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
946
947   for (p = option_help_msgid;  *p;  p++)
948     {
949       if (!**p)
950         putchar ('\n');
951       else
952         {
953           char const *msg = _(*p);
954           char const *nl;
955           while ((nl = strchr (msg, '\n')))
956             {
957               int msglen = nl + 1 - msg;
958               printf ("  %.*s", msglen, msg);
959               msg = nl + 1;
960             }
961
962           printf ("  %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
963         }
964     }
965 }
966
967 /* Set VAR to VALUE, reporting an OPTION error if this is a
968    conflict.  */
969 static void
970 specify_value (char const **var, char const *value, char const *option)
971 {
972   if (*var && strcmp (*var, value) != 0)
973     {
974       error (0, 0, _("conflicting %s option value `%s'"), option, value);
975       try_help (0, 0);
976     }
977   *var = value;
978 }
979
980 /* Set the output style to STYLE, diagnosing conflicts.  */
981 static void
982 specify_style (enum output_style style)
983 {
984   if (output_style != style)
985     {
986       if (output_style != OUTPUT_UNSPECIFIED)
987         try_help ("conflicting output style options", 0);
988       output_style = style;
989     }
990 }
991 \f
992 /* Set the last-modified time of *ST to be the current time.  */
993
994 static void
995 set_mtime_to_now (struct stat *st)
996 {
997 #ifdef ST_MTIM_NSEC
998
999 # if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
1000   if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0)
1001     return;
1002 # endif
1003
1004 # if HAVE_GETTIMEOFDAY
1005   {
1006     struct timeval timeval;
1007     if (gettimeofday (&timeval, 0) == 0)
1008       {
1009         st->st_mtime = timeval.tv_sec;
1010         st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000;
1011         return;
1012       }
1013   }
1014 # endif
1015
1016 #endif /* ST_MTIM_NSEC */
1017
1018   time (&st->st_mtime);
1019 }
1020 \f
1021 /* Compare two files (or dirs) with parent comparison PARENT
1022    and names NAME0 and NAME1.
1023    (If PARENT is 0, then the first name is just NAME0, etc.)
1024    This is self-contained; it opens the files and closes them.
1025
1026    Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1027    different, EXIT_TROUBLE if there is a problem opening them.  */
1028
1029 static int
1030 compare_files (struct comparison const *parent,
1031                char const *name0,
1032                char const *name1)
1033 {
1034   struct comparison cmp;
1035 #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1036   register int f;
1037   int status = EXIT_SUCCESS;
1038   bool same_files;
1039   char *free0, *free1;
1040
1041   /* If this is directory comparison, perhaps we have a file
1042      that exists only in one of the directories.
1043      If so, just print a message to that effect.  */
1044
1045   if (! ((name0 && name1)
1046          || (unidirectional_new_file && name1)
1047          || new_file))
1048     {
1049       char const *name = name0 == 0 ? name1 : name0;
1050       char const *dir = parent->file[name0 == 0].name;
1051
1052       /* See POSIX 1003.1-2001 for this format.  */
1053       message ("Only in %s: %s\n", dir, name);
1054
1055       /* Return EXIT_FAILURE so that diff_dirs will return
1056          EXIT_FAILURE ("some files differ").  */
1057       return EXIT_FAILURE;
1058     }
1059
1060   memset (cmp.file, 0, sizeof cmp.file);
1061   cmp.parent = parent;
1062
1063   /* cmp.file[f].desc markers */
1064 #define NONEXISTENT (-1) /* nonexistent file */
1065 #define UNOPENED (-2) /* unopened file (e.g. directory) */
1066 #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1067
1068 #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1069
1070   cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED;
1071   cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED;
1072
1073   /* Now record the full name of each file, including nonexistent ones.  */
1074
1075   if (name0 == 0)
1076     name0 = name1;
1077   if (name1 == 0)
1078     name1 = name0;
1079
1080   if (!parent)
1081     {
1082       free0 = 0;
1083       free1 = 0;
1084       cmp.file[0].name = name0;
1085       cmp.file[1].name = name1;
1086     }
1087   else
1088     {
1089       cmp.file[0].name = free0
1090         = dir_file_pathname (parent->file[0].name, name0);
1091       cmp.file[1].name = free1
1092         = dir_file_pathname (parent->file[1].name, name1);
1093     }
1094
1095   /* Stat the files.  */
1096
1097   for (f = 0; f < 2; f++)
1098     {
1099       if (cmp.file[f].desc != NONEXISTENT)
1100         {
1101           if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1102             {
1103               cmp.file[f].desc = cmp.file[0].desc;
1104               cmp.file[f].stat = cmp.file[0].stat;
1105             }
1106           else if (strcmp (cmp.file[f].name, "-") == 0)
1107             {
1108               cmp.file[f].desc = STDIN_FILENO;
1109               if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1110                 cmp.file[f].desc = ERRNO_ENCODE (errno);
1111               else
1112                 {
1113                   if (S_ISREG (cmp.file[f].stat.st_mode))
1114                     {
1115                       off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
1116                       if (pos < 0)
1117                         cmp.file[f].desc = ERRNO_ENCODE (errno);
1118                       else
1119                         cmp.file[f].stat.st_size =
1120                           MAX (0, cmp.file[f].stat.st_size - pos);
1121                     }
1122
1123                   /* POSIX 1003.1-2001 requires current time for
1124                      stdin.  */
1125                   set_mtime_to_now (&cmp.file[f].stat);
1126                 }
1127             }
1128           else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
1129             cmp.file[f].desc = ERRNO_ENCODE (errno);
1130         }
1131     }
1132
1133   /* Mark files as nonexistent as needed for -N and -P, if they are
1134      inaccessible empty regular files (the kind of files that 'patch'
1135      creates to indicate nonexistent backups), or if they are
1136      top-level files that do not exist but their counterparts do
1137      exist.  */
1138   for (f = 0; f < 2; f++)
1139     if ((new_file || (f == 0 && unidirectional_new_file))
1140         && (cmp.file[f].desc == UNOPENED
1141             ? (S_ISREG (cmp.file[f].stat.st_mode)
1142                && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1143                && cmp.file[f].stat.st_size == 0)
1144             : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
1145                && ! parent
1146                && cmp.file[1 - f].desc == UNOPENED)))
1147       cmp.file[f].desc = NONEXISTENT;
1148
1149   for (f = 0; f < 2; f++)
1150     if (cmp.file[f].desc == NONEXISTENT)
1151       {
1152         memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1153         cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1154       }
1155
1156   for (f = 0; f < 2; f++)
1157     {
1158       int e = ERRNO_DECODE (cmp.file[f].desc);
1159       if (0 <= e)
1160         {
1161           errno = e;
1162           perror_with_name (cmp.file[f].name);
1163           status = EXIT_TROUBLE;
1164         }
1165     }
1166
1167   if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1168     {
1169       /* If one is a directory, and it was specified in the command line,
1170          use the file in that dir with the other file's basename.  */
1171
1172       int fnm_arg = DIR_P (0);
1173       int dir_arg = 1 - fnm_arg;
1174       char const *fnm = cmp.file[fnm_arg].name;
1175       char const *dir = cmp.file[dir_arg].name;
1176       char const *filename = cmp.file[dir_arg].name = free0
1177         = dir_file_pathname (dir, base_name (fnm));
1178
1179       if (strcmp (fnm, "-") == 0)
1180         fatal ("cannot compare `-' to a directory");
1181
1182       if (stat (filename, &cmp.file[dir_arg].stat) != 0)
1183         {
1184           perror_with_name (filename);
1185           status = EXIT_TROUBLE;
1186         }
1187     }
1188
1189   if (status != EXIT_SUCCESS)
1190     {
1191       /* One of the files should exist but does not.  */
1192     }
1193   else if (cmp.file[0].desc == NONEXISTENT
1194            && cmp.file[1].desc == NONEXISTENT)
1195     {
1196       /* Neither file "exists", so there's nothing to compare.  */
1197     }
1198   else if ((same_files
1199             = (cmp.file[0].desc != NONEXISTENT
1200                && cmp.file[1].desc != NONEXISTENT
1201                && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1202                && same_file_attributes (&cmp.file[0].stat,
1203                                         &cmp.file[1].stat)))
1204            && no_diff_means_no_output)
1205     {
1206       /* The two named files are actually the same physical file.
1207          We know they are identical without actually reading them.  */
1208     }
1209   else if (DIR_P (0) & DIR_P (1))
1210     {
1211       if (output_style == OUTPUT_IFDEF)
1212         fatal ("-D option not supported with directories");
1213
1214       /* If both are directories, compare the files in them.  */
1215
1216       if (parent && !recursive)
1217         {
1218           /* But don't compare dir contents one level down
1219              unless -r was specified.
1220              See POSIX 1003.1-2001 for this format.  */
1221           message ("Common subdirectories: %s and %s\n",
1222                    cmp.file[0].name, cmp.file[1].name);
1223         }
1224       else
1225         status = diff_dirs (&cmp, compare_files);
1226     }
1227   else if ((DIR_P (0) | DIR_P (1))
1228            || (parent
1229                && (! S_ISREG (cmp.file[0].stat.st_mode)
1230                    || ! S_ISREG (cmp.file[1].stat.st_mode))))
1231     {
1232       if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1233         {
1234           /* We have a subdirectory that exists only in one directory.  */
1235
1236           if ((DIR_P (0) | DIR_P (1))
1237               && recursive
1238               && (new_file
1239                   || (unidirectional_new_file
1240                       && cmp.file[0].desc == NONEXISTENT)))
1241             status = diff_dirs (&cmp, compare_files);
1242           else
1243             {
1244               char const *dir
1245                 = parent->file[cmp.file[0].desc == NONEXISTENT].name;
1246
1247               /* See POSIX 1003.1-2001 for this format.  */
1248               message ("Only in %s: %s\n", dir, name0);
1249
1250               status = EXIT_FAILURE;
1251             }
1252         }
1253       else
1254         {
1255           /* We have two files that are not to be compared.  */
1256
1257           /* See POSIX 1003.1-2001 for this format.  */
1258           message5 ("File %s is a %s while file %s is a %s\n",
1259                     file_label[0] ? file_label[0] : cmp.file[0].name,
1260                     file_type (&cmp.file[0].stat),
1261                     file_label[1] ? file_label[1] : cmp.file[1].name,
1262                     file_type (&cmp.file[1].stat));
1263
1264           /* This is a difference.  */
1265           status = EXIT_FAILURE;
1266         }
1267     }
1268   else if (files_can_be_treated_as_binary
1269            && S_ISREG (cmp.file[0].stat.st_mode)
1270            && S_ISREG (cmp.file[1].stat.st_mode)
1271            && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size)
1272     {
1273       message ("Files %s and %s differ\n",
1274                file_label[0] ? file_label[0] : cmp.file[0].name,
1275                file_label[1] ? file_label[1] : cmp.file[1].name);
1276       status = EXIT_FAILURE;
1277     }
1278   else
1279     {
1280       /* Both exist and neither is a directory.  */
1281
1282       /* Open the files and record their descriptors.  */
1283
1284       if (cmp.file[0].desc == UNOPENED)
1285         if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0)
1286           {
1287             perror_with_name (cmp.file[0].name);
1288             status = EXIT_TROUBLE;
1289           }
1290       if (cmp.file[1].desc == UNOPENED)
1291         {
1292           if (same_files)
1293             cmp.file[1].desc = cmp.file[0].desc;
1294           else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0))
1295                    < 0)
1296             {
1297               perror_with_name (cmp.file[1].name);
1298               status = EXIT_TROUBLE;
1299             }
1300         }
1301
1302 #if HAVE_SETMODE_DOS
1303       if (binary)
1304         for (f = 0; f < 2; f++)
1305           if (0 <= cmp.file[f].desc)
1306             set_binary_mode (cmp.file[f].desc, true);
1307 #endif
1308
1309       /* Compare the files, if no error was found.  */
1310
1311       if (status == EXIT_SUCCESS)
1312         status = diff_2_files (&cmp);
1313
1314       /* Close the file descriptors.  */
1315
1316       if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1317         {
1318           perror_with_name (cmp.file[0].name);
1319           status = EXIT_TROUBLE;
1320         }
1321       if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1322           && close (cmp.file[1].desc) != 0)
1323         {
1324           perror_with_name (cmp.file[1].name);
1325           status = EXIT_TROUBLE;
1326         }
1327     }
1328
1329   /* Now the comparison has been done, if no error prevented it,
1330      and STATUS is the value this function will return.  */
1331
1332   if (status == EXIT_SUCCESS)
1333     {
1334       if (report_identical_files && !DIR_P (0))
1335         message ("Files %s and %s are identical\n",
1336                  file_label[0] ? file_label[0] : cmp.file[0].name,
1337                  file_label[1] ? file_label[1] : cmp.file[1].name);
1338     }
1339   else
1340     {
1341       /* Flush stdout so that the user sees differences immediately.
1342          This can hurt performance, unfortunately.  */
1343       if (fflush (stdout) != 0)
1344         pfatal_with_name (_("standard output"));
1345     }
1346
1347   if (free0)
1348     free (free0);
1349   if (free1)
1350     free (free1);
1351
1352   return status;
1353 }