1 /* diff - compare files line by line
3 Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
4 2009-2010 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/>. */
30 #include <file-type.h>
33 #include <hard-locale.h>
37 #include <stat-time.h>
39 #include <version-etc.h>
43 /* The official name of this program (e.g., no `g' prefix). */
44 #define PROGRAM_NAME "diff"
47 proper_name ("Paul Eggert"), \
48 proper_name ("Mike Haertel"), \
49 proper_name ("David Hayes"), \
50 proper_name ("Richard Stallman"), \
51 proper_name ("Len Tower")
53 #ifndef GUTTER_WIDTH_MINIMUM
54 # define GUTTER_WIDTH_MINIMUM 3
59 char *regexps; /* chars representing disjunction of the regexps */
60 size_t len; /* chars used in `regexps' */
61 size_t size; /* size malloc'ed for `regexps'; 0 if not malloc'ed */
62 bool multiple_regexps;/* Does `regexps' represent a disjunction? */
63 struct re_pattern_buffer *buf;
66 static int compare_files (struct comparison const *, char const *, char const *);
67 static void add_regexp (struct regexp_list *, char const *);
68 static void summarize_regexp_list (struct regexp_list *);
69 static void specify_style (enum output_style);
70 static void specify_value (char const **, char const *, char const *);
71 static void try_help (char const *, char const *) __attribute__((noreturn));
72 static void check_stdout (void);
73 static void usage (void);
75 /* If comparing directories, compare their common subdirectories
77 static bool recursive;
79 /* In context diffs, show previous lines that match these regexps. */
80 static struct regexp_list function_regexp_list;
82 /* Ignore changes affecting only lines that match these regexps. */
83 static struct regexp_list ignore_regexp_list;
86 /* Use binary I/O when reading and writing data (--binary).
87 On POSIX hosts, this has no effect. */
90 enum { binary = true };
93 /* When comparing directories, if a file appears only in one
94 directory, treat it as present but empty in the other (-N).
95 Then `patch' would create the file with appropriate contents. */
98 /* When comparing directories, if a file appears only in the second
99 directory of the two, treat it as present but empty in the other
100 (--unidirectional-new-file).
101 Then `patch' would create the file with appropriate contents. */
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:y";
111 /* Values for long options that do not have single-letter equivalents. */
114 BINARY_OPTION = CHAR_MAX + 1,
117 HORIZON_LINES_OPTION,
118 IGNORE_FILE_NAME_CASE_OPTION,
119 INHIBIT_HUNK_MERGE_OPTION,
122 NO_IGNORE_FILE_NAME_CASE_OPTION,
124 SDIFF_MERGE_ASSIST_OPTION,
125 STRIP_TRAILING_CR_OPTION,
126 SUPPRESS_BLANK_EMPTY_OPTION,
127 SUPPRESS_COMMON_LINES_OPTION,
131 /* These options must be in sequence. */
132 UNCHANGED_LINE_FORMAT_OPTION,
133 OLD_LINE_FORMAT_OPTION,
134 NEW_LINE_FORMAT_OPTION,
136 /* These options must be in sequence. */
137 UNCHANGED_GROUP_FORMAT_OPTION,
138 OLD_GROUP_FORMAT_OPTION,
139 NEW_GROUP_FORMAT_OPTION,
140 CHANGED_GROUP_FORMAT_OPTION
143 static char const group_format_option[][sizeof "--unchanged-group-format"] =
145 "--unchanged-group-format",
146 "--old-group-format",
147 "--new-group-format",
148 "--changed-group-format"
151 static char const line_format_option[][sizeof "--unchanged-line-format"] =
153 "--unchanged-line-format",
158 static struct option const longopts[] =
160 {"binary", 0, 0, BINARY_OPTION},
161 {"brief", 0, 0, 'q'},
162 {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
163 {"context", 2, 0, 'C'},
165 {"exclude", 1, 0, 'x'},
166 {"exclude-from", 1, 0, 'X'},
167 {"expand-tabs", 0, 0, 't'},
168 {"forward-ed", 0, 0, 'f'},
169 {"from-file", 1, 0, FROM_FILE_OPTION},
170 {"help", 0, 0, HELP_OPTION},
171 {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
172 {"ifdef", 1, 0, 'D'},
173 {"ignore-all-space", 0, 0, 'w'},
174 {"ignore-blank-lines", 0, 0, 'B'},
175 {"ignore-case", 0, 0, 'i'},
176 {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
177 {"ignore-matching-lines", 1, 0, 'I'},
178 {"ignore-space-change", 0, 0, 'b'},
179 {"ignore-tab-expansion", 0, 0, 'E'},
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-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
190 {"normal", 0, 0, NORMAL_OPTION},
191 {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
192 {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
193 {"paginate", 0, 0, 'l'},
195 {"recursive", 0, 0, 'r'},
196 {"report-identical-files", 0, 0, 's'},
197 {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
198 {"show-c-function", 0, 0, 'p'},
199 {"show-function-line", 1, 0, 'F'},
200 {"side-by-side", 0, 0, 'y'},
201 {"speed-large-files", 0, 0, 'H'},
202 {"starting-file", 1, 0, 'S'},
203 {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
204 {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
205 {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
206 {"tabsize", 1, 0, TABSIZE_OPTION},
208 {"to-file", 1, 0, TO_FILE_OPTION},
209 {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
210 {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
211 {"unidirectional-new-file", 0, 0, 'P'},
212 {"unified", 2, 0, 'U'},
213 {"version", 0, 0, 'v'},
214 {"width", 1, 0, 'W'},
218 /* Return a string containing the command options with which diff was invoked.
219 Spaces appear between what were separate ARGV-elements.
220 There is a space at the beginning but none at the end.
221 If there were no options, the result is an empty string.
223 Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
224 the length of that vector. */
227 option_list (char **optionvec, int count)
234 for (i = 0; i < count; i++)
235 size += 1 + shell_quote_length (optionvec[i]);
237 p = result = xmalloc (size);
239 for (i = 0; i < count; i++)
242 p = shell_quote_copy (p, optionvec[i]);
250 /* Return an option value suitable for add_exclude. */
253 exclude_options (void)
255 return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
259 main (int argc, char **argv)
261 int exit_status = EXIT_SUCCESS;
266 bool explicit_context = false;
268 bool show_c_function = false;
269 char const *from_file = NULL;
270 char const *to_file = NULL;
274 /* Do our initializations. */
275 exit_failure = EXIT_TROUBLE;
276 initialize_main (&argc, &argv);
277 set_program_name (argv[0]);
278 setlocale (LC_ALL, "");
279 textdomain (PACKAGE);
281 function_regexp_list.buf = &function_regexp;
282 ignore_regexp_list.buf = &ignore_regexp;
283 re_set_syntax (RE_SYNTAX_GREP);
284 excluded = new_exclude ();
286 /* Decode the options. */
288 while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
305 if (! ISDIGIT (prev))
307 else if (LIN_MAX / 10 < ocontext
308 || ((ocontext = 10 * ocontext + c - '0') < 0))
317 if (ignore_white_space < IGNORE_SPACE_CHANGE)
318 ignore_white_space = IGNORE_SPACE_CHANGE;
322 ignore_blank_lines = true;
330 numval = strtoumax (optarg, &numend, 10);
332 try_help ("invalid context length `%s'", optarg);
333 if (LIN_MAX < numval)
339 specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
340 if (context < numval)
342 explicit_context = true;
347 specify_style (OUTPUT_CONTEXT);
357 specify_style (OUTPUT_IFDEF);
359 static char const C_ifdef_group_formats[] =
360 "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
361 char *b = xmalloc (sizeof C_ifdef_group_formats
362 + 7 * strlen (optarg) - 14 /* 7*"%s" */
363 - 8 /* 5*"%%" + 3*"%c" */);
364 sprintf (b, C_ifdef_group_formats,
368 optarg, optarg, optarg);
369 for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
371 specify_value (&group_format[i], b, "-D");
378 specify_style (OUTPUT_ED);
382 if (ignore_white_space < IGNORE_TAB_EXPANSION)
383 ignore_white_space = IGNORE_TAB_EXPANSION;
387 specify_style (OUTPUT_FORWARD_ED);
391 add_regexp (&function_regexp_list, optarg);
395 /* Split the files into chunks for faster processing.
396 Usually does not change the result.
398 This currently has no effect. */
402 speed_large_files = true;
410 add_regexp (&ignore_regexp_list, optarg);
415 try_help ("pagination not supported on this host", NULL);
418 /* Pagination requires forking and waiting, and
419 System V fork+wait does not work if SIGCHLD is ignored. */
420 signal (SIGCHLD, SIG_DFL);
426 file_label[0] = optarg;
427 else if (!file_label[1])
428 file_label[1] = optarg;
430 fatal ("too many file label options");
434 specify_style (OUTPUT_RCS);
442 show_c_function = true;
443 add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
447 unidirectional_new_file = true;
459 report_identical_files = true;
463 specify_value (&starting_file, optarg, "-S");
475 specify_style (OUTPUT_UNIFIED);
481 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, PACKAGE_VERSION,
482 AUTHORS, (char *) NULL);
487 ignore_white_space = IGNORE_ALL_SPACE;
491 add_exclude (excluded, optarg, exclude_options ());
495 if (add_exclude_file (add_exclude, excluded, optarg,
496 exclude_options (), '\n'))
497 pfatal_with_name (optarg);
501 specify_style (OUTPUT_SDIFF);
505 numval = strtoumax (optarg, &numend, 10);
506 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
507 try_help ("invalid width `%s'", optarg);
511 fatal ("conflicting width options");
519 if (! isatty (STDOUT_FILENO))
520 xfreopen (NULL, "wb", stdout);
524 case FROM_FILE_OPTION:
525 specify_value (&from_file, optarg, "--from-file");
533 case HORIZON_LINES_OPTION:
534 numval = strtoumax (optarg, &numend, 10);
536 try_help ("invalid horizon length `%s'", optarg);
537 horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
540 case IGNORE_FILE_NAME_CASE_OPTION:
541 ignore_file_name_case = true;
544 case INHIBIT_HUNK_MERGE_OPTION:
545 /* This option is obsolete, but accept it for backward
549 case LEFT_COLUMN_OPTION:
553 case LINE_FORMAT_OPTION:
554 specify_style (OUTPUT_IFDEF);
555 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
556 specify_value (&line_format[i], optarg, "--line-format");
559 case NO_IGNORE_FILE_NAME_CASE_OPTION:
560 ignore_file_name_case = false;
564 specify_style (OUTPUT_NORMAL);
567 case SDIFF_MERGE_ASSIST_OPTION:
568 specify_style (OUTPUT_SDIFF);
569 sdiff_merge_assist = true;
572 case STRIP_TRAILING_CR_OPTION:
573 strip_trailing_cr = true;
576 case SUPPRESS_BLANK_EMPTY_OPTION:
577 suppress_blank_empty = true;
580 case SUPPRESS_COMMON_LINES_OPTION:
581 suppress_common_lines = true;
585 numval = strtoumax (optarg, &numend, 10);
586 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
587 try_help ("invalid tabsize `%s'", optarg);
588 if (tabsize != numval)
591 fatal ("conflicting tabsize options");
597 specify_value (&to_file, optarg, "--to-file");
600 case UNCHANGED_LINE_FORMAT_OPTION:
601 case OLD_LINE_FORMAT_OPTION:
602 case NEW_LINE_FORMAT_OPTION:
603 specify_style (OUTPUT_IFDEF);
604 c -= UNCHANGED_LINE_FORMAT_OPTION;
605 specify_value (&line_format[c], optarg, line_format_option[c]);
608 case UNCHANGED_GROUP_FORMAT_OPTION:
609 case OLD_GROUP_FORMAT_OPTION:
610 case NEW_GROUP_FORMAT_OPTION:
611 case CHANGED_GROUP_FORMAT_OPTION:
612 specify_style (OUTPUT_IFDEF);
613 c -= UNCHANGED_GROUP_FORMAT_OPTION;
614 specify_value (&group_format[c], optarg, group_format_option[c]);
618 try_help (NULL, NULL);
623 if (output_style == OUTPUT_UNSPECIFIED)
627 specify_style (OUTPUT_CONTEXT);
632 specify_style (OUTPUT_NORMAL);
635 if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
637 #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
638 || defined HAVE_STRUCT_STAT_ST_SPARE1)
639 time_format = "%Y-%m-%d %H:%M:%S.%N %z";
641 time_format = "%Y-%m-%d %H:%M:%S %z";
646 /* See POSIX 1003.1-2001 for this format. */
647 time_format = "%a %b %e %T %Y";
651 && (output_style == OUTPUT_CONTEXT
652 || output_style == OUTPUT_UNIFIED)
653 && (context < ocontext
654 || (ocontext < context && ! explicit_context)))
663 /* Maximize first the half line width, and then the gutter width,
664 according to the following constraints:
666 1. Two half lines plus a gutter must fit in a line.
667 2. If the half line width is nonzero:
668 a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
669 b. If tabs are not expanded to spaces,
670 a half line plus a gutter is an integral number of tabs,
671 so that tabs in the right column line up. */
673 intmax_t t = expand_tabs ? 1 : tabsize;
675 intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t) * t;
676 sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
677 sdiff_column2_offset = sdiff_half_width ? off : w;
680 /* Make the horizon at least as large as the context, so that
681 shift_boundaries has more freedom to shift the first and last hunks. */
682 if (horizon_lines < context)
683 horizon_lines = context;
685 summarize_regexp_list (&function_regexp_list);
686 summarize_regexp_list (&ignore_regexp_list);
688 if (output_style == OUTPUT_IFDEF)
690 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
692 line_format[i] = "%l\n";
693 if (!group_format[OLD])
695 = group_format[CHANGED] ? group_format[CHANGED] : "%<";
696 if (!group_format[NEW])
698 = group_format[CHANGED] ? group_format[CHANGED] : "%>";
699 if (!group_format[UNCHANGED])
700 group_format[UNCHANGED] = "%=";
701 if (!group_format[CHANGED])
702 group_format[CHANGED] = concat (group_format[OLD],
703 group_format[NEW], "");
706 no_diff_means_no_output =
707 (output_style == OUTPUT_IFDEF ?
708 (!*group_format[UNCHANGED]
709 || (STREQ (group_format[UNCHANGED], "%=")
710 && !*line_format[UNCHANGED]))
711 : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
713 files_can_be_treated_as_binary =
715 & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
716 | (ignore_regexp_list.regexps || ignore_white_space)));
718 switch_string = option_list (argv + 1, optind - 1);
723 fatal ("--from-file and --to-file both specified");
725 for (; optind < argc; optind++)
727 int status = compare_files (NULL, from_file, argv[optind]);
728 if (exit_status < status)
729 exit_status = status;
735 for (; optind < argc; optind++)
737 int status = compare_files (NULL, argv[optind], to_file);
738 if (exit_status < status)
739 exit_status = status;
743 if (argc - optind != 2)
745 if (argc - optind < 2)
746 try_help ("missing operand after `%s'", argv[argc - 1]);
748 try_help ("extra operand `%s'", argv[optind + 2]);
751 exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
755 /* Print any messages that were saved up for last. */
756 print_message_queue ();
763 /* Append to REGLIST the regexp PATTERN. */
766 add_regexp (struct regexp_list *reglist, char const *pattern)
768 size_t patlen = strlen (pattern);
769 char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
772 error (0, 0, "%s: %s", pattern, m);
775 char *regexps = reglist->regexps;
776 size_t len = reglist->len;
777 bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
778 size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
779 size_t size = reglist->size;
787 while (size <= newlen);
789 reglist->size = size;
790 reglist->regexps = regexps = xrealloc (regexps, size);
792 if (multiple_regexps)
794 regexps[len++] = '\\';
795 regexps[len++] = '|';
797 memcpy (regexps + len, pattern, patlen + 1);
801 /* Ensure that REGLIST represents the disjunction of its regexps.
802 This is done here, rather than earlier, to avoid O(N^2) behavior. */
805 summarize_regexp_list (struct regexp_list *reglist)
807 if (reglist->regexps)
809 /* At least one regexp was specified. Allocate a fastmap for it. */
810 reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
811 if (reglist->multiple_regexps)
813 /* Compile the disjunction of the regexps.
814 (If just one regexp was specified, it is already compiled.) */
815 char const *m = re_compile_pattern (reglist->regexps, reglist->len,
818 error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
824 try_help (char const *reason_msgid, char const *operand)
827 error (0, 0, _(reason_msgid), operand);
828 error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
837 fatal ("write failed");
838 else if (fclose (stdout) != 0)
839 pfatal_with_name (_("standard output"));
842 static char const * const option_help_msgid[] = {
843 N_("Compare files line by line."),
845 N_("-i --ignore-case Ignore case differences in file contents."),
846 N_("--ignore-file-name-case Ignore case when comparing file names."),
847 N_("--no-ignore-file-name-case Consider case when comparing file names."),
848 N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."),
849 N_("-b --ignore-space-change Ignore changes in the amount of white space."),
850 N_("-w --ignore-all-space Ignore all white space."),
851 N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."),
852 N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."),
853 N_("--strip-trailing-cr Strip trailing carriage return on input."),
855 N_("--binary Read and write data in binary mode."),
857 N_("-a --text Treat all files as text."),
859 N_("-c -C NUM --context[=NUM] Output NUM (default 3) lines of copied context.\n\
860 -u -U NUM --unified[=NUM] Output NUM (default 3) lines of unified context.\n\
861 --label LABEL Use LABEL instead of file name.\n\
862 -p --show-c-function Show which C function each change is in.\n\
863 -F RE --show-function-line=RE Show the most recent line matching RE."),
864 N_("-q --brief Output only whether files differ."),
865 N_("-e --ed Output an ed script."),
866 N_("--normal Output a normal diff."),
867 N_("-n --rcs Output an RCS format diff."),
868 N_("-y --side-by-side Output in two columns.\n\
869 -W NUM --width=NUM Output at most NUM (default 130) print columns.\n\
870 --left-column Output only the left column of common lines.\n\
871 --suppress-common-lines Do not output common lines."),
872 N_("-D NAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs."),
873 N_("--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT."),
874 N_("--line-format=LFMT Similar, but format all input lines with LFMT."),
875 N_("--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT."),
876 N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."),
877 N_(" GFMT may contain:\n\
878 %< lines from FILE1\n\
879 %> lines from FILE2\n\
880 %= lines common to FILE1 and FILE2\n\
881 %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
882 LETTERs are as follows for new group, lower case for old group:\n\
883 F first line number\n\
884 L last line number\n\
885 N number of lines = L-F+1\n\
888 N_(" LFMT may contain:\n\
889 %L contents of line\n\
890 %l contents of line, excluding any trailing newline\n\
891 %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
892 N_(" Either GFMT or LFMT may contain:\n\
894 %c'C' the single character C\n\
895 %c'\\OOO' the character with octal code OOO"),
897 N_("-l --paginate Pass the output through `pr' to paginate it."),
898 N_("-t --expand-tabs Expand tabs to spaces in output."),
899 N_("-T --initial-tab Make tabs line up by prepending a tab."),
900 N_("--tabsize=NUM Tab stops are every NUM (default 8) print columns."),
901 N_("--suppress-blank-empty Suppress space or tab before empty output lines."),
903 N_("-r --recursive Recursively compare any subdirectories found."),
904 N_("-N --new-file Treat absent files as empty."),
905 N_("--unidirectional-new-file Treat absent first files as empty."),
906 N_("-s --report-identical-files Report when two files are the same."),
907 N_("-x PAT --exclude=PAT Exclude files that match PAT."),
908 N_("-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE."),
909 N_("-S FILE --starting-file=FILE Start with FILE when comparing directories."),
910 N_("--from-file=FILE1 Compare FILE1 to all operands. FILE1 can be a directory."),
911 N_("--to-file=FILE2 Compare all operands to FILE2. FILE2 can be a directory."),
913 N_("--horizon-lines=NUM Keep NUM lines of the common prefix and suffix."),
914 N_("-d --minimal Try hard to find a smaller set of changes."),
915 N_("--speed-large-files Assume large files and many scattered small changes."),
917 N_("-v --version Output version info."),
918 N_("--help Output this help."),
920 N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
921 N_("If --from-file or --to-file is given, there are no restrictions on FILES."),
922 N_("If a FILE is `-', read standard input."),
923 N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
930 char const * const *p;
932 printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
934 for (p = option_help_msgid; *p; p++)
940 char const *msg = _(*p);
942 while ((nl = strchr (msg, '\n')))
944 int msglen = nl + 1 - msg;
945 printf (" %.*s", msglen, msg);
949 printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
952 emit_bug_reporting_address ();
955 /* Set VAR to VALUE, reporting an OPTION error if this is a
958 specify_value (char const **var, char const *value, char const *option)
960 if (*var && strcmp (*var, value) != 0)
962 error (0, 0, _("conflicting %s option value `%s'"), option, value);
963 try_help (NULL, NULL);
968 /* Set the output style to STYLE, diagnosing conflicts. */
970 specify_style (enum output_style style)
972 if (output_style != style)
974 if (output_style != OUTPUT_UNSPECIFIED)
975 try_help ("conflicting output style options", NULL);
976 output_style = style;
980 /* Set the last-modified time of *ST to be the current time. */
983 set_mtime_to_now (struct stat *st)
986 gettime (&STAT_TIMESPEC (st, st_mtim));
990 st->st_mtime = t.tv_sec;
991 # if defined STAT_TIMESPEC_NS
992 STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
993 # elif defined HAVE_STRUCT_STAT_ST_SPARE1
994 st->st_spare1 = t.tv_nsec / 1000;
999 /* Compare two files (or dirs) with parent comparison PARENT
1000 and names NAME0 and NAME1.
1001 (If PARENT is null, then the first name is just NAME0, etc.)
1002 This is self-contained; it opens the files and closes them.
1004 Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1005 different, EXIT_TROUBLE if there is a problem opening them. */
1008 compare_files (struct comparison const *parent,
1012 struct comparison cmp;
1013 #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1015 int status = EXIT_SUCCESS;
1020 /* If this is directory comparison, perhaps we have a file
1021 that exists only in one of the directories.
1022 If so, just print a message to that effect. */
1024 if (! ((name0 && name1)
1025 || (unidirectional_new_file && name1)
1028 char const *name = name0 ? name0 : name1;
1029 char const *dir = parent->file[!name0].name;
1031 /* See POSIX 1003.1-2001 for this format. */
1032 message ("Only in %s: %s\n", dir, name);
1034 /* Return EXIT_FAILURE so that diff_dirs will return
1035 EXIT_FAILURE ("some files differ"). */
1036 return EXIT_FAILURE;
1039 memset (cmp.file, 0, sizeof cmp.file);
1040 cmp.parent = parent;
1042 /* cmp.file[f].desc markers */
1043 #define NONEXISTENT (-1) /* nonexistent file */
1044 #define UNOPENED (-2) /* unopened file (e.g. directory) */
1045 #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1047 #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1049 cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
1050 cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
1052 /* Now record the full name of each file, including nonexistent ones. */
1063 cmp.file[0].name = name0;
1064 cmp.file[1].name = name1;
1068 cmp.file[0].name = free0
1069 = dir_file_pathname (parent->file[0].name, name0);
1070 cmp.file[1].name = free1
1071 = dir_file_pathname (parent->file[1].name, name1);
1074 /* Stat the files. */
1076 for (f = 0; f < 2; f++)
1078 if (cmp.file[f].desc != NONEXISTENT)
1080 if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1082 cmp.file[f].desc = cmp.file[0].desc;
1083 cmp.file[f].stat = cmp.file[0].stat;
1085 else if (STREQ (cmp.file[f].name, "-"))
1087 cmp.file[f].desc = STDIN_FILENO;
1088 if (O_BINARY && binary && ! isatty (STDIN_FILENO))
1089 xfreopen (NULL, "rb", stdin);
1090 if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1091 cmp.file[f].desc = ERRNO_ENCODE (errno);
1094 if (S_ISREG (cmp.file[f].stat.st_mode))
1096 off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
1098 cmp.file[f].desc = ERRNO_ENCODE (errno);
1100 cmp.file[f].stat.st_size =
1101 MAX (0, cmp.file[f].stat.st_size - pos);
1104 /* POSIX 1003.1-2001 requires current time for
1106 set_mtime_to_now (&cmp.file[f].stat);
1109 else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
1110 cmp.file[f].desc = ERRNO_ENCODE (errno);
1114 /* Mark files as nonexistent as needed for -N and -P, if they are
1115 inaccessible empty regular files (the kind of files that 'patch'
1116 creates to indicate nonexistent backups), or if they are
1117 top-level files that do not exist but their counterparts do
1119 for (f = 0; f < 2; f++)
1120 if ((new_file || (f == 0 && unidirectional_new_file))
1121 && (cmp.file[f].desc == UNOPENED
1122 ? (S_ISREG (cmp.file[f].stat.st_mode)
1123 && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1124 && cmp.file[f].stat.st_size == 0)
1125 : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
1127 && cmp.file[1 - f].desc == UNOPENED)))
1128 cmp.file[f].desc = NONEXISTENT;
1130 for (f = 0; f < 2; f++)
1131 if (cmp.file[f].desc == NONEXISTENT)
1133 memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1134 cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1137 for (f = 0; f < 2; f++)
1139 int e = ERRNO_DECODE (cmp.file[f].desc);
1143 perror_with_name (cmp.file[f].name);
1144 status = EXIT_TROUBLE;
1148 if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1150 /* If one is a directory, and it was specified in the command line,
1151 use the file in that dir with the other file's basename. */
1153 int fnm_arg = DIR_P (0);
1154 int dir_arg = 1 - fnm_arg;
1155 char const *fnm = cmp.file[fnm_arg].name;
1156 char const *dir = cmp.file[dir_arg].name;
1157 char const *filename = cmp.file[dir_arg].name = free0
1158 = dir_file_pathname (dir, last_component (fnm));
1160 if (STREQ (fnm, "-"))
1161 fatal ("cannot compare `-' to a directory");
1163 if (stat (filename, &cmp.file[dir_arg].stat) != 0)
1165 perror_with_name (filename);
1166 status = EXIT_TROUBLE;
1170 if (status != EXIT_SUCCESS)
1172 /* One of the files should exist but does not. */
1174 else if (cmp.file[0].desc == NONEXISTENT
1175 && cmp.file[1].desc == NONEXISTENT)
1177 /* Neither file "exists", so there's nothing to compare. */
1179 else if ((same_files
1180 = (cmp.file[0].desc != NONEXISTENT
1181 && cmp.file[1].desc != NONEXISTENT
1182 && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1183 && same_file_attributes (&cmp.file[0].stat,
1184 &cmp.file[1].stat)))
1185 && no_diff_means_no_output)
1187 /* The two named files are actually the same physical file.
1188 We know they are identical without actually reading them. */
1190 else if (DIR_P (0) & DIR_P (1))
1192 if (output_style == OUTPUT_IFDEF)
1193 fatal ("-D option not supported with directories");
1195 /* If both are directories, compare the files in them. */
1197 if (parent && !recursive)
1199 /* But don't compare dir contents one level down
1200 unless -r was specified.
1201 See POSIX 1003.1-2001 for this format. */
1202 message ("Common subdirectories: %s and %s\n",
1203 cmp.file[0].name, cmp.file[1].name);
1206 status = diff_dirs (&cmp, compare_files);
1208 else if ((DIR_P (0) | DIR_P (1))
1210 && (! S_ISREG (cmp.file[0].stat.st_mode)
1211 || ! S_ISREG (cmp.file[1].stat.st_mode))))
1213 if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1215 /* We have a subdirectory that exists only in one directory. */
1217 if ((DIR_P (0) | DIR_P (1))
1220 || (unidirectional_new_file
1221 && cmp.file[0].desc == NONEXISTENT)))
1222 status = diff_dirs (&cmp, compare_files);
1227 /* PARENT must be non-NULL here. */
1229 dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
1231 /* See POSIX 1003.1-2001 for this format. */
1232 message ("Only in %s: %s\n", dir, name0);
1234 status = EXIT_FAILURE;
1239 /* We have two files that are not to be compared. */
1241 /* See POSIX 1003.1-2001 for this format. */
1242 message5 ("File %s is a %s while file %s is a %s\n",
1243 file_label[0] ? file_label[0] : cmp.file[0].name,
1244 file_type (&cmp.file[0].stat),
1245 file_label[1] ? file_label[1] : cmp.file[1].name,
1246 file_type (&cmp.file[1].stat));
1248 /* This is a difference. */
1249 status = EXIT_FAILURE;
1252 else if (files_can_be_treated_as_binary
1253 && S_ISREG (cmp.file[0].stat.st_mode)
1254 && S_ISREG (cmp.file[1].stat.st_mode)
1255 && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size)
1257 message ("Files %s and %s differ\n",
1258 file_label[0] ? file_label[0] : cmp.file[0].name,
1259 file_label[1] ? file_label[1] : cmp.file[1].name);
1260 status = EXIT_FAILURE;
1264 /* Both exist and neither is a directory. */
1266 /* Open the files and record their descriptors. */
1268 int oflags = O_RDONLY | (binary ? O_BINARY : 0);
1270 if (cmp.file[0].desc == UNOPENED)
1271 if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
1273 perror_with_name (cmp.file[0].name);
1274 status = EXIT_TROUBLE;
1276 if (cmp.file[1].desc == UNOPENED)
1279 cmp.file[1].desc = cmp.file[0].desc;
1280 else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
1282 perror_with_name (cmp.file[1].name);
1283 status = EXIT_TROUBLE;
1287 /* Compare the files, if no error was found. */
1289 if (status == EXIT_SUCCESS)
1290 status = diff_2_files (&cmp);
1292 /* Close the file descriptors. */
1294 if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1296 perror_with_name (cmp.file[0].name);
1297 status = EXIT_TROUBLE;
1299 if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1300 && close (cmp.file[1].desc) != 0)
1302 perror_with_name (cmp.file[1].name);
1303 status = EXIT_TROUBLE;
1307 /* Now the comparison has been done, if no error prevented it,
1308 and STATUS is the value this function will return. */
1310 if (status == EXIT_SUCCESS)
1312 if (report_identical_files && !DIR_P (0))
1313 message ("Files %s and %s are identical\n",
1314 file_label[0] ? file_label[0] : cmp.file[0].name,
1315 file_label[1] ? file_label[1] : cmp.file[1].name);
1319 /* Flush stdout so that the user sees differences immediately.
1320 This can hurt performance, unfortunately. */
1321 if (fflush (stdout) != 0)
1322 pfatal_with_name (_("standard output"));