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 bindtextdomain (PACKAGE, LOCALEDIR);
280 textdomain (PACKAGE);
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 ();
287 /* Decode the options. */
289 while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
306 if (! ISDIGIT (prev))
308 else if (LIN_MAX / 10 < ocontext
309 || ((ocontext = 10 * ocontext + c - '0') < 0))
318 if (ignore_white_space < IGNORE_SPACE_CHANGE)
319 ignore_white_space = IGNORE_SPACE_CHANGE;
323 ignore_blank_lines = true;
331 numval = strtoumax (optarg, &numend, 10);
333 try_help ("invalid context length `%s'", optarg);
334 if (LIN_MAX < numval)
340 specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
341 if (context < numval)
343 explicit_context = true;
348 specify_style (OUTPUT_CONTEXT);
358 specify_style (OUTPUT_IFDEF);
360 static char const C_ifdef_group_formats[] =
361 "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
362 char *b = xmalloc (sizeof C_ifdef_group_formats
363 + 7 * strlen (optarg) - 14 /* 7*"%s" */
364 - 8 /* 5*"%%" + 3*"%c" */);
365 sprintf (b, C_ifdef_group_formats,
369 optarg, optarg, optarg);
370 for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
372 specify_value (&group_format[i], b, "-D");
379 specify_style (OUTPUT_ED);
383 if (ignore_white_space < IGNORE_TAB_EXPANSION)
384 ignore_white_space = IGNORE_TAB_EXPANSION;
388 specify_style (OUTPUT_FORWARD_ED);
392 add_regexp (&function_regexp_list, optarg);
396 /* Split the files into chunks for faster processing.
397 Usually does not change the result.
399 This currently has no effect. */
403 speed_large_files = true;
411 add_regexp (&ignore_regexp_list, optarg);
416 try_help ("pagination not supported on this host", NULL);
419 /* Pagination requires forking and waiting, and
420 System V fork+wait does not work if SIGCHLD is ignored. */
421 signal (SIGCHLD, SIG_DFL);
427 file_label[0] = optarg;
428 else if (!file_label[1])
429 file_label[1] = optarg;
431 fatal ("too many file label options");
435 specify_style (OUTPUT_RCS);
443 show_c_function = true;
444 add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
448 unidirectional_new_file = true;
460 report_identical_files = true;
464 specify_value (&starting_file, optarg, "-S");
476 specify_style (OUTPUT_UNIFIED);
482 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, PACKAGE_VERSION,
483 AUTHORS, (char *) NULL);
488 ignore_white_space = IGNORE_ALL_SPACE;
492 add_exclude (excluded, optarg, exclude_options ());
496 if (add_exclude_file (add_exclude, excluded, optarg,
497 exclude_options (), '\n'))
498 pfatal_with_name (optarg);
502 specify_style (OUTPUT_SDIFF);
506 numval = strtoumax (optarg, &numend, 10);
507 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
508 try_help ("invalid width `%s'", optarg);
512 fatal ("conflicting width options");
520 if (! isatty (STDOUT_FILENO))
521 xfreopen (NULL, "wb", stdout);
525 case FROM_FILE_OPTION:
526 specify_value (&from_file, optarg, "--from-file");
534 case HORIZON_LINES_OPTION:
535 numval = strtoumax (optarg, &numend, 10);
537 try_help ("invalid horizon length `%s'", optarg);
538 horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
541 case IGNORE_FILE_NAME_CASE_OPTION:
542 ignore_file_name_case = true;
545 case INHIBIT_HUNK_MERGE_OPTION:
546 /* This option is obsolete, but accept it for backward
550 case LEFT_COLUMN_OPTION:
554 case LINE_FORMAT_OPTION:
555 specify_style (OUTPUT_IFDEF);
556 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
557 specify_value (&line_format[i], optarg, "--line-format");
560 case NO_IGNORE_FILE_NAME_CASE_OPTION:
561 ignore_file_name_case = false;
565 specify_style (OUTPUT_NORMAL);
568 case SDIFF_MERGE_ASSIST_OPTION:
569 specify_style (OUTPUT_SDIFF);
570 sdiff_merge_assist = true;
573 case STRIP_TRAILING_CR_OPTION:
574 strip_trailing_cr = true;
577 case SUPPRESS_BLANK_EMPTY_OPTION:
578 suppress_blank_empty = true;
581 case SUPPRESS_COMMON_LINES_OPTION:
582 suppress_common_lines = true;
586 numval = strtoumax (optarg, &numend, 10);
587 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
588 try_help ("invalid tabsize `%s'", optarg);
589 if (tabsize != numval)
592 fatal ("conflicting tabsize options");
598 specify_value (&to_file, optarg, "--to-file");
601 case UNCHANGED_LINE_FORMAT_OPTION:
602 case OLD_LINE_FORMAT_OPTION:
603 case NEW_LINE_FORMAT_OPTION:
604 specify_style (OUTPUT_IFDEF);
605 c -= UNCHANGED_LINE_FORMAT_OPTION;
606 specify_value (&line_format[c], optarg, line_format_option[c]);
609 case UNCHANGED_GROUP_FORMAT_OPTION:
610 case OLD_GROUP_FORMAT_OPTION:
611 case NEW_GROUP_FORMAT_OPTION:
612 case CHANGED_GROUP_FORMAT_OPTION:
613 specify_style (OUTPUT_IFDEF);
614 c -= UNCHANGED_GROUP_FORMAT_OPTION;
615 specify_value (&group_format[c], optarg, group_format_option[c]);
619 try_help (NULL, NULL);
624 if (output_style == OUTPUT_UNSPECIFIED)
628 specify_style (OUTPUT_CONTEXT);
633 specify_style (OUTPUT_NORMAL);
636 if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
638 #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
639 || defined HAVE_STRUCT_STAT_ST_SPARE1)
640 time_format = "%Y-%m-%d %H:%M:%S.%N %z";
642 time_format = "%Y-%m-%d %H:%M:%S %z";
647 /* See POSIX 1003.1-2001 for this format. */
648 time_format = "%a %b %e %T %Y";
652 && (output_style == OUTPUT_CONTEXT
653 || output_style == OUTPUT_UNIFIED)
654 && (context < ocontext
655 || (ocontext < context && ! explicit_context)))
664 /* Maximize first the half line width, and then the gutter width,
665 according to the following constraints:
667 1. Two half lines plus a gutter must fit in a line.
668 2. If the half line width is nonzero:
669 a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
670 b. If tabs are not expanded to spaces,
671 a half line plus a gutter is an integral number of tabs,
672 so that tabs in the right column line up. */
674 intmax_t t = expand_tabs ? 1 : tabsize;
676 intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t) * t;
677 sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
678 sdiff_column2_offset = sdiff_half_width ? off : w;
681 /* Make the horizon at least as large as the context, so that
682 shift_boundaries has more freedom to shift the first and last hunks. */
683 if (horizon_lines < context)
684 horizon_lines = context;
686 summarize_regexp_list (&function_regexp_list);
687 summarize_regexp_list (&ignore_regexp_list);
689 if (output_style == OUTPUT_IFDEF)
691 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
693 line_format[i] = "%l\n";
694 if (!group_format[OLD])
696 = group_format[CHANGED] ? group_format[CHANGED] : "%<";
697 if (!group_format[NEW])
699 = group_format[CHANGED] ? group_format[CHANGED] : "%>";
700 if (!group_format[UNCHANGED])
701 group_format[UNCHANGED] = "%=";
702 if (!group_format[CHANGED])
703 group_format[CHANGED] = concat (group_format[OLD],
704 group_format[NEW], "");
707 no_diff_means_no_output =
708 (output_style == OUTPUT_IFDEF ?
709 (!*group_format[UNCHANGED]
710 || (STREQ (group_format[UNCHANGED], "%=")
711 && !*line_format[UNCHANGED]))
712 : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
714 files_can_be_treated_as_binary =
716 & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
717 | (ignore_regexp_list.regexps || ignore_white_space)));
719 switch_string = option_list (argv + 1, optind - 1);
724 fatal ("--from-file and --to-file both specified");
726 for (; optind < argc; optind++)
728 int status = compare_files (NULL, from_file, argv[optind]);
729 if (exit_status < status)
730 exit_status = status;
736 for (; optind < argc; optind++)
738 int status = compare_files (NULL, argv[optind], to_file);
739 if (exit_status < status)
740 exit_status = status;
744 if (argc - optind != 2)
746 if (argc - optind < 2)
747 try_help ("missing operand after `%s'", argv[argc - 1]);
749 try_help ("extra operand `%s'", argv[optind + 2]);
752 exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
756 /* Print any messages that were saved up for last. */
757 print_message_queue ();
764 /* Append to REGLIST the regexp PATTERN. */
767 add_regexp (struct regexp_list *reglist, char const *pattern)
769 size_t patlen = strlen (pattern);
770 char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
773 error (0, 0, "%s: %s", pattern, m);
776 char *regexps = reglist->regexps;
777 size_t len = reglist->len;
778 bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
779 size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
780 size_t size = reglist->size;
788 while (size <= newlen);
790 reglist->size = size;
791 reglist->regexps = regexps = xrealloc (regexps, size);
793 if (multiple_regexps)
795 regexps[len++] = '\\';
796 regexps[len++] = '|';
798 memcpy (regexps + len, pattern, patlen + 1);
802 /* Ensure that REGLIST represents the disjunction of its regexps.
803 This is done here, rather than earlier, to avoid O(N^2) behavior. */
806 summarize_regexp_list (struct regexp_list *reglist)
808 if (reglist->regexps)
810 /* At least one regexp was specified. Allocate a fastmap for it. */
811 reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
812 if (reglist->multiple_regexps)
814 /* Compile the disjunction of the regexps.
815 (If just one regexp was specified, it is already compiled.) */
816 char const *m = re_compile_pattern (reglist->regexps, reglist->len,
819 error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
825 try_help (char const *reason_msgid, char const *operand)
828 error (0, 0, _(reason_msgid), operand);
829 error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
838 fatal ("write failed");
839 else if (fclose (stdout) != 0)
840 pfatal_with_name (_("standard output"));
843 static char const * const option_help_msgid[] = {
844 N_("Compare files line by line."),
846 N_("-i --ignore-case Ignore case differences in file contents."),
847 N_("--ignore-file-name-case Ignore case when comparing file names."),
848 N_("--no-ignore-file-name-case Consider case when comparing file names."),
849 N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."),
850 N_("-b --ignore-space-change Ignore changes in the amount of white space."),
851 N_("-w --ignore-all-space Ignore all white space."),
852 N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."),
853 N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."),
854 N_("--strip-trailing-cr Strip trailing carriage return on input."),
856 N_("--binary Read and write data in binary mode."),
858 N_("-a --text Treat all files as text."),
860 N_("-c -C NUM --context[=NUM] Output NUM (default 3) lines of copied context.\n\
861 -u -U NUM --unified[=NUM] Output NUM (default 3) lines of unified context.\n\
862 --label LABEL Use LABEL instead of file name.\n\
863 -p --show-c-function Show which C function each change is in.\n\
864 -F RE --show-function-line=RE Show the most recent line matching RE."),
865 N_("-q --brief Output only whether files differ."),
866 N_("-e --ed Output an ed script."),
867 N_("--normal Output a normal diff."),
868 N_("-n --rcs Output an RCS format diff."),
869 N_("-y --side-by-side Output in two columns.\n\
870 -W NUM --width=NUM Output at most NUM (default 130) print columns.\n\
871 --left-column Output only the left column of common lines.\n\
872 --suppress-common-lines Do not output common lines."),
873 N_("-D NAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs."),
874 N_("--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT."),
875 N_("--line-format=LFMT Similar, but format all input lines with LFMT."),
876 N_("--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT."),
877 N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."),
878 N_(" GFMT may contain:\n\
879 %< lines from FILE1\n\
880 %> lines from FILE2\n\
881 %= lines common to FILE1 and FILE2\n\
882 %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
883 LETTERs are as follows for new group, lower case for old group:\n\
884 F first line number\n\
885 L last line number\n\
886 N number of lines = L-F+1\n\
889 N_(" LFMT may contain:\n\
890 %L contents of line\n\
891 %l contents of line, excluding any trailing newline\n\
892 %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
893 N_(" Either GFMT or LFMT may contain:\n\
895 %c'C' the single character C\n\
896 %c'\\OOO' the character with octal code OOO"),
898 N_("-l --paginate Pass the output through `pr' to paginate it."),
899 N_("-t --expand-tabs Expand tabs to spaces in output."),
900 N_("-T --initial-tab Make tabs line up by prepending a tab."),
901 N_("--tabsize=NUM Tab stops are every NUM (default 8) print columns."),
902 N_("--suppress-blank-empty Suppress space or tab before empty output lines."),
904 N_("-r --recursive Recursively compare any subdirectories found."),
905 N_("-N --new-file Treat absent files as empty."),
906 N_("--unidirectional-new-file Treat absent first files as empty."),
907 N_("-s --report-identical-files Report when two files are the same."),
908 N_("-x PAT --exclude=PAT Exclude files that match PAT."),
909 N_("-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE."),
910 N_("-S FILE --starting-file=FILE Start with FILE when comparing directories."),
911 N_("--from-file=FILE1 Compare FILE1 to all operands. FILE1 can be a directory."),
912 N_("--to-file=FILE2 Compare all operands to FILE2. FILE2 can be a directory."),
914 N_("--horizon-lines=NUM Keep NUM lines of the common prefix and suffix."),
915 N_("-d --minimal Try hard to find a smaller set of changes."),
916 N_("--speed-large-files Assume large files and many scattered small changes."),
918 N_("-v --version Output version info."),
919 N_("--help Output this help."),
921 N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
922 N_("If --from-file or --to-file is given, there are no restrictions on FILES."),
923 N_("If a FILE is `-', read standard input."),
924 N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
931 char const * const *p;
933 printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
935 for (p = option_help_msgid; *p; p++)
941 char const *msg = _(*p);
943 while ((nl = strchr (msg, '\n')))
945 int msglen = nl + 1 - msg;
946 printf (" %.*s", msglen, msg);
950 printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
953 emit_bug_reporting_address ();
956 /* Set VAR to VALUE, reporting an OPTION error if this is a
959 specify_value (char const **var, char const *value, char const *option)
961 if (*var && strcmp (*var, value) != 0)
963 error (0, 0, _("conflicting %s option value `%s'"), option, value);
964 try_help (NULL, NULL);
969 /* Set the output style to STYLE, diagnosing conflicts. */
971 specify_style (enum output_style style)
973 if (output_style != style)
975 if (output_style != OUTPUT_UNSPECIFIED)
976 try_help ("conflicting output style options", NULL);
977 output_style = style;
981 /* Set the last-modified time of *ST to be the current time. */
984 set_mtime_to_now (struct stat *st)
987 gettime (&STAT_TIMESPEC (st, st_mtim));
991 st->st_mtime = t.tv_sec;
992 # if defined STAT_TIMESPEC_NS
993 STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
994 # elif defined HAVE_STRUCT_STAT_ST_SPARE1
995 st->st_spare1 = t.tv_nsec / 1000;
1000 /* Compare two files (or dirs) with parent comparison PARENT
1001 and names NAME0 and NAME1.
1002 (If PARENT is null, then the first name is just NAME0, etc.)
1003 This is self-contained; it opens the files and closes them.
1005 Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1006 different, EXIT_TROUBLE if there is a problem opening them. */
1009 compare_files (struct comparison const *parent,
1013 struct comparison cmp;
1014 #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1016 int status = EXIT_SUCCESS;
1021 /* If this is directory comparison, perhaps we have a file
1022 that exists only in one of the directories.
1023 If so, just print a message to that effect. */
1025 if (! ((name0 && name1)
1026 || (unidirectional_new_file && name1)
1029 char const *name = name0 ? name0 : name1;
1030 char const *dir = parent->file[!name0].name;
1032 /* See POSIX 1003.1-2001 for this format. */
1033 message ("Only in %s: %s\n", dir, name);
1035 /* Return EXIT_FAILURE so that diff_dirs will return
1036 EXIT_FAILURE ("some files differ"). */
1037 return EXIT_FAILURE;
1040 memset (cmp.file, 0, sizeof cmp.file);
1041 cmp.parent = parent;
1043 /* cmp.file[f].desc markers */
1044 #define NONEXISTENT (-1) /* nonexistent file */
1045 #define UNOPENED (-2) /* unopened file (e.g. directory) */
1046 #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1048 #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1050 cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
1051 cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
1053 /* Now record the full name of each file, including nonexistent ones. */
1064 cmp.file[0].name = name0;
1065 cmp.file[1].name = name1;
1069 cmp.file[0].name = free0
1070 = dir_file_pathname (parent->file[0].name, name0);
1071 cmp.file[1].name = free1
1072 = dir_file_pathname (parent->file[1].name, name1);
1075 /* Stat the files. */
1077 for (f = 0; f < 2; f++)
1079 if (cmp.file[f].desc != NONEXISTENT)
1081 if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1083 cmp.file[f].desc = cmp.file[0].desc;
1084 cmp.file[f].stat = cmp.file[0].stat;
1086 else if (STREQ (cmp.file[f].name, "-"))
1088 cmp.file[f].desc = STDIN_FILENO;
1089 if (O_BINARY && binary && ! isatty (STDIN_FILENO))
1090 xfreopen (NULL, "rb", stdin);
1091 if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1092 cmp.file[f].desc = ERRNO_ENCODE (errno);
1095 if (S_ISREG (cmp.file[f].stat.st_mode))
1097 off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
1099 cmp.file[f].desc = ERRNO_ENCODE (errno);
1101 cmp.file[f].stat.st_size =
1102 MAX (0, cmp.file[f].stat.st_size - pos);
1105 /* POSIX 1003.1-2001 requires current time for
1107 set_mtime_to_now (&cmp.file[f].stat);
1110 else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
1111 cmp.file[f].desc = ERRNO_ENCODE (errno);
1115 /* Mark files as nonexistent as needed for -N and -P, if they are
1116 inaccessible empty regular files (the kind of files that 'patch'
1117 creates to indicate nonexistent backups), or if they are
1118 top-level files that do not exist but their counterparts do
1120 for (f = 0; f < 2; f++)
1121 if ((new_file || (f == 0 && unidirectional_new_file))
1122 && (cmp.file[f].desc == UNOPENED
1123 ? (S_ISREG (cmp.file[f].stat.st_mode)
1124 && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1125 && cmp.file[f].stat.st_size == 0)
1126 : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
1128 && cmp.file[1 - f].desc == UNOPENED)))
1129 cmp.file[f].desc = NONEXISTENT;
1131 for (f = 0; f < 2; f++)
1132 if (cmp.file[f].desc == NONEXISTENT)
1134 memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1135 cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1138 for (f = 0; f < 2; f++)
1140 int e = ERRNO_DECODE (cmp.file[f].desc);
1144 perror_with_name (cmp.file[f].name);
1145 status = EXIT_TROUBLE;
1149 if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1151 /* If one is a directory, and it was specified in the command line,
1152 use the file in that dir with the other file's basename. */
1154 int fnm_arg = DIR_P (0);
1155 int dir_arg = 1 - fnm_arg;
1156 char const *fnm = cmp.file[fnm_arg].name;
1157 char const *dir = cmp.file[dir_arg].name;
1158 char const *filename = cmp.file[dir_arg].name = free0
1159 = dir_file_pathname (dir, last_component (fnm));
1161 if (STREQ (fnm, "-"))
1162 fatal ("cannot compare `-' to a directory");
1164 if (stat (filename, &cmp.file[dir_arg].stat) != 0)
1166 perror_with_name (filename);
1167 status = EXIT_TROUBLE;
1171 if (status != EXIT_SUCCESS)
1173 /* One of the files should exist but does not. */
1175 else if (cmp.file[0].desc == NONEXISTENT
1176 && cmp.file[1].desc == NONEXISTENT)
1178 /* Neither file "exists", so there's nothing to compare. */
1180 else if ((same_files
1181 = (cmp.file[0].desc != NONEXISTENT
1182 && cmp.file[1].desc != NONEXISTENT
1183 && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1184 && same_file_attributes (&cmp.file[0].stat,
1185 &cmp.file[1].stat)))
1186 && no_diff_means_no_output)
1188 /* The two named files are actually the same physical file.
1189 We know they are identical without actually reading them. */
1191 else if (DIR_P (0) & DIR_P (1))
1193 if (output_style == OUTPUT_IFDEF)
1194 fatal ("-D option not supported with directories");
1196 /* If both are directories, compare the files in them. */
1198 if (parent && !recursive)
1200 /* But don't compare dir contents one level down
1201 unless -r was specified.
1202 See POSIX 1003.1-2001 for this format. */
1203 message ("Common subdirectories: %s and %s\n",
1204 cmp.file[0].name, cmp.file[1].name);
1207 status = diff_dirs (&cmp, compare_files);
1209 else if ((DIR_P (0) | DIR_P (1))
1211 && (! S_ISREG (cmp.file[0].stat.st_mode)
1212 || ! S_ISREG (cmp.file[1].stat.st_mode))))
1214 if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1216 /* We have a subdirectory that exists only in one directory. */
1218 if ((DIR_P (0) | DIR_P (1))
1221 || (unidirectional_new_file
1222 && cmp.file[0].desc == NONEXISTENT)))
1223 status = diff_dirs (&cmp, compare_files);
1228 /* PARENT must be non-NULL here. */
1230 dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
1232 /* See POSIX 1003.1-2001 for this format. */
1233 message ("Only in %s: %s\n", dir, name0);
1235 status = EXIT_FAILURE;
1240 /* We have two files that are not to be compared. */
1242 /* See POSIX 1003.1-2001 for this format. */
1243 message5 ("File %s is a %s while file %s is a %s\n",
1244 file_label[0] ? file_label[0] : cmp.file[0].name,
1245 file_type (&cmp.file[0].stat),
1246 file_label[1] ? file_label[1] : cmp.file[1].name,
1247 file_type (&cmp.file[1].stat));
1249 /* This is a difference. */
1250 status = EXIT_FAILURE;
1253 else if (files_can_be_treated_as_binary
1254 && S_ISREG (cmp.file[0].stat.st_mode)
1255 && S_ISREG (cmp.file[1].stat.st_mode)
1256 && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size)
1258 message ("Files %s and %s differ\n",
1259 file_label[0] ? file_label[0] : cmp.file[0].name,
1260 file_label[1] ? file_label[1] : cmp.file[1].name);
1261 status = EXIT_FAILURE;
1265 /* Both exist and neither is a directory. */
1267 /* Open the files and record their descriptors. */
1269 int oflags = O_RDONLY | (binary ? O_BINARY : 0);
1271 if (cmp.file[0].desc == UNOPENED)
1272 if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
1274 perror_with_name (cmp.file[0].name);
1275 status = EXIT_TROUBLE;
1277 if (cmp.file[1].desc == UNOPENED)
1280 cmp.file[1].desc = cmp.file[0].desc;
1281 else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
1283 perror_with_name (cmp.file[1].name);
1284 status = EXIT_TROUBLE;
1288 /* Compare the files, if no error was found. */
1290 if (status == EXIT_SUCCESS)
1291 status = diff_2_files (&cmp);
1293 /* Close the file descriptors. */
1295 if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1297 perror_with_name (cmp.file[0].name);
1298 status = EXIT_TROUBLE;
1300 if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1301 && close (cmp.file[1].desc) != 0)
1303 perror_with_name (cmp.file[1].name);
1304 status = EXIT_TROUBLE;
1308 /* Now the comparison has been done, if no error prevented it,
1309 and STATUS is the value this function will return. */
1311 if (status == EXIT_SUCCESS)
1313 if (report_identical_files && !DIR_P (0))
1314 message ("Files %s and %s are identical\n",
1315 file_label[0] ? file_label[0] : cmp.file[0].name,
1316 file_label[1] ? file_label[1] : cmp.file[1].name);
1320 /* Flush stdout so that the user sees differences immediately.
1321 This can hurt performance, unfortunately. */
1322 if (fflush (stdout) != 0)
1323 pfatal_with_name (_("standard output"));