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