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