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