xref: /dragonfly/contrib/diffutils/src/diff.c (revision cfd1aba3)
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 
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 
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 
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 
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 }
1415