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