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