rc.conf: Add and document the missing root_rw_mount=YES
[dragonfly.git] / contrib / diffutils / src / diff.c
blobb388ce42ca280aac0f0fe66da5510798c45e8dbe
1 /* GNU diff - compare files line by line
3 Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
4 2009-2013, 2015-2018 Free Software Foundation, Inc.
6 This file is part of GNU DIFF.
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.
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.
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/>. */
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>
46 /* The official name of this program (e.g., no 'g' prefix). */
47 #define PROGRAM_NAME "diff"
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")
56 #ifndef GUTTER_WIDTH_MINIMUM
57 # define GUTTER_WIDTH_MINIMUM 3
58 #endif
60 struct regexp_list
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;
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);
79 /* If comparing directories, compare their common subdirectories
80 recursively. */
81 static bool recursive;
83 /* In context diffs, show previous lines that match these regexps. */
84 static struct regexp_list function_regexp_list;
86 /* Ignore changes affecting only lines that match these regexps. */
87 static struct regexp_list ignore_regexp_list;
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
97 /* If one file is missing, treat it as present but empty (-N). */
98 static bool new_file;
100 /* If the first file is missing, treat it as present but empty
101 (--unidirectional-new-file). */
102 static bool unidirectional_new_file;
104 /* Report files compared that are the same (-s).
105 Normally nothing is output when that happens. */
106 static bool report_identical_files;
108 static char const shortopts[] =
109 "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
111 /* Values for long options that do not have single-letter equivalents. */
112 enum
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,
132 /* These options must be in sequence. */
133 UNCHANGED_LINE_FORMAT_OPTION,
134 OLD_LINE_FORMAT_OPTION,
135 NEW_LINE_FORMAT_OPTION,
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,
143 COLOR_OPTION,
144 COLOR_PALETTE_OPTION,
146 PRESUME_OUTPUT_TTY_OPTION,
149 static char const group_format_option[][sizeof "--unchanged-group-format"] =
151 "--unchanged-group-format",
152 "--old-group-format",
153 "--new-group-format",
154 "--changed-group-format"
157 static char const line_format_option[][sizeof "--unchanged-line-format"] =
159 "--unchanged-line-format",
160 "--old-line-format",
161 "--new-line-format"
164 static struct option const longopts[] =
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'},
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}
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.
236 Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
237 the length of that vector. */
239 static char *
240 option_list (char **optionvec, int count)
242 int i;
243 size_t size = 1;
244 char *result;
245 char *p;
247 for (i = 0; i < count; i++)
248 size += 1 + shell_quote_length (optionvec[i]);
250 p = result = xmalloc (size);
252 for (i = 0; i < count; i++)
254 *p++ = ' ';
255 p = shell_quote_copy (p, optionvec[i]);
258 *p = '\0';
259 return result;
263 /* Return an option value suitable for add_exclude. */
265 static int
266 exclude_options (void)
268 return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
272 main (int argc, char **argv)
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;
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 textdomain (PACKAGE);
293 c_stack_action (0);
294 function_regexp_list.buf = &function_regexp;
295 ignore_regexp_list.buf = &ignore_regexp;
296 re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
297 excluded = new_exclude ();
298 presume_output_tty = false;
300 /* Decode the options. */
302 while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
304 switch (c)
306 case 0:
307 break;
309 case '0':
310 case '1':
311 case '2':
312 case '3':
313 case '4':
314 case '5':
315 case '6':
316 case '7':
317 case '8':
318 case '9':
319 ocontext = (! ISDIGIT (prev)
320 ? c - '0'
321 : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
322 < CONTEXT_MAX / 10)
323 ? 10 * ocontext + (c - '0')
324 : CONTEXT_MAX);
325 break;
327 case 'a':
328 text = true;
329 break;
331 case 'b':
332 if (ignore_white_space < IGNORE_SPACE_CHANGE)
333 ignore_white_space = IGNORE_SPACE_CHANGE;
334 break;
336 case 'Z':
337 if (ignore_white_space < IGNORE_SPACE_CHANGE)
338 ignore_white_space |= IGNORE_TRAILING_SPACE;
339 break;
341 case 'B':
342 ignore_blank_lines = true;
343 break;
345 case 'C':
346 case 'U':
348 if (optarg)
350 numval = strtoumax (optarg, &numend, 10);
351 if (*numend)
352 try_help ("invalid context length '%s'", optarg);
353 if (CONTEXT_MAX < numval)
354 numval = CONTEXT_MAX;
356 else
357 numval = 3;
359 specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
360 if (context < numval)
361 context = numval;
362 explicit_context = true;
364 break;
366 case 'c':
367 specify_style (OUTPUT_CONTEXT);
368 if (context < 3)
369 context = 3;
370 break;
372 case 'd':
373 minimal = true;
374 break;
376 case 'D':
377 specify_style (OUTPUT_IFDEF);
379 static char const C_ifdef_group_formats[] =
380 "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
381 char *b = xmalloc (sizeof C_ifdef_group_formats
382 + 7 * strlen (optarg) - 14 /* 7*"%s" */
383 - 8 /* 5*"%%" + 3*"%c" */);
384 sprintf (b, C_ifdef_group_formats,
386 optarg, optarg, 0,
387 optarg, optarg, 0,
388 optarg, optarg, optarg);
389 for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
391 specify_value (&group_format[i], b, "-D");
392 b += strlen (b) + 1;
395 break;
397 case 'e':
398 specify_style (OUTPUT_ED);
399 break;
401 case 'E':
402 if (ignore_white_space < IGNORE_SPACE_CHANGE)
403 ignore_white_space |= IGNORE_TAB_EXPANSION;
404 break;
406 case 'f':
407 specify_style (OUTPUT_FORWARD_ED);
408 break;
410 case 'F':
411 add_regexp (&function_regexp_list, optarg);
412 break;
414 case 'h':
415 /* Split the files into chunks for faster processing.
416 Usually does not change the result.
418 This currently has no effect. */
419 break;
421 case 'H':
422 speed_large_files = true;
423 break;
425 case 'i':
426 ignore_case = true;
427 break;
429 case 'I':
430 add_regexp (&ignore_regexp_list, optarg);
431 break;
433 case 'l':
434 if (!pr_program[0])
435 try_help ("pagination not supported on this host", NULL);
436 paginate = true;
437 #ifdef SIGCHLD
438 /* Pagination requires forking and waiting, and
439 System V fork+wait does not work if SIGCHLD is ignored. */
440 signal (SIGCHLD, SIG_DFL);
441 #endif
442 break;
444 case 'L':
445 if (!file_label[0])
446 file_label[0] = optarg;
447 else if (!file_label[1])
448 file_label[1] = optarg;
449 else
450 fatal ("too many file label options");
451 break;
453 case 'n':
454 specify_style (OUTPUT_RCS);
455 break;
457 case 'N':
458 new_file = true;
459 break;
461 case 'p':
462 show_c_function = true;
463 add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
464 break;
466 case 'P':
467 unidirectional_new_file = true;
468 break;
470 case 'q':
471 brief = true;
472 break;
474 case 'r':
475 recursive = true;
476 break;
478 case 's':
479 report_identical_files = true;
480 break;
482 case 'S':
483 specify_value (&starting_file, optarg, "-S");
484 break;
486 case 't':
487 expand_tabs = true;
488 break;
490 case 'T':
491 initial_tab = true;
492 break;
494 case 'u':
495 specify_style (OUTPUT_UNIFIED);
496 if (context < 3)
497 context = 3;
498 break;
500 case 'v':
501 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
502 AUTHORS, (char *) NULL);
503 check_stdout ();
504 return EXIT_SUCCESS;
506 case 'w':
507 ignore_white_space = IGNORE_ALL_SPACE;
508 break;
510 case 'x':
511 add_exclude (excluded, optarg, exclude_options ());
512 break;
514 case 'X':
515 if (add_exclude_file (add_exclude, excluded, optarg,
516 exclude_options (), '\n'))
517 pfatal_with_name (optarg);
518 break;
520 case 'y':
521 specify_style (OUTPUT_SDIFF);
522 break;
524 case 'W':
525 numval = strtoumax (optarg, &numend, 10);
526 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
527 try_help ("invalid width '%s'", optarg);
528 if (width != numval)
530 if (width)
531 fatal ("conflicting width options");
532 width = numval;
534 break;
536 case BINARY_OPTION:
537 #if O_BINARY
538 binary = true;
539 if (! isatty (STDOUT_FILENO))
540 set_binary_mode (STDOUT_FILENO, O_BINARY);
541 #endif
542 break;
544 case FROM_FILE_OPTION:
545 specify_value (&from_file, optarg, "--from-file");
546 break;
548 case HELP_OPTION:
549 usage ();
550 check_stdout ();
551 return EXIT_SUCCESS;
553 case HORIZON_LINES_OPTION:
554 numval = strtoumax (optarg, &numend, 10);
555 if (*numend)
556 try_help ("invalid horizon length '%s'", optarg);
557 horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
558 break;
560 case IGNORE_FILE_NAME_CASE_OPTION:
561 ignore_file_name_case = true;
562 break;
564 case INHIBIT_HUNK_MERGE_OPTION:
565 /* This option is obsolete, but accept it for backward
566 compatibility. */
567 break;
569 case LEFT_COLUMN_OPTION:
570 left_column = true;
571 break;
573 case LINE_FORMAT_OPTION:
574 specify_style (OUTPUT_IFDEF);
575 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
576 specify_value (&line_format[i], optarg, "--line-format");
577 break;
579 case NO_DEREFERENCE_OPTION:
580 no_dereference_symlinks = true;
581 break;
583 case NO_IGNORE_FILE_NAME_CASE_OPTION:
584 ignore_file_name_case = false;
585 break;
587 case NORMAL_OPTION:
588 specify_style (OUTPUT_NORMAL);
589 break;
591 case SDIFF_MERGE_ASSIST_OPTION:
592 specify_style (OUTPUT_SDIFF);
593 sdiff_merge_assist = true;
594 break;
596 case STRIP_TRAILING_CR_OPTION:
597 strip_trailing_cr = true;
598 break;
600 case SUPPRESS_BLANK_EMPTY_OPTION:
601 suppress_blank_empty = true;
602 break;
604 case SUPPRESS_COMMON_LINES_OPTION:
605 suppress_common_lines = true;
606 break;
608 case TABSIZE_OPTION:
609 numval = strtoumax (optarg, &numend, 10);
610 if (! (0 < numval && numval <= SIZE_MAX - GUTTER_WIDTH_MINIMUM)
611 || *numend)
612 try_help ("invalid tabsize '%s'", optarg);
613 if (tabsize != numval)
615 if (tabsize)
616 fatal ("conflicting tabsize options");
617 tabsize = numval;
619 break;
621 case TO_FILE_OPTION:
622 specify_value (&to_file, optarg, "--to-file");
623 break;
625 case UNCHANGED_LINE_FORMAT_OPTION:
626 case OLD_LINE_FORMAT_OPTION:
627 case NEW_LINE_FORMAT_OPTION:
628 specify_style (OUTPUT_IFDEF);
629 c -= UNCHANGED_LINE_FORMAT_OPTION;
630 specify_value (&line_format[c], optarg, line_format_option[c]);
631 break;
633 case UNCHANGED_GROUP_FORMAT_OPTION:
634 case OLD_GROUP_FORMAT_OPTION:
635 case NEW_GROUP_FORMAT_OPTION:
636 case CHANGED_GROUP_FORMAT_OPTION:
637 specify_style (OUTPUT_IFDEF);
638 c -= UNCHANGED_GROUP_FORMAT_OPTION;
639 specify_value (&group_format[c], optarg, group_format_option[c]);
640 break;
642 case COLOR_OPTION:
643 specify_colors_style (optarg);
644 break;
646 case COLOR_PALETTE_OPTION:
647 set_color_palette (optarg);
648 break;
650 case PRESUME_OUTPUT_TTY_OPTION:
651 presume_output_tty = true;
652 break;
654 default:
655 try_help (NULL, NULL);
657 prev = c;
660 if (colors_style == AUTO)
662 char const *t = getenv ("TERM");
663 if (t && STREQ (t, "dumb"))
664 colors_style = NEVER;
667 if (output_style == OUTPUT_UNSPECIFIED)
669 if (show_c_function)
671 specify_style (OUTPUT_CONTEXT);
672 if (ocontext < 0)
673 context = 3;
675 else
676 specify_style (OUTPUT_NORMAL);
679 if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
681 #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
682 || defined HAVE_STRUCT_STAT_ST_SPARE1)
683 time_format = "%Y-%m-%d %H:%M:%S.%N %z";
684 #else
685 time_format = "%Y-%m-%d %H:%M:%S %z";
686 #endif
688 else
690 /* See POSIX 1003.1-2001 for this format. */
691 time_format = "%a %b %e %T %Y";
694 if (0 <= ocontext
695 && (output_style == OUTPUT_CONTEXT
696 || output_style == OUTPUT_UNIFIED)
697 && (context < ocontext
698 || (ocontext < context && ! explicit_context)))
699 context = ocontext;
701 if (! tabsize)
702 tabsize = 8;
703 if (! width)
704 width = 130;
707 /* Maximize first the half line width, and then the gutter width,
708 according to the following constraints:
710 1. Two half lines plus a gutter must fit in a line.
711 2. If the half line width is nonzero:
712 a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
713 b. If tabs are not expanded to spaces,
714 a half line plus a gutter is an integral number of tabs,
715 so that tabs in the right column line up. */
717 size_t t = expand_tabs ? 1 : tabsize;
718 size_t w = width;
719 size_t t_plus_g = t + GUTTER_WIDTH_MINIMUM;
720 size_t unaligned_off = (w >> 1) + (t_plus_g >> 1) + (w & t_plus_g & 1);
721 size_t off = unaligned_off - unaligned_off % t;
722 sdiff_half_width = (off <= GUTTER_WIDTH_MINIMUM || w <= off
724 : MIN (off - GUTTER_WIDTH_MINIMUM, w - off));
725 sdiff_column2_offset = sdiff_half_width ? off : w;
728 /* Make the horizon at least as large as the context, so that
729 shift_boundaries has more freedom to shift the first and last hunks. */
730 if (horizon_lines < context)
731 horizon_lines = context;
733 summarize_regexp_list (&function_regexp_list);
734 summarize_regexp_list (&ignore_regexp_list);
736 if (output_style == OUTPUT_IFDEF)
738 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
739 if (!line_format[i])
740 line_format[i] = "%l\n";
741 if (!group_format[OLD])
742 group_format[OLD]
743 = group_format[CHANGED] ? group_format[CHANGED] : "%<";
744 if (!group_format[NEW])
745 group_format[NEW]
746 = group_format[CHANGED] ? group_format[CHANGED] : "%>";
747 if (!group_format[UNCHANGED])
748 group_format[UNCHANGED] = "%=";
749 if (!group_format[CHANGED])
750 group_format[CHANGED] = concat (group_format[OLD],
751 group_format[NEW], "");
754 no_diff_means_no_output =
755 (output_style == OUTPUT_IFDEF ?
756 (!*group_format[UNCHANGED]
757 || (STREQ (group_format[UNCHANGED], "%=")
758 && !*line_format[UNCHANGED]))
759 : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
761 files_can_be_treated_as_binary =
762 (brief & binary
763 & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
764 | (ignore_regexp_list.regexps || ignore_white_space)));
766 switch_string = option_list (argv + 1, optind - 1);
768 if (from_file)
770 if (to_file)
771 fatal ("--from-file and --to-file both specified");
772 else
773 for (; optind < argc; optind++)
775 int status = compare_files (NULL, from_file, argv[optind]);
776 if (exit_status < status)
777 exit_status = status;
780 else
782 if (to_file)
783 for (; optind < argc; optind++)
785 int status = compare_files (NULL, argv[optind], to_file);
786 if (exit_status < status)
787 exit_status = status;
789 else
791 if (argc - optind != 2)
793 if (argc - optind < 2)
794 try_help ("missing operand after '%s'", argv[argc - 1]);
795 else
796 try_help ("extra operand '%s'", argv[optind + 2]);
799 exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
803 /* Print any messages that were saved up for last. */
804 print_message_queue ();
806 check_stdout ();
807 exit (exit_status);
808 return exit_status;
811 /* Append to REGLIST the regexp PATTERN. */
813 static void
814 add_regexp (struct regexp_list *reglist, char const *pattern)
816 size_t patlen = strlen (pattern);
817 char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
819 if (m != 0)
820 error (EXIT_TROUBLE, 0, "%s: %s", pattern, m);
821 else
823 char *regexps = reglist->regexps;
824 size_t len = reglist->len;
825 bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
826 size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
827 size_t size = reglist->size;
829 if (size <= newlen)
831 if (!size)
832 size = 1;
834 do size *= 2;
835 while (size <= newlen);
837 reglist->size = size;
838 reglist->regexps = regexps = xrealloc (regexps, size);
840 if (multiple_regexps)
842 regexps[len++] = '\\';
843 regexps[len++] = '|';
845 memcpy (regexps + len, pattern, patlen + 1);
849 /* Ensure that REGLIST represents the disjunction of its regexps.
850 This is done here, rather than earlier, to avoid O(N^2) behavior. */
852 static void
853 summarize_regexp_list (struct regexp_list *reglist)
855 if (reglist->regexps)
857 /* At least one regexp was specified. Allocate a fastmap for it. */
858 reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
859 if (reglist->multiple_regexps)
861 /* Compile the disjunction of the regexps.
862 (If just one regexp was specified, it is already compiled.) */
863 char const *m = re_compile_pattern (reglist->regexps, reglist->len,
864 reglist->buf);
865 if (m)
866 die (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
871 static void
872 try_help (char const *reason_msgid, char const *operand)
874 if (reason_msgid)
875 error (0, 0, _(reason_msgid), operand);
876 die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
877 program_name);
880 static void
881 check_stdout (void)
883 if (ferror (stdout))
884 fatal ("write failed");
885 else if (fclose (stdout) != 0)
886 pfatal_with_name (_("standard output"));
889 static char const * const option_help_msgid[] = {
890 N_(" --normal output a normal diff (the default)"),
891 N_("-q, --brief report only when files differ"),
892 N_("-s, --report-identical-files report when two files are the same"),
893 N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
894 N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
895 N_("-e, --ed output an ed script"),
896 N_("-n, --rcs output an RCS format diff"),
897 N_("-y, --side-by-side output in two columns"),
898 N_("-W, --width=NUM output at most NUM (default 130) print columns"),
899 N_(" --left-column output only the left column of common lines"),
900 N_(" --suppress-common-lines do not output common lines"),
902 N_("-p, --show-c-function show which C function each change is in"),
903 N_("-F, --show-function-line=RE show the most recent line matching RE"),
904 N_(" --label LABEL use LABEL instead of file name and timestamp\n"
905 " (can be repeated)"),
907 N_("-t, --expand-tabs expand tabs to spaces in output"),
908 N_("-T, --initial-tab make tabs line up by prepending a tab"),
909 N_(" --tabsize=NUM tab stops every NUM (default 8) print columns"),
910 N_(" --suppress-blank-empty suppress space or tab before empty output lines"),
911 N_("-l, --paginate pass output through 'pr' to paginate it"),
913 N_("-r, --recursive recursively compare any subdirectories found"),
914 N_(" --no-dereference don't follow symbolic links"),
915 N_("-N, --new-file treat absent files as empty"),
916 N_(" --unidirectional-new-file treat absent first files as empty"),
917 N_(" --ignore-file-name-case ignore case when comparing file names"),
918 N_(" --no-ignore-file-name-case consider case when comparing file names"),
919 N_("-x, --exclude=PAT exclude files that match PAT"),
920 N_("-X, --exclude-from=FILE exclude files that match any pattern in FILE"),
921 N_("-S, --starting-file=FILE start with FILE when comparing directories"),
922 N_(" --from-file=FILE1 compare FILE1 to all operands;\n"
923 " FILE1 can be a directory"),
924 N_(" --to-file=FILE2 compare all operands to FILE2;\n"
925 " FILE2 can be a directory"),
927 N_("-i, --ignore-case ignore case differences in file contents"),
928 N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
929 N_("-Z, --ignore-trailing-space ignore white space at line end"),
930 N_("-b, --ignore-space-change ignore changes in the amount of white space"),
931 N_("-w, --ignore-all-space ignore all white space"),
932 N_("-B, --ignore-blank-lines ignore changes where lines are all blank"),
933 N_("-I, --ignore-matching-lines=RE ignore changes where all lines match RE"),
935 N_("-a, --text treat all files as text"),
936 N_(" --strip-trailing-cr strip trailing carriage return on input"),
937 #if O_BINARY
938 N_(" --binary read and write data in binary mode"),
939 #endif
941 N_("-D, --ifdef=NAME output merged file with '#ifdef NAME' diffs"),
942 N_(" --GTYPE-group-format=GFMT format GTYPE input groups with GFMT"),
943 N_(" --line-format=LFMT format all input lines with LFMT"),
944 N_(" --LTYPE-line-format=LFMT format LTYPE input lines with LFMT"),
945 N_(" These format options provide fine-grained control over the output\n"
946 " of diff, generalizing -D/--ifdef."),
947 N_(" LTYPE is 'old', 'new', or 'unchanged'. GTYPE is LTYPE or 'changed'."),
948 N_(" GFMT (only) may contain:\n\
949 %< lines from FILE1\n\
950 %> lines from FILE2\n\
951 %= lines common to FILE1 and FILE2\n\
952 %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
953 LETTERs are as follows for new group, lower case for old group:\n\
954 F first line number\n\
955 L last line number\n\
956 N number of lines = L-F+1\n\
957 E F-1\n\
958 M L+1\n\
959 %(A=B?T:E) if A equals B then T else E"),
960 N_(" LFMT (only) may contain:\n\
961 %L contents of line\n\
962 %l contents of line, excluding any trailing newline\n\
963 %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
964 N_(" Both GFMT and LFMT may contain:\n\
965 %% %\n\
966 %c'C' the single character C\n\
967 %c'\\OOO' the character with octal code OOO\n\
968 C the character C (other characters represent themselves)"),
970 N_("-d, --minimal try hard to find a smaller set of changes"),
971 N_(" --horizon-lines=NUM keep NUM lines of the common prefix and suffix"),
972 N_(" --speed-large-files assume large files and many scattered small changes"),
973 N_(" --color[=WHEN] colorize the output; WHEN can be 'never', 'always',\n"
974 " or 'auto' (the default)"),
975 N_(" --palette=PALETTE the colors to use when --color is active; PALETTE is\n"
976 " a colon-separated list of terminfo capabilities"),
978 N_(" --help display this help and exit"),
979 N_("-v, --version output version information and exit"),
981 N_("FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'."),
982 N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
983 N_("If a FILE is '-', read standard input."),
984 N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
988 static void
989 usage (void)
991 char const * const *p;
993 printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
994 printf ("%s\n\n", _("Compare FILES line by line."));
996 fputs (_("\
997 Mandatory arguments to long options are mandatory for short options too.\n\
998 "), stdout);
1000 for (p = option_help_msgid; *p; p++)
1002 if (!**p)
1003 putchar ('\n');
1004 else
1006 char const *msg = _(*p);
1007 char const *nl;
1008 while ((nl = strchr (msg, '\n')))
1010 int msglen = nl + 1 - msg;
1011 /* This assertion is solely to avoid a warning from
1012 gcc's -Wformat-overflow=. */
1013 assert (msglen < 4096);
1014 printf (" %.*s", msglen, msg);
1015 msg = nl + 1;
1018 printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
1021 emit_bug_reporting_address ();
1024 /* Set VAR to VALUE, reporting an OPTION error if this is a
1025 conflict. */
1026 static void
1027 specify_value (char const **var, char const *value, char const *option)
1029 if (*var && ! STREQ (*var, value))
1031 error (0, 0, _("conflicting %s option value '%s'"), option, value);
1032 try_help (NULL, NULL);
1034 *var = value;
1037 /* Set the output style to STYLE, diagnosing conflicts. */
1038 static void
1039 specify_style (enum output_style style)
1041 if (output_style != style)
1043 if (output_style != OUTPUT_UNSPECIFIED)
1044 try_help ("conflicting output style options", NULL);
1045 output_style = style;
1049 /* Set the color mode. */
1050 static void
1051 specify_colors_style (char const *value)
1053 if (value == NULL || STREQ (value, "auto"))
1054 colors_style = AUTO;
1055 else if (STREQ (value, "always"))
1056 colors_style = ALWAYS;
1057 else if (STREQ (value, "never"))
1058 colors_style = NEVER;
1059 else
1060 try_help ("invalid color '%s'", value);
1064 /* Set the last-modified time of *ST to be the current time. */
1066 static void
1067 set_mtime_to_now (struct stat *st)
1069 #ifdef STAT_TIMESPEC
1070 gettime (&STAT_TIMESPEC (st, st_mtim));
1071 #else
1072 struct timespec t;
1073 gettime (&t);
1074 st->st_mtime = t.tv_sec;
1075 # if defined STAT_TIMESPEC_NS
1076 STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
1077 # elif defined HAVE_STRUCT_STAT_ST_SPARE1
1078 st->st_spare1 = t.tv_nsec / 1000;
1079 # endif
1080 #endif
1083 /* Compare two files (or dirs) with parent comparison PARENT
1084 and names NAME0 and NAME1.
1085 (If PARENT is null, then the first name is just NAME0, etc.)
1086 This is self-contained; it opens the files and closes them.
1088 Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1089 different, EXIT_TROUBLE if there is a problem opening them. */
1091 static int
1092 compare_files (struct comparison const *parent,
1093 char const *name0,
1094 char const *name1)
1096 struct comparison cmp;
1097 #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1098 register int f;
1099 int status = EXIT_SUCCESS;
1100 bool same_files;
1101 char *free0;
1102 char *free1;
1104 /* If this is directory comparison, perhaps we have a file
1105 that exists only in one of the directories.
1106 If so, just print a message to that effect. */
1108 if (! ((name0 && name1)
1109 || (unidirectional_new_file && name1)
1110 || new_file))
1112 char const *name = name0 ? name0 : name1;
1113 char const *dir = parent->file[!name0].name;
1115 /* See POSIX 1003.1-2001 for this format. */
1116 message ("Only in %s: %s\n", dir, name);
1118 /* Return EXIT_FAILURE so that diff_dirs will return
1119 EXIT_FAILURE ("some files differ"). */
1120 return EXIT_FAILURE;
1123 memset (cmp.file, 0, sizeof cmp.file);
1124 cmp.parent = parent;
1126 /* cmp.file[f].desc markers */
1127 #define NONEXISTENT (-1) /* nonexistent file */
1128 #define UNOPENED (-2) /* unopened file (e.g. directory) */
1129 #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1131 #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1133 cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
1134 cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
1136 /* Now record the full name of each file, including nonexistent ones. */
1138 if (!name0)
1139 name0 = name1;
1140 if (!name1)
1141 name1 = name0;
1143 if (!parent)
1145 free0 = NULL;
1146 free1 = NULL;
1147 cmp.file[0].name = name0;
1148 cmp.file[1].name = name1;
1150 else
1152 cmp.file[0].name = free0
1153 = file_name_concat (parent->file[0].name, name0, NULL);
1154 cmp.file[1].name = free1
1155 = file_name_concat (parent->file[1].name, name1, NULL);
1158 /* Stat the files. */
1160 for (f = 0; f < 2; f++)
1162 if (cmp.file[f].desc != NONEXISTENT)
1164 if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1166 cmp.file[f].desc = cmp.file[0].desc;
1167 cmp.file[f].stat = cmp.file[0].stat;
1169 else if (STREQ (cmp.file[f].name, "-"))
1171 cmp.file[f].desc = STDIN_FILENO;
1172 if (binary && ! isatty (STDIN_FILENO))
1173 set_binary_mode (STDIN_FILENO, O_BINARY);
1174 if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1175 cmp.file[f].desc = ERRNO_ENCODE (errno);
1176 else
1178 if (S_ISREG (cmp.file[f].stat.st_mode))
1180 off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
1181 if (pos < 0)
1182 cmp.file[f].desc = ERRNO_ENCODE (errno);
1183 else
1184 cmp.file[f].stat.st_size =
1185 MAX (0, cmp.file[f].stat.st_size - pos);
1188 /* POSIX 1003.1-2001 requires current time for
1189 stdin. */
1190 set_mtime_to_now (&cmp.file[f].stat);
1193 else if ((no_dereference_symlinks
1194 ? lstat (cmp.file[f].name, &cmp.file[f].stat)
1195 : stat (cmp.file[f].name, &cmp.file[f].stat))
1196 != 0)
1197 cmp.file[f].desc = ERRNO_ENCODE (errno);
1201 /* Mark files as nonexistent as needed for -N and -P, if they are
1202 inaccessible empty regular files (the kind of files that 'patch'
1203 creates to indicate nonexistent backups), or if they are
1204 top-level files that do not exist but their counterparts do
1205 exist. */
1206 for (f = 0; f < 2; f++)
1207 if ((new_file || (f == 0 && unidirectional_new_file))
1208 && (cmp.file[f].desc == UNOPENED
1209 ? (S_ISREG (cmp.file[f].stat.st_mode)
1210 && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1211 && cmp.file[f].stat.st_size == 0)
1212 : ((cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
1213 || cmp.file[f].desc == ERRNO_ENCODE (EBADF))
1214 && ! parent
1215 && (cmp.file[1 - f].desc == UNOPENED
1216 || cmp.file[1 - f].desc == STDIN_FILENO))))
1217 cmp.file[f].desc = NONEXISTENT;
1219 for (f = 0; f < 2; f++)
1220 if (cmp.file[f].desc == NONEXISTENT)
1222 memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1223 cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1226 for (f = 0; f < 2; f++)
1228 int e = ERRNO_DECODE (cmp.file[f].desc);
1229 if (0 <= e)
1231 errno = e;
1232 perror_with_name (cmp.file[f].name);
1233 status = EXIT_TROUBLE;
1237 if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1239 /* If one is a directory, and it was specified in the command line,
1240 use the file in that dir with the other file's basename. */
1242 int fnm_arg = DIR_P (0);
1243 int dir_arg = 1 - fnm_arg;
1244 char const *fnm = cmp.file[fnm_arg].name;
1245 char const *dir = cmp.file[dir_arg].name;
1246 char const *filename = cmp.file[dir_arg].name = free0
1247 = find_dir_file_pathname (dir, last_component (fnm));
1249 if (STREQ (fnm, "-"))
1250 fatal ("cannot compare '-' to a directory");
1252 if ((no_dereference_symlinks
1253 ? lstat (filename, &cmp.file[dir_arg].stat)
1254 : stat (filename, &cmp.file[dir_arg].stat))
1255 != 0)
1257 perror_with_name (filename);
1258 status = EXIT_TROUBLE;
1262 if (status != EXIT_SUCCESS)
1264 /* One of the files should exist but does not. */
1266 else if (cmp.file[0].desc == NONEXISTENT
1267 && cmp.file[1].desc == NONEXISTENT)
1269 /* Neither file "exists", so there's nothing to compare. */
1271 else if ((same_files
1272 = (cmp.file[0].desc != NONEXISTENT
1273 && cmp.file[1].desc != NONEXISTENT
1274 && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1275 && same_file_attributes (&cmp.file[0].stat,
1276 &cmp.file[1].stat)))
1277 && no_diff_means_no_output)
1279 /* The two named files are actually the same physical file.
1280 We know they are identical without actually reading them. */
1282 else if (DIR_P (0) & DIR_P (1))
1284 if (output_style == OUTPUT_IFDEF)
1285 fatal ("-D option not supported with directories");
1287 /* If both are directories, compare the files in them. */
1289 if (parent && !recursive)
1291 /* But don't compare dir contents one level down
1292 unless -r was specified.
1293 See POSIX 1003.1-2001 for this format. */
1294 message ("Common subdirectories: %s and %s\n",
1295 cmp.file[0].name, cmp.file[1].name);
1297 else
1298 status = diff_dirs (&cmp, compare_files);
1300 else if ((DIR_P (0) | DIR_P (1))
1301 || (parent
1302 && !((S_ISREG (cmp.file[0].stat.st_mode)
1303 || S_ISLNK (cmp.file[0].stat.st_mode))
1304 && (S_ISREG (cmp.file[1].stat.st_mode)
1305 || S_ISLNK (cmp.file[1].stat.st_mode)))))
1307 if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1309 /* We have a subdirectory that exists only in one directory. */
1311 if ((DIR_P (0) | DIR_P (1))
1312 && recursive
1313 && (new_file
1314 || (unidirectional_new_file
1315 && cmp.file[0].desc == NONEXISTENT)))
1316 status = diff_dirs (&cmp, compare_files);
1317 else
1319 char const *dir;
1321 /* PARENT must be non-NULL here. */
1322 assert (parent);
1323 dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
1325 /* See POSIX 1003.1-2001 for this format. */
1326 message ("Only in %s: %s\n", dir, name0);
1328 status = EXIT_FAILURE;
1331 else
1333 /* We have two files that are not to be compared. */
1335 /* See POSIX 1003.1-2001 for this format. */
1336 message5 ("File %s is a %s while file %s is a %s\n",
1337 file_label[0] ? file_label[0] : cmp.file[0].name,
1338 file_type (&cmp.file[0].stat),
1339 file_label[1] ? file_label[1] : cmp.file[1].name,
1340 file_type (&cmp.file[1].stat));
1342 /* This is a difference. */
1343 status = EXIT_FAILURE;
1346 else if (S_ISLNK (cmp.file[0].stat.st_mode)
1347 || S_ISLNK (cmp.file[1].stat.st_mode))
1349 /* We get here only if we use lstat(), not stat(). */
1350 assert (no_dereference_symlinks);
1352 if (S_ISLNK (cmp.file[0].stat.st_mode)
1353 && S_ISLNK (cmp.file[1].stat.st_mode))
1355 /* Compare the values of the symbolic links. */
1356 char *link_value[2] = { NULL, NULL };
1358 for (f = 0; f < 2; f++)
1360 link_value[f] = xreadlink (cmp.file[f].name);
1361 if (link_value[f] == NULL)
1363 perror_with_name (cmp.file[f].name);
1364 status = EXIT_TROUBLE;
1365 break;
1368 if (status == EXIT_SUCCESS)
1370 if ( ! STREQ (link_value[0], link_value[1]))
1372 message ("Symbolic links %s and %s differ\n",
1373 cmp.file[0].name, cmp.file[1].name);
1374 /* This is a difference. */
1375 status = EXIT_FAILURE;
1378 for (f = 0; f < 2; f++)
1379 free (link_value[f]);
1381 else
1383 /* We have two files that are not to be compared, because
1384 one of them is a symbolic link and the other one is not. */
1386 message5 ("File %s is a %s while file %s is a %s\n",
1387 file_label[0] ? file_label[0] : cmp.file[0].name,
1388 file_type (&cmp.file[0].stat),
1389 file_label[1] ? file_label[1] : cmp.file[1].name,
1390 file_type (&cmp.file[1].stat));
1392 /* This is a difference. */
1393 status = EXIT_FAILURE;
1396 else if (files_can_be_treated_as_binary
1397 && S_ISREG (cmp.file[0].stat.st_mode)
1398 && S_ISREG (cmp.file[1].stat.st_mode)
1399 && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
1400 && 0 < cmp.file[0].stat.st_size
1401 && 0 < cmp.file[1].stat.st_size)
1403 message ("Files %s and %s differ\n",
1404 file_label[0] ? file_label[0] : cmp.file[0].name,
1405 file_label[1] ? file_label[1] : cmp.file[1].name);
1406 status = EXIT_FAILURE;
1408 else
1410 /* Both exist and neither is a directory. */
1412 /* Open the files and record their descriptors. */
1414 int oflags = O_RDONLY | (binary ? O_BINARY : 0);
1416 if (cmp.file[0].desc == UNOPENED)
1417 if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
1419 perror_with_name (cmp.file[0].name);
1420 status = EXIT_TROUBLE;
1422 if (cmp.file[1].desc == UNOPENED)
1424 if (same_files)
1425 cmp.file[1].desc = cmp.file[0].desc;
1426 else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
1428 perror_with_name (cmp.file[1].name);
1429 status = EXIT_TROUBLE;
1433 /* Compare the files, if no error was found. */
1435 if (status == EXIT_SUCCESS)
1436 status = diff_2_files (&cmp);
1438 /* Close the file descriptors. */
1440 if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1442 perror_with_name (cmp.file[0].name);
1443 status = EXIT_TROUBLE;
1445 if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1446 && close (cmp.file[1].desc) != 0)
1448 perror_with_name (cmp.file[1].name);
1449 status = EXIT_TROUBLE;
1453 /* Now the comparison has been done, if no error prevented it,
1454 and STATUS is the value this function will return. */
1456 if (status == EXIT_SUCCESS)
1458 if (report_identical_files && !DIR_P (0))
1459 message ("Files %s and %s are identical\n",
1460 file_label[0] ? file_label[0] : cmp.file[0].name,
1461 file_label[1] ? file_label[1] : cmp.file[1].name);
1463 else
1465 /* Flush stdout so that the user sees differences immediately.
1466 This can hurt performance, unfortunately. */
1467 if (fflush (stdout) != 0)
1468 pfatal_with_name (_("standard output"));
1471 free (free0);
1472 free (free1);
1474 return status;