1 /* diff - compare files line by line
3 Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
4 2009-2011 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 <filenamecat.h>
31 #include <file-type.h>
34 #include <hard-locale.h>
38 #include <stat-time.h>
40 #include <version-etc.h>
44 /* The official name of this program (e.g., no `g' prefix). */
45 #define PROGRAM_NAME "diff"
48 proper_name ("Paul Eggert"), \
49 proper_name ("Mike Haertel"), \
50 proper_name ("David Hayes"), \
51 proper_name ("Richard Stallman"), \
52 proper_name ("Len Tower")
54 #ifndef GUTTER_WIDTH_MINIMUM
55 # define GUTTER_WIDTH_MINIMUM 3
60 char *regexps; /* chars representing disjunction of the regexps */
61 size_t len; /* chars used in `regexps' */
62 size_t size; /* size malloc'ed for `regexps'; 0 if not malloc'ed */
63 bool multiple_regexps;/* Does `regexps' represent a disjunction? */
64 struct re_pattern_buffer *buf;
67 static int compare_files (struct comparison const *, char const *, char const *);
68 static void add_regexp (struct regexp_list *, char const *);
69 static void summarize_regexp_list (struct regexp_list *);
70 static void specify_style (enum output_style);
71 static void specify_value (char const **, char const *, char const *);
72 static void try_help (char const *, char const *) __attribute__((noreturn));
73 static void check_stdout (void);
74 static void usage (void);
76 /* If comparing directories, compare their common subdirectories
78 static bool recursive;
80 /* In context diffs, show previous lines that match these regexps. */
81 static struct regexp_list function_regexp_list;
83 /* Ignore changes affecting only lines that match these regexps. */
84 static struct regexp_list ignore_regexp_list;
87 /* Use binary I/O when reading and writing data (--binary).
88 On POSIX hosts, this has no effect. */
91 enum { binary = true };
94 /* When comparing directories, if a file appears only in one
95 directory, treat it as present but empty in the other (-N).
96 Then `patch' would create the file with appropriate contents. */
99 /* When comparing directories, if a file appears only in the second
100 directory of the two, treat it as present but empty in the other
101 (--unidirectional-new-file).
102 Then `patch' would create the file with appropriate contents. */
103 static bool unidirectional_new_file;
105 /* Report files compared that are the same (-s).
106 Normally nothing is output when that happens. */
107 static bool report_identical_files;
109 static char const shortopts[] =
110 "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
112 /* Values for long options that do not have single-letter equivalents. */
115 BINARY_OPTION = CHAR_MAX + 1,
118 HORIZON_LINES_OPTION,
119 IGNORE_FILE_NAME_CASE_OPTION,
120 INHIBIT_HUNK_MERGE_OPTION,
123 NO_IGNORE_FILE_NAME_CASE_OPTION,
125 SDIFF_MERGE_ASSIST_OPTION,
126 STRIP_TRAILING_CR_OPTION,
127 SUPPRESS_BLANK_EMPTY_OPTION,
128 SUPPRESS_COMMON_LINES_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
144 static char const group_format_option[][sizeof "--unchanged-group-format"] =
146 "--unchanged-group-format",
147 "--old-group-format",
148 "--new-group-format",
149 "--changed-group-format"
152 static char const line_format_option[][sizeof "--unchanged-line-format"] =
154 "--unchanged-line-format",
159 static struct option const longopts[] =
161 {"binary", 0, 0, BINARY_OPTION},
162 {"brief", 0, 0, 'q'},
163 {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
164 {"context", 2, 0, 'C'},
166 {"exclude", 1, 0, 'x'},
167 {"exclude-from", 1, 0, 'X'},
168 {"expand-tabs", 0, 0, 't'},
169 {"forward-ed", 0, 0, 'f'},
170 {"from-file", 1, 0, FROM_FILE_OPTION},
171 {"help", 0, 0, HELP_OPTION},
172 {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
173 {"ifdef", 1, 0, 'D'},
174 {"ignore-all-space", 0, 0, 'w'},
175 {"ignore-blank-lines", 0, 0, 'B'},
176 {"ignore-case", 0, 0, 'i'},
177 {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
178 {"ignore-matching-lines", 1, 0, 'I'},
179 {"ignore-space-change", 0, 0, 'b'},
180 {"ignore-tab-expansion", 0, 0, 'E'},
181 {"ignore-trailing-space", 0, 0, 'Z'},
182 {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
183 {"initial-tab", 0, 0, 'T'},
184 {"label", 1, 0, 'L'},
185 {"left-column", 0, 0, LEFT_COLUMN_OPTION},
186 {"line-format", 1, 0, LINE_FORMAT_OPTION},
187 {"minimal", 0, 0, 'd'},
188 {"new-file", 0, 0, 'N'},
189 {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
190 {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
191 {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
192 {"normal", 0, 0, NORMAL_OPTION},
193 {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
194 {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
195 {"paginate", 0, 0, 'l'},
197 {"recursive", 0, 0, 'r'},
198 {"report-identical-files", 0, 0, 's'},
199 {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
200 {"show-c-function", 0, 0, 'p'},
201 {"show-function-line", 1, 0, 'F'},
202 {"side-by-side", 0, 0, 'y'},
203 {"speed-large-files", 0, 0, 'H'},
204 {"starting-file", 1, 0, 'S'},
205 {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
206 {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
207 {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
208 {"tabsize", 1, 0, TABSIZE_OPTION},
210 {"to-file", 1, 0, TO_FILE_OPTION},
211 {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
212 {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
213 {"unidirectional-new-file", 0, 0, 'P'},
214 {"unified", 2, 0, 'U'},
215 {"version", 0, 0, 'v'},
216 {"width", 1, 0, 'W'},
220 /* Return a string containing the command options with which diff was invoked.
221 Spaces appear between what were separate ARGV-elements.
222 There is a space at the beginning but none at the end.
223 If there were no options, the result is an empty string.
225 Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
226 the length of that vector. */
229 option_list (char **optionvec, int count)
236 for (i = 0; i < count; i++)
237 size += 1 + shell_quote_length (optionvec[i]);
239 p = result = xmalloc (size);
241 for (i = 0; i < count; i++)
244 p = shell_quote_copy (p, optionvec[i]);
252 /* Return an option value suitable for add_exclude. */
255 exclude_options (void)
257 return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
261 main (int argc, char **argv)
263 int exit_status = EXIT_SUCCESS;
268 bool explicit_context = false;
270 bool show_c_function = false;
271 char const *from_file = NULL;
272 char const *to_file = NULL;
276 /* Do our initializations. */
277 exit_failure = EXIT_TROUBLE;
278 initialize_main (&argc, &argv);
279 set_program_name (argv[0]);
280 setlocale (LC_ALL, "");
281 textdomain (PACKAGE);
283 function_regexp_list.buf = &function_regexp;
284 ignore_regexp_list.buf = &ignore_regexp;
285 re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
286 excluded = new_exclude ();
288 /* Decode the options. */
290 while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
307 if (! ISDIGIT (prev))
309 else if (LIN_MAX / 10 < ocontext
310 || ((ocontext = 10 * ocontext + c - '0') < 0))
319 if (ignore_white_space < IGNORE_SPACE_CHANGE)
320 ignore_white_space = IGNORE_SPACE_CHANGE;
324 if (ignore_white_space < IGNORE_SPACE_CHANGE)
325 ignore_white_space |= IGNORE_TRAILING_SPACE;
329 ignore_blank_lines = true;
337 numval = strtoumax (optarg, &numend, 10);
339 try_help ("invalid context length `%s'", optarg);
340 if (LIN_MAX < numval)
346 specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
347 if (context < numval)
349 explicit_context = true;
354 specify_style (OUTPUT_CONTEXT);
364 specify_style (OUTPUT_IFDEF);
366 static char const C_ifdef_group_formats[] =
367 "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
368 char *b = xmalloc (sizeof C_ifdef_group_formats
369 + 7 * strlen (optarg) - 14 /* 7*"%s" */
370 - 8 /* 5*"%%" + 3*"%c" */);
371 sprintf (b, C_ifdef_group_formats,
375 optarg, optarg, optarg);
376 for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
378 specify_value (&group_format[i], b, "-D");
385 specify_style (OUTPUT_ED);
389 if (ignore_white_space < IGNORE_SPACE_CHANGE)
390 ignore_white_space |= IGNORE_TAB_EXPANSION;
394 specify_style (OUTPUT_FORWARD_ED);
398 add_regexp (&function_regexp_list, optarg);
402 /* Split the files into chunks for faster processing.
403 Usually does not change the result.
405 This currently has no effect. */
409 speed_large_files = true;
417 add_regexp (&ignore_regexp_list, optarg);
422 try_help ("pagination not supported on this host", NULL);
425 /* Pagination requires forking and waiting, and
426 System V fork+wait does not work if SIGCHLD is ignored. */
427 signal (SIGCHLD, SIG_DFL);
433 file_label[0] = optarg;
434 else if (!file_label[1])
435 file_label[1] = optarg;
437 fatal ("too many file label options");
441 specify_style (OUTPUT_RCS);
449 show_c_function = true;
450 add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
454 unidirectional_new_file = true;
466 report_identical_files = true;
470 specify_value (&starting_file, optarg, "-S");
482 specify_style (OUTPUT_UNIFIED);
488 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
489 AUTHORS, (char *) NULL);
494 ignore_white_space = IGNORE_ALL_SPACE;
498 add_exclude (excluded, optarg, exclude_options ());
502 if (add_exclude_file (add_exclude, excluded, optarg,
503 exclude_options (), '\n'))
504 pfatal_with_name (optarg);
508 specify_style (OUTPUT_SDIFF);
512 numval = strtoumax (optarg, &numend, 10);
513 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
514 try_help ("invalid width `%s'", optarg);
518 fatal ("conflicting width options");
526 if (! isatty (STDOUT_FILENO))
527 xfreopen (NULL, "wb", stdout);
531 case FROM_FILE_OPTION:
532 specify_value (&from_file, optarg, "--from-file");
540 case HORIZON_LINES_OPTION:
541 numval = strtoumax (optarg, &numend, 10);
543 try_help ("invalid horizon length `%s'", optarg);
544 horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
547 case IGNORE_FILE_NAME_CASE_OPTION:
548 ignore_file_name_case = true;
551 case INHIBIT_HUNK_MERGE_OPTION:
552 /* This option is obsolete, but accept it for backward
556 case LEFT_COLUMN_OPTION:
560 case LINE_FORMAT_OPTION:
561 specify_style (OUTPUT_IFDEF);
562 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
563 specify_value (&line_format[i], optarg, "--line-format");
566 case NO_IGNORE_FILE_NAME_CASE_OPTION:
567 ignore_file_name_case = false;
571 specify_style (OUTPUT_NORMAL);
574 case SDIFF_MERGE_ASSIST_OPTION:
575 specify_style (OUTPUT_SDIFF);
576 sdiff_merge_assist = true;
579 case STRIP_TRAILING_CR_OPTION:
580 strip_trailing_cr = true;
583 case SUPPRESS_BLANK_EMPTY_OPTION:
584 suppress_blank_empty = true;
587 case SUPPRESS_COMMON_LINES_OPTION:
588 suppress_common_lines = true;
592 numval = strtoumax (optarg, &numend, 10);
593 if (! (0 < numval && numval <= SIZE_MAX) || *numend)
594 try_help ("invalid tabsize `%s'", optarg);
595 if (tabsize != numval)
598 fatal ("conflicting tabsize options");
604 specify_value (&to_file, optarg, "--to-file");
607 case UNCHANGED_LINE_FORMAT_OPTION:
608 case OLD_LINE_FORMAT_OPTION:
609 case NEW_LINE_FORMAT_OPTION:
610 specify_style (OUTPUT_IFDEF);
611 c -= UNCHANGED_LINE_FORMAT_OPTION;
612 specify_value (&line_format[c], optarg, line_format_option[c]);
615 case UNCHANGED_GROUP_FORMAT_OPTION:
616 case OLD_GROUP_FORMAT_OPTION:
617 case NEW_GROUP_FORMAT_OPTION:
618 case CHANGED_GROUP_FORMAT_OPTION:
619 specify_style (OUTPUT_IFDEF);
620 c -= UNCHANGED_GROUP_FORMAT_OPTION;
621 specify_value (&group_format[c], optarg, group_format_option[c]);
625 try_help (NULL, NULL);
630 if (output_style == OUTPUT_UNSPECIFIED)
634 specify_style (OUTPUT_CONTEXT);
639 specify_style (OUTPUT_NORMAL);
642 if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
644 #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
645 || defined HAVE_STRUCT_STAT_ST_SPARE1)
646 time_format = "%Y-%m-%d %H:%M:%S.%N %z";
648 time_format = "%Y-%m-%d %H:%M:%S %z";
653 /* See POSIX 1003.1-2001 for this format. */
654 time_format = "%a %b %e %T %Y";
658 && (output_style == OUTPUT_CONTEXT
659 || output_style == OUTPUT_UNIFIED)
660 && (context < ocontext
661 || (ocontext < context && ! explicit_context)))
670 /* Maximize first the half line width, and then the gutter width,
671 according to the following constraints:
673 1. Two half lines plus a gutter must fit in a line.
674 2. If the half line width is nonzero:
675 a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
676 b. If tabs are not expanded to spaces,
677 a half line plus a gutter is an integral number of tabs,
678 so that tabs in the right column line up. */
680 intmax_t t = expand_tabs ? 1 : tabsize;
682 intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t) * t;
683 sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
684 sdiff_column2_offset = sdiff_half_width ? off : w;
687 /* Make the horizon at least as large as the context, so that
688 shift_boundaries has more freedom to shift the first and last hunks. */
689 if (horizon_lines < context)
690 horizon_lines = context;
692 summarize_regexp_list (&function_regexp_list);
693 summarize_regexp_list (&ignore_regexp_list);
695 if (output_style == OUTPUT_IFDEF)
697 for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
699 line_format[i] = "%l\n";
700 if (!group_format[OLD])
702 = group_format[CHANGED] ? group_format[CHANGED] : "%<";
703 if (!group_format[NEW])
705 = group_format[CHANGED] ? group_format[CHANGED] : "%>";
706 if (!group_format[UNCHANGED])
707 group_format[UNCHANGED] = "%=";
708 if (!group_format[CHANGED])
709 group_format[CHANGED] = concat (group_format[OLD],
710 group_format[NEW], "");
713 no_diff_means_no_output =
714 (output_style == OUTPUT_IFDEF ?
715 (!*group_format[UNCHANGED]
716 || (STREQ (group_format[UNCHANGED], "%=")
717 && !*line_format[UNCHANGED]))
718 : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
720 files_can_be_treated_as_binary =
722 & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
723 | (ignore_regexp_list.regexps || ignore_white_space)));
725 switch_string = option_list (argv + 1, optind - 1);
730 fatal ("--from-file and --to-file both specified");
732 for (; optind < argc; optind++)
734 int status = compare_files (NULL, from_file, argv[optind]);
735 if (exit_status < status)
736 exit_status = status;
742 for (; optind < argc; optind++)
744 int status = compare_files (NULL, argv[optind], to_file);
745 if (exit_status < status)
746 exit_status = status;
750 if (argc - optind != 2)
752 if (argc - optind < 2)
753 try_help ("missing operand after `%s'", argv[argc - 1]);
755 try_help ("extra operand `%s'", argv[optind + 2]);
758 exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
762 /* Print any messages that were saved up for last. */
763 print_message_queue ();
770 /* Append to REGLIST the regexp PATTERN. */
773 add_regexp (struct regexp_list *reglist, char const *pattern)
775 size_t patlen = strlen (pattern);
776 char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
779 error (0, 0, "%s: %s", pattern, m);
782 char *regexps = reglist->regexps;
783 size_t len = reglist->len;
784 bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
785 size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
786 size_t size = reglist->size;
794 while (size <= newlen);
796 reglist->size = size;
797 reglist->regexps = regexps = xrealloc (regexps, size);
799 if (multiple_regexps)
801 regexps[len++] = '\\';
802 regexps[len++] = '|';
804 memcpy (regexps + len, pattern, patlen + 1);
808 /* Ensure that REGLIST represents the disjunction of its regexps.
809 This is done here, rather than earlier, to avoid O(N^2) behavior. */
812 summarize_regexp_list (struct regexp_list *reglist)
814 if (reglist->regexps)
816 /* At least one regexp was specified. Allocate a fastmap for it. */
817 reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
818 if (reglist->multiple_regexps)
820 /* Compile the disjunction of the regexps.
821 (If just one regexp was specified, it is already compiled.) */
822 char const *m = re_compile_pattern (reglist->regexps, reglist->len,
825 error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
831 try_help (char const *reason_msgid, char const *operand)
834 error (0, 0, _(reason_msgid), operand);
835 error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
844 fatal ("write failed");
845 else if (fclose (stdout) != 0)
846 pfatal_with_name (_("standard output"));
849 static char const * const option_help_msgid[] = {
850 N_(" --normal output a normal diff (the default)"),
851 N_("-q, --brief report only when files differ"),
852 N_("-s, --report-identical-files report when two files are the same"),
853 N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
854 N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
855 N_("-e, --ed output an ed script"),
856 N_("-n, --rcs output an RCS format diff"),
857 N_("-y, --side-by-side output in two columns"),
858 N_("-W, --width=NUM output at most NUM (default 130) print columns"),
859 N_(" --left-column output only the left column of common lines"),
860 N_(" --suppress-common-lines do not output common lines"),
862 N_("-p, --show-c-function show which C function each change is in"),
863 N_("-F, --show-function-line=RE show the most recent line matching RE"),
864 N_(" --label LABEL use LABEL instead of file name\n"
865 " (can be repeated)"),
867 N_("-t, --expand-tabs expand tabs to spaces in output"),
868 N_("-T, --initial-tab make tabs line up by prepending a tab"),
869 N_(" --tabsize=NUM tab stops every NUM (default 8) print columns"),
870 N_(" --suppress-blank-empty suppress space or tab before empty output lines"),
871 N_("-l, --paginate pass output through `pr' to paginate it"),
873 N_("-r, --recursive recursively compare any subdirectories found"),
874 N_("-N, --new-file treat absent files as empty"),
875 N_(" --unidirectional-new-file treat absent first files as empty"),
876 N_(" --ignore-file-name-case ignore case when comparing file names"),
877 N_(" --no-ignore-file-name-case consider case when comparing file names"),
878 N_("-x, --exclude=PAT exclude files that match PAT"),
879 N_("-X, --exclude-from=FILE exclude files that match any pattern in FILE"),
880 N_("-S, --starting-file=FILE start with FILE when comparing directories"),
881 N_(" --from-file=FILE1 compare FILE1 to all operands;\n"
882 " FILE1 can be a directory"),
883 N_(" --to-file=FILE2 compare all operands to FILE2;\n"
884 " FILE2 can be a directory"),
886 N_("-i, --ignore-case ignore case differences in file contents"),
887 N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
888 N_("-Z, --ignore-trailing-space ignore white space at line end"),
889 N_("-b, --ignore-space-change ignore changes in the amount of white space"),
890 N_("-w, --ignore-all-space ignore all white space"),
891 N_("-B, --ignore-blank-lines ignore changes whose lines are all blank"),
892 N_("-I, --ignore-matching-lines=RE ignore changes whose lines all match RE"),
894 N_("-a, --text treat all files as text"),
895 N_(" --strip-trailing-cr strip trailing carriage return on input"),
897 N_(" --binary read and write data in binary mode"),
900 N_("-D, --ifdef=NAME output merged file with `#ifdef NAME' diffs"),
901 N_(" --GTYPE-group-format=GFMT format GTYPE input groups with GFMT"),
902 N_(" --line-format=LFMT format all input lines with LFMT"),
903 N_(" --LTYPE-line-format=LFMT format LTYPE input lines with LFMT"),
904 N_(" These format options provide fine-grained control over the output\n"
905 " of diff, generalizing -D/--ifdef."),
906 N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."),
907 N_(" GFMT (only) may contain:\n\
908 %< lines from FILE1\n\
909 %> lines from FILE2\n\
910 %= lines common to FILE1 and FILE2\n\
911 %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
912 LETTERs are as follows for new group, lower case for old group:\n\
913 F first line number\n\
914 L last line number\n\
915 N number of lines = L-F+1\n\
918 %(A=B?T:E) if A equals B then T else E"),
919 N_(" LFMT (only) may contain:\n\
920 %L contents of line\n\
921 %l contents of line, excluding any trailing newline\n\
922 %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
923 N_(" Both GFMT and LFMT may contain:\n\
925 %c'C' the single character C\n\
926 %c'\\OOO' the character with octal code OOO\n\
927 C the character C (other characters represent themselves)"),
929 N_("-d, --minimal try hard to find a smaller set of changes"),
930 N_(" --horizon-lines=NUM keep NUM lines of the common prefix and suffix"),
931 N_(" --speed-large-files assume large files and many scattered small changes"),
933 N_(" --help display this help and exit"),
934 N_("-v, --version output version information and exit"),
936 N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
937 N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
938 N_("If a FILE is `-', read standard input."),
939 N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
946 char const * const *p;
948 printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
949 printf ("%s\n\n", _("Compare FILES line by line."));
952 Mandatory arguments to long options are mandatory for short options too.\n\
955 for (p = option_help_msgid; *p; p++)
961 char const *msg = _(*p);
963 while ((nl = strchr (msg, '\n')))
965 int msglen = nl + 1 - msg;
966 printf (" %.*s", msglen, msg);
970 printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
973 emit_bug_reporting_address ();
976 /* Set VAR to VALUE, reporting an OPTION error if this is a
979 specify_value (char const **var, char const *value, char const *option)
981 if (*var && ! STREQ (*var, value))
983 error (0, 0, _("conflicting %s option value `%s'"), option, value);
984 try_help (NULL, NULL);
989 /* Set the output style to STYLE, diagnosing conflicts. */
991 specify_style (enum output_style style)
993 if (output_style != style)
995 if (output_style != OUTPUT_UNSPECIFIED)
996 try_help ("conflicting output style options", NULL);
997 output_style = style;
1001 /* Set the last-modified time of *ST to be the current time. */
1004 set_mtime_to_now (struct stat *st)
1006 #ifdef STAT_TIMESPEC
1007 gettime (&STAT_TIMESPEC (st, st_mtim));
1011 st->st_mtime = t.tv_sec;
1012 # if defined STAT_TIMESPEC_NS
1013 STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
1014 # elif defined HAVE_STRUCT_STAT_ST_SPARE1
1015 st->st_spare1 = t.tv_nsec / 1000;
1020 /* Compare two files (or dirs) with parent comparison PARENT
1021 and names NAME0 and NAME1.
1022 (If PARENT is null, then the first name is just NAME0, etc.)
1023 This is self-contained; it opens the files and closes them.
1025 Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1026 different, EXIT_TROUBLE if there is a problem opening them. */
1029 compare_files (struct comparison const *parent,
1033 struct comparison cmp;
1034 #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1036 int status = EXIT_SUCCESS;
1041 /* If this is directory comparison, perhaps we have a file
1042 that exists only in one of the directories.
1043 If so, just print a message to that effect. */
1045 if (! ((name0 && name1)
1046 || (unidirectional_new_file && name1)
1049 char const *name = name0 ? name0 : name1;
1050 char const *dir = parent->file[!name0].name;
1052 /* See POSIX 1003.1-2001 for this format. */
1053 message ("Only in %s: %s\n", dir, name);
1055 /* Return EXIT_FAILURE so that diff_dirs will return
1056 EXIT_FAILURE ("some files differ"). */
1057 return EXIT_FAILURE;
1060 memset (cmp.file, 0, sizeof cmp.file);
1061 cmp.parent = parent;
1063 /* cmp.file[f].desc markers */
1064 #define NONEXISTENT (-1) /* nonexistent file */
1065 #define UNOPENED (-2) /* unopened file (e.g. directory) */
1066 #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1068 #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1070 cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
1071 cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
1073 /* Now record the full name of each file, including nonexistent ones. */
1084 cmp.file[0].name = name0;
1085 cmp.file[1].name = name1;
1089 cmp.file[0].name = free0
1090 = file_name_concat (parent->file[0].name, name0, NULL);
1091 cmp.file[1].name = free1
1092 = file_name_concat (parent->file[1].name, name1, NULL);
1095 /* Stat the files. */
1097 for (f = 0; f < 2; f++)
1099 if (cmp.file[f].desc != NONEXISTENT)
1101 if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1103 cmp.file[f].desc = cmp.file[0].desc;
1104 cmp.file[f].stat = cmp.file[0].stat;
1106 else if (STREQ (cmp.file[f].name, "-"))
1108 cmp.file[f].desc = STDIN_FILENO;
1109 if (O_BINARY && binary && ! isatty (STDIN_FILENO))
1110 xfreopen (NULL, "rb", stdin);
1111 if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1112 cmp.file[f].desc = ERRNO_ENCODE (errno);
1115 if (S_ISREG (cmp.file[f].stat.st_mode))
1117 off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
1119 cmp.file[f].desc = ERRNO_ENCODE (errno);
1121 cmp.file[f].stat.st_size =
1122 MAX (0, cmp.file[f].stat.st_size - pos);
1125 /* POSIX 1003.1-2001 requires current time for
1127 set_mtime_to_now (&cmp.file[f].stat);
1130 else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
1131 cmp.file[f].desc = ERRNO_ENCODE (errno);
1135 /* Mark files as nonexistent as needed for -N and -P, if they are
1136 inaccessible empty regular files (the kind of files that 'patch'
1137 creates to indicate nonexistent backups), or if they are
1138 top-level files that do not exist but their counterparts do
1140 for (f = 0; f < 2; f++)
1141 if ((new_file || (f == 0 && unidirectional_new_file))
1142 && (cmp.file[f].desc == UNOPENED
1143 ? (S_ISREG (cmp.file[f].stat.st_mode)
1144 && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1145 && cmp.file[f].stat.st_size == 0)
1146 : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
1148 && cmp.file[1 - f].desc == UNOPENED)))
1149 cmp.file[f].desc = NONEXISTENT;
1151 for (f = 0; f < 2; f++)
1152 if (cmp.file[f].desc == NONEXISTENT)
1154 memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1155 cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1158 for (f = 0; f < 2; f++)
1160 int e = ERRNO_DECODE (cmp.file[f].desc);
1164 perror_with_name (cmp.file[f].name);
1165 status = EXIT_TROUBLE;
1169 if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1171 /* If one is a directory, and it was specified in the command line,
1172 use the file in that dir with the other file's basename. */
1174 int fnm_arg = DIR_P (0);
1175 int dir_arg = 1 - fnm_arg;
1176 char const *fnm = cmp.file[fnm_arg].name;
1177 char const *dir = cmp.file[dir_arg].name;
1178 char const *filename = cmp.file[dir_arg].name = free0
1179 = find_dir_file_pathname (dir, last_component (fnm));
1181 if (STREQ (fnm, "-"))
1182 fatal ("cannot compare `-' to a directory");
1184 if (stat (filename, &cmp.file[dir_arg].stat) != 0)
1186 perror_with_name (filename);
1187 status = EXIT_TROUBLE;
1191 if (status != EXIT_SUCCESS)
1193 /* One of the files should exist but does not. */
1195 else if (cmp.file[0].desc == NONEXISTENT
1196 && cmp.file[1].desc == NONEXISTENT)
1198 /* Neither file "exists", so there's nothing to compare. */
1200 else if ((same_files
1201 = (cmp.file[0].desc != NONEXISTENT
1202 && cmp.file[1].desc != NONEXISTENT
1203 && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1204 && same_file_attributes (&cmp.file[0].stat,
1205 &cmp.file[1].stat)))
1206 && no_diff_means_no_output)
1208 /* The two named files are actually the same physical file.
1209 We know they are identical without actually reading them. */
1211 else if (DIR_P (0) & DIR_P (1))
1213 if (output_style == OUTPUT_IFDEF)
1214 fatal ("-D option not supported with directories");
1216 /* If both are directories, compare the files in them. */
1218 if (parent && !recursive)
1220 /* But don't compare dir contents one level down
1221 unless -r was specified.
1222 See POSIX 1003.1-2001 for this format. */
1223 message ("Common subdirectories: %s and %s\n",
1224 cmp.file[0].name, cmp.file[1].name);
1227 status = diff_dirs (&cmp, compare_files);
1229 else if ((DIR_P (0) | DIR_P (1))
1231 && (! S_ISREG (cmp.file[0].stat.st_mode)
1232 || ! S_ISREG (cmp.file[1].stat.st_mode))))
1234 if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1236 /* We have a subdirectory that exists only in one directory. */
1238 if ((DIR_P (0) | DIR_P (1))
1241 || (unidirectional_new_file
1242 && cmp.file[0].desc == NONEXISTENT)))
1243 status = diff_dirs (&cmp, compare_files);
1248 /* PARENT must be non-NULL here. */
1250 dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
1252 /* See POSIX 1003.1-2001 for this format. */
1253 message ("Only in %s: %s\n", dir, name0);
1255 status = EXIT_FAILURE;
1260 /* We have two files that are not to be compared. */
1262 /* See POSIX 1003.1-2001 for this format. */
1263 message5 ("File %s is a %s while file %s is a %s\n",
1264 file_label[0] ? file_label[0] : cmp.file[0].name,
1265 file_type (&cmp.file[0].stat),
1266 file_label[1] ? file_label[1] : cmp.file[1].name,
1267 file_type (&cmp.file[1].stat));
1269 /* This is a difference. */
1270 status = EXIT_FAILURE;
1273 else if (files_can_be_treated_as_binary
1274 && S_ISREG (cmp.file[0].stat.st_mode)
1275 && S_ISREG (cmp.file[1].stat.st_mode)
1276 && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size)
1278 message ("Files %s and %s differ\n",
1279 file_label[0] ? file_label[0] : cmp.file[0].name,
1280 file_label[1] ? file_label[1] : cmp.file[1].name);
1281 status = EXIT_FAILURE;
1285 /* Both exist and neither is a directory. */
1287 /* Open the files and record their descriptors. */
1289 int oflags = O_RDONLY | (binary ? O_BINARY : 0);
1291 if (cmp.file[0].desc == UNOPENED)
1292 if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
1294 perror_with_name (cmp.file[0].name);
1295 status = EXIT_TROUBLE;
1297 if (cmp.file[1].desc == UNOPENED)
1300 cmp.file[1].desc = cmp.file[0].desc;
1301 else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
1303 perror_with_name (cmp.file[1].name);
1304 status = EXIT_TROUBLE;
1308 /* Compare the files, if no error was found. */
1310 if (status == EXIT_SUCCESS)
1311 status = diff_2_files (&cmp);
1313 /* Close the file descriptors. */
1315 if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1317 perror_with_name (cmp.file[0].name);
1318 status = EXIT_TROUBLE;
1320 if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1321 && close (cmp.file[1].desc) != 0)
1323 perror_with_name (cmp.file[1].name);
1324 status = EXIT_TROUBLE;
1328 /* Now the comparison has been done, if no error prevented it,
1329 and STATUS is the value this function will return. */
1331 if (status == EXIT_SUCCESS)
1333 if (report_identical_files && !DIR_P (0))
1334 message ("Files %s and %s are identical\n",
1335 file_label[0] ? file_label[0] : cmp.file[0].name,
1336 file_label[1] ? file_label[1] : cmp.file[1].name);
1340 /* Flush stdout so that the user sees differences immediately.
1341 This can hurt performance, unfortunately. */
1342 if (fflush (stdout) != 0)
1343 pfatal_with_name (_("standard output"));