1*6ea1f93eSDaniel Fojt /* GNU diff - compare files line by line
2855caec6SPeter Avalos
344b87433SJohn Marino Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
4*6ea1f93eSDaniel Fojt 2009-2013, 2015-2018 Free Software Foundation, Inc.
5855caec6SPeter Avalos
6855caec6SPeter Avalos This file is part of GNU DIFF.
7855caec6SPeter Avalos
844b87433SJohn Marino This program is free software: you can redistribute it and/or modify
9855caec6SPeter Avalos it under the terms of the GNU General Public License as published by
1044b87433SJohn Marino the Free Software Foundation, either version 3 of the License, or
1144b87433SJohn Marino (at your option) any later version.
12855caec6SPeter Avalos
1344b87433SJohn Marino This program is distributed in the hope that it will be useful,
14855caec6SPeter Avalos but WITHOUT ANY WARRANTY; without even the implied warranty of
1544b87433SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1644b87433SJohn Marino GNU General Public License for more details.
17855caec6SPeter Avalos
18855caec6SPeter Avalos You should have received a copy of the GNU General Public License
1944b87433SJohn Marino along with this program. If not, see <http://www.gnu.org/licenses/>. */
20855caec6SPeter Avalos
21855caec6SPeter Avalos #define GDIFF_MAIN
22855caec6SPeter Avalos #include "diff.h"
23*6ea1f93eSDaniel Fojt #include "die.h"
2444b87433SJohn Marino #include <assert.h>
25855caec6SPeter Avalos #include "paths.h"
26855caec6SPeter Avalos #include <c-stack.h>
27855caec6SPeter Avalos #include <dirname.h>
28855caec6SPeter Avalos #include <error.h>
29855caec6SPeter Avalos #include <exclude.h>
30855caec6SPeter Avalos #include <exitfail.h>
31008e37b6SJohn Marino #include <filenamecat.h>
32855caec6SPeter Avalos #include <file-type.h>
33855caec6SPeter Avalos #include <fnmatch.h>
34855caec6SPeter Avalos #include <getopt.h>
35855caec6SPeter Avalos #include <hard-locale.h>
36855caec6SPeter Avalos #include <prepargs.h>
3744b87433SJohn Marino #include <progname.h>
3844b87433SJohn Marino #include <sh-quote.h>
3944b87433SJohn Marino #include <stat-time.h>
4044b87433SJohn Marino #include <timespec.h>
41855caec6SPeter Avalos #include <version-etc.h>
42855caec6SPeter Avalos #include <xalloc.h>
434536c563SJohn Marino #include <xreadlink.h>
444536c563SJohn Marino #include <binary-io.h>
4544b87433SJohn Marino
464536c563SJohn Marino /* The official name of this program (e.g., no 'g' prefix). */
4744b87433SJohn Marino #define PROGRAM_NAME "diff"
4844b87433SJohn Marino
4944b87433SJohn Marino #define AUTHORS \
5044b87433SJohn Marino proper_name ("Paul Eggert"), \
5144b87433SJohn Marino proper_name ("Mike Haertel"), \
5244b87433SJohn Marino proper_name ("David Hayes"), \
5344b87433SJohn Marino proper_name ("Richard Stallman"), \
5444b87433SJohn Marino proper_name ("Len Tower")
55855caec6SPeter Avalos
56855caec6SPeter Avalos #ifndef GUTTER_WIDTH_MINIMUM
57855caec6SPeter Avalos # define GUTTER_WIDTH_MINIMUM 3
58855caec6SPeter Avalos #endif
59855caec6SPeter Avalos
60855caec6SPeter Avalos struct regexp_list
61855caec6SPeter Avalos {
62855caec6SPeter Avalos char *regexps; /* chars representing disjunction of the regexps */
634536c563SJohn Marino size_t len; /* chars used in 'regexps' */
644536c563SJohn Marino size_t size; /* size malloc'ed for 'regexps'; 0 if not malloc'ed */
654536c563SJohn Marino bool multiple_regexps;/* Does 'regexps' represent a disjunction? */
66855caec6SPeter Avalos struct re_pattern_buffer *buf;
67855caec6SPeter Avalos };
68855caec6SPeter Avalos
69855caec6SPeter Avalos static int compare_files (struct comparison const *, char const *, char const *);
70855caec6SPeter Avalos static void add_regexp (struct regexp_list *, char const *);
71855caec6SPeter Avalos static void summarize_regexp_list (struct regexp_list *);
72855caec6SPeter Avalos static void specify_style (enum output_style);
73855caec6SPeter Avalos static void specify_value (char const **, char const *, char const *);
74*6ea1f93eSDaniel Fojt static void specify_colors_style (char const *);
75855caec6SPeter Avalos static void try_help (char const *, char const *) __attribute__((noreturn));
76855caec6SPeter Avalos static void check_stdout (void);
77855caec6SPeter Avalos static void usage (void);
78855caec6SPeter Avalos
79855caec6SPeter Avalos /* If comparing directories, compare their common subdirectories
80855caec6SPeter Avalos recursively. */
81855caec6SPeter Avalos static bool recursive;
82855caec6SPeter Avalos
83855caec6SPeter Avalos /* In context diffs, show previous lines that match these regexps. */
84855caec6SPeter Avalos static struct regexp_list function_regexp_list;
85855caec6SPeter Avalos
86855caec6SPeter Avalos /* Ignore changes affecting only lines that match these regexps. */
87855caec6SPeter Avalos static struct regexp_list ignore_regexp_list;
88855caec6SPeter Avalos
8944b87433SJohn Marino #if O_BINARY
90855caec6SPeter Avalos /* Use binary I/O when reading and writing data (--binary).
91855caec6SPeter Avalos On POSIX hosts, this has no effect. */
92855caec6SPeter Avalos static bool binary;
93855caec6SPeter Avalos #else
94855caec6SPeter Avalos enum { binary = true };
95855caec6SPeter Avalos #endif
96855caec6SPeter Avalos
974536c563SJohn Marino /* If one file is missing, treat it as present but empty (-N). */
98855caec6SPeter Avalos static bool new_file;
99855caec6SPeter Avalos
1004536c563SJohn Marino /* If the first file is missing, treat it as present but empty
1014536c563SJohn Marino (--unidirectional-new-file). */
102855caec6SPeter Avalos static bool unidirectional_new_file;
103855caec6SPeter Avalos
104855caec6SPeter Avalos /* Report files compared that are the same (-s).
105855caec6SPeter Avalos Normally nothing is output when that happens. */
106855caec6SPeter Avalos static bool report_identical_files;
107855caec6SPeter Avalos
108855caec6SPeter Avalos static char const shortopts[] =
109008e37b6SJohn Marino "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
110855caec6SPeter Avalos
111855caec6SPeter Avalos /* Values for long options that do not have single-letter equivalents. */
112855caec6SPeter Avalos enum
113855caec6SPeter Avalos {
114855caec6SPeter Avalos BINARY_OPTION = CHAR_MAX + 1,
115855caec6SPeter Avalos FROM_FILE_OPTION,
116855caec6SPeter Avalos HELP_OPTION,
117855caec6SPeter Avalos HORIZON_LINES_OPTION,
118855caec6SPeter Avalos IGNORE_FILE_NAME_CASE_OPTION,
119855caec6SPeter Avalos INHIBIT_HUNK_MERGE_OPTION,
120855caec6SPeter Avalos LEFT_COLUMN_OPTION,
121855caec6SPeter Avalos LINE_FORMAT_OPTION,
1224536c563SJohn Marino NO_DEREFERENCE_OPTION,
123855caec6SPeter Avalos NO_IGNORE_FILE_NAME_CASE_OPTION,
124855caec6SPeter Avalos NORMAL_OPTION,
125855caec6SPeter Avalos SDIFF_MERGE_ASSIST_OPTION,
126855caec6SPeter Avalos STRIP_TRAILING_CR_OPTION,
12744b87433SJohn Marino SUPPRESS_BLANK_EMPTY_OPTION,
128855caec6SPeter Avalos SUPPRESS_COMMON_LINES_OPTION,
129855caec6SPeter Avalos TABSIZE_OPTION,
130855caec6SPeter Avalos TO_FILE_OPTION,
131855caec6SPeter Avalos
132855caec6SPeter Avalos /* These options must be in sequence. */
133855caec6SPeter Avalos UNCHANGED_LINE_FORMAT_OPTION,
134855caec6SPeter Avalos OLD_LINE_FORMAT_OPTION,
135855caec6SPeter Avalos NEW_LINE_FORMAT_OPTION,
136855caec6SPeter Avalos
137855caec6SPeter Avalos /* These options must be in sequence. */
138855caec6SPeter Avalos UNCHANGED_GROUP_FORMAT_OPTION,
139855caec6SPeter Avalos OLD_GROUP_FORMAT_OPTION,
140855caec6SPeter Avalos NEW_GROUP_FORMAT_OPTION,
141*6ea1f93eSDaniel Fojt CHANGED_GROUP_FORMAT_OPTION,
142*6ea1f93eSDaniel Fojt
143*6ea1f93eSDaniel Fojt COLOR_OPTION,
144*6ea1f93eSDaniel Fojt COLOR_PALETTE_OPTION,
145*6ea1f93eSDaniel Fojt
146*6ea1f93eSDaniel Fojt PRESUME_OUTPUT_TTY_OPTION,
147855caec6SPeter Avalos };
148855caec6SPeter Avalos
149855caec6SPeter Avalos static char const group_format_option[][sizeof "--unchanged-group-format"] =
150855caec6SPeter Avalos {
151855caec6SPeter Avalos "--unchanged-group-format",
152855caec6SPeter Avalos "--old-group-format",
153855caec6SPeter Avalos "--new-group-format",
154855caec6SPeter Avalos "--changed-group-format"
155855caec6SPeter Avalos };
156855caec6SPeter Avalos
157855caec6SPeter Avalos static char const line_format_option[][sizeof "--unchanged-line-format"] =
158855caec6SPeter Avalos {
159855caec6SPeter Avalos "--unchanged-line-format",
160855caec6SPeter Avalos "--old-line-format",
161855caec6SPeter Avalos "--new-line-format"
162855caec6SPeter Avalos };
163855caec6SPeter Avalos
164855caec6SPeter Avalos static struct option const longopts[] =
165855caec6SPeter Avalos {
166855caec6SPeter Avalos {"binary", 0, 0, BINARY_OPTION},
167855caec6SPeter Avalos {"brief", 0, 0, 'q'},
168855caec6SPeter Avalos {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
169*6ea1f93eSDaniel Fojt {"color", 2, 0, COLOR_OPTION},
170855caec6SPeter Avalos {"context", 2, 0, 'C'},
171855caec6SPeter Avalos {"ed", 0, 0, 'e'},
172855caec6SPeter Avalos {"exclude", 1, 0, 'x'},
173855caec6SPeter Avalos {"exclude-from", 1, 0, 'X'},
174855caec6SPeter Avalos {"expand-tabs", 0, 0, 't'},
175855caec6SPeter Avalos {"forward-ed", 0, 0, 'f'},
176855caec6SPeter Avalos {"from-file", 1, 0, FROM_FILE_OPTION},
177855caec6SPeter Avalos {"help", 0, 0, HELP_OPTION},
178855caec6SPeter Avalos {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
179855caec6SPeter Avalos {"ifdef", 1, 0, 'D'},
180855caec6SPeter Avalos {"ignore-all-space", 0, 0, 'w'},
181855caec6SPeter Avalos {"ignore-blank-lines", 0, 0, 'B'},
182855caec6SPeter Avalos {"ignore-case", 0, 0, 'i'},
183855caec6SPeter Avalos {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
184855caec6SPeter Avalos {"ignore-matching-lines", 1, 0, 'I'},
185855caec6SPeter Avalos {"ignore-space-change", 0, 0, 'b'},
186855caec6SPeter Avalos {"ignore-tab-expansion", 0, 0, 'E'},
187008e37b6SJohn Marino {"ignore-trailing-space", 0, 0, 'Z'},
188855caec6SPeter Avalos {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
189855caec6SPeter Avalos {"initial-tab", 0, 0, 'T'},
190855caec6SPeter Avalos {"label", 1, 0, 'L'},
191855caec6SPeter Avalos {"left-column", 0, 0, LEFT_COLUMN_OPTION},
192855caec6SPeter Avalos {"line-format", 1, 0, LINE_FORMAT_OPTION},
193855caec6SPeter Avalos {"minimal", 0, 0, 'd'},
194855caec6SPeter Avalos {"new-file", 0, 0, 'N'},
195855caec6SPeter Avalos {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
196855caec6SPeter Avalos {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
1974536c563SJohn Marino {"no-dereference", 0, 0, NO_DEREFERENCE_OPTION},
198855caec6SPeter Avalos {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
199855caec6SPeter Avalos {"normal", 0, 0, NORMAL_OPTION},
200855caec6SPeter Avalos {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
201855caec6SPeter Avalos {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
202855caec6SPeter Avalos {"paginate", 0, 0, 'l'},
203*6ea1f93eSDaniel Fojt {"palette", 1, 0, COLOR_PALETTE_OPTION},
204855caec6SPeter Avalos {"rcs", 0, 0, 'n'},
205855caec6SPeter Avalos {"recursive", 0, 0, 'r'},
206855caec6SPeter Avalos {"report-identical-files", 0, 0, 's'},
207855caec6SPeter Avalos {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
208855caec6SPeter Avalos {"show-c-function", 0, 0, 'p'},
209855caec6SPeter Avalos {"show-function-line", 1, 0, 'F'},
210855caec6SPeter Avalos {"side-by-side", 0, 0, 'y'},
211855caec6SPeter Avalos {"speed-large-files", 0, 0, 'H'},
212855caec6SPeter Avalos {"starting-file", 1, 0, 'S'},
213855caec6SPeter Avalos {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
21444b87433SJohn Marino {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
215855caec6SPeter Avalos {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
216855caec6SPeter Avalos {"tabsize", 1, 0, TABSIZE_OPTION},
217855caec6SPeter Avalos {"text", 0, 0, 'a'},
218855caec6SPeter Avalos {"to-file", 1, 0, TO_FILE_OPTION},
219855caec6SPeter Avalos {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
220855caec6SPeter Avalos {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
221855caec6SPeter Avalos {"unidirectional-new-file", 0, 0, 'P'},
222855caec6SPeter Avalos {"unified", 2, 0, 'U'},
223855caec6SPeter Avalos {"version", 0, 0, 'v'},
224855caec6SPeter Avalos {"width", 1, 0, 'W'},
225*6ea1f93eSDaniel Fojt
226*6ea1f93eSDaniel Fojt /* This is solely for testing. Do not document. */
227*6ea1f93eSDaniel Fojt {"-presume-output-tty", no_argument, NULL, PRESUME_OUTPUT_TTY_OPTION},
228855caec6SPeter Avalos {0, 0, 0, 0}
229855caec6SPeter Avalos };
230855caec6SPeter Avalos
23144b87433SJohn Marino /* Return a string containing the command options with which diff was invoked.
23244b87433SJohn Marino Spaces appear between what were separate ARGV-elements.
23344b87433SJohn Marino There is a space at the beginning but none at the end.
23444b87433SJohn Marino If there were no options, the result is an empty string.
23544b87433SJohn Marino
23644b87433SJohn Marino Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
23744b87433SJohn Marino the length of that vector. */
23844b87433SJohn Marino
23944b87433SJohn Marino static char *
option_list(char ** optionvec,int count)24044b87433SJohn Marino option_list (char **optionvec, int count)
24144b87433SJohn Marino {
24244b87433SJohn Marino int i;
24344b87433SJohn Marino size_t size = 1;
24444b87433SJohn Marino char *result;
24544b87433SJohn Marino char *p;
24644b87433SJohn Marino
24744b87433SJohn Marino for (i = 0; i < count; i++)
24844b87433SJohn Marino size += 1 + shell_quote_length (optionvec[i]);
24944b87433SJohn Marino
25044b87433SJohn Marino p = result = xmalloc (size);
25144b87433SJohn Marino
25244b87433SJohn Marino for (i = 0; i < count; i++)
25344b87433SJohn Marino {
25444b87433SJohn Marino *p++ = ' ';
25544b87433SJohn Marino p = shell_quote_copy (p, optionvec[i]);
25644b87433SJohn Marino }
25744b87433SJohn Marino
25844b87433SJohn Marino *p = '\0';
25944b87433SJohn Marino return result;
26044b87433SJohn Marino }
26144b87433SJohn Marino
26244b87433SJohn Marino
26344b87433SJohn Marino /* Return an option value suitable for add_exclude. */
26444b87433SJohn Marino
26544b87433SJohn Marino static int
exclude_options(void)26644b87433SJohn Marino exclude_options (void)
26744b87433SJohn Marino {
26844b87433SJohn Marino return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
26944b87433SJohn Marino }
27044b87433SJohn Marino
271855caec6SPeter Avalos int
main(int argc,char ** argv)272855caec6SPeter Avalos main (int argc, char **argv)
273855caec6SPeter Avalos {
274855caec6SPeter Avalos int exit_status = EXIT_SUCCESS;
275855caec6SPeter Avalos int c;
276855caec6SPeter Avalos int i;
277855caec6SPeter Avalos int prev = -1;
278855caec6SPeter Avalos lin ocontext = -1;
279855caec6SPeter Avalos bool explicit_context = false;
280855caec6SPeter Avalos size_t width = 0;
281855caec6SPeter Avalos bool show_c_function = false;
28244b87433SJohn Marino char const *from_file = NULL;
28344b87433SJohn Marino char const *to_file = NULL;
284855caec6SPeter Avalos uintmax_t numval;
285855caec6SPeter Avalos char *numend;
286855caec6SPeter Avalos
287855caec6SPeter Avalos /* Do our initializations. */
28844b87433SJohn Marino exit_failure = EXIT_TROUBLE;
289855caec6SPeter Avalos initialize_main (&argc, &argv);
29044b87433SJohn Marino set_program_name (argv[0]);
291855caec6SPeter Avalos setlocale (LC_ALL, "");
292855caec6SPeter Avalos textdomain (PACKAGE);
293855caec6SPeter Avalos c_stack_action (0);
294855caec6SPeter Avalos function_regexp_list.buf = &function_regexp;
295855caec6SPeter Avalos ignore_regexp_list.buf = &ignore_regexp;
296855caec6SPeter Avalos re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
297855caec6SPeter Avalos excluded = new_exclude ();
298855caec6SPeter Avalos presume_output_tty = false;
299*6ea1f93eSDaniel Fojt
300855caec6SPeter Avalos /* Decode the options. */
301855caec6SPeter Avalos
302855caec6SPeter Avalos while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
30344b87433SJohn Marino {
304855caec6SPeter Avalos switch (c)
305855caec6SPeter Avalos {
306855caec6SPeter Avalos case 0:
307855caec6SPeter Avalos break;
308855caec6SPeter Avalos
309855caec6SPeter Avalos case '0':
310855caec6SPeter Avalos case '1':
311855caec6SPeter Avalos case '2':
312855caec6SPeter Avalos case '3':
313855caec6SPeter Avalos case '4':
314855caec6SPeter Avalos case '5':
315855caec6SPeter Avalos case '6':
316855caec6SPeter Avalos case '7':
317855caec6SPeter Avalos case '8':
318855caec6SPeter Avalos case '9':
319855caec6SPeter Avalos ocontext = (! ISDIGIT (prev)
320*6ea1f93eSDaniel Fojt ? c - '0'
321*6ea1f93eSDaniel Fojt : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
322*6ea1f93eSDaniel Fojt < CONTEXT_MAX / 10)
323*6ea1f93eSDaniel Fojt ? 10 * ocontext + (c - '0')
324*6ea1f93eSDaniel Fojt : CONTEXT_MAX);
325*6ea1f93eSDaniel Fojt break;
326855caec6SPeter Avalos
327855caec6SPeter Avalos case 'a':
328855caec6SPeter Avalos text = true;
329855caec6SPeter Avalos break;
330855caec6SPeter Avalos
331855caec6SPeter Avalos case 'b':
332855caec6SPeter Avalos if (ignore_white_space < IGNORE_SPACE_CHANGE)
333855caec6SPeter Avalos ignore_white_space = IGNORE_SPACE_CHANGE;
334855caec6SPeter Avalos break;
335855caec6SPeter Avalos
336855caec6SPeter Avalos case 'Z':
337008e37b6SJohn Marino if (ignore_white_space < IGNORE_SPACE_CHANGE)
338008e37b6SJohn Marino ignore_white_space |= IGNORE_TRAILING_SPACE;
339008e37b6SJohn Marino break;
340008e37b6SJohn Marino
341008e37b6SJohn Marino case 'B':
342855caec6SPeter Avalos ignore_blank_lines = true;
343855caec6SPeter Avalos break;
344855caec6SPeter Avalos
345855caec6SPeter Avalos case 'C':
346855caec6SPeter Avalos case 'U':
347855caec6SPeter Avalos {
348855caec6SPeter Avalos if (optarg)
349855caec6SPeter Avalos {
350855caec6SPeter Avalos numval = strtoumax (optarg, &numend, 10);
351855caec6SPeter Avalos if (*numend)
352855caec6SPeter Avalos try_help ("invalid context length '%s'", optarg);
3534536c563SJohn Marino if (CONTEXT_MAX < numval)
354*6ea1f93eSDaniel Fojt numval = CONTEXT_MAX;
355*6ea1f93eSDaniel Fojt }
356855caec6SPeter Avalos else
357855caec6SPeter Avalos numval = 3;
358855caec6SPeter Avalos
359855caec6SPeter Avalos specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
360855caec6SPeter Avalos if (context < numval)
361855caec6SPeter Avalos context = numval;
362855caec6SPeter Avalos explicit_context = true;
363855caec6SPeter Avalos }
364855caec6SPeter Avalos break;
365855caec6SPeter Avalos
366855caec6SPeter Avalos case 'c':
367855caec6SPeter Avalos specify_style (OUTPUT_CONTEXT);
368855caec6SPeter Avalos if (context < 3)
369855caec6SPeter Avalos context = 3;
370855caec6SPeter Avalos break;
371855caec6SPeter Avalos
372855caec6SPeter Avalos case 'd':
373855caec6SPeter Avalos minimal = true;
374855caec6SPeter Avalos break;
375855caec6SPeter Avalos
376855caec6SPeter Avalos case 'D':
377855caec6SPeter Avalos specify_style (OUTPUT_IFDEF);
378855caec6SPeter Avalos {
379855caec6SPeter Avalos static char const C_ifdef_group_formats[] =
380855caec6SPeter Avalos "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
381855caec6SPeter Avalos char *b = xmalloc (sizeof C_ifdef_group_formats
382855caec6SPeter Avalos + 7 * strlen (optarg) - 14 /* 7*"%s" */
383855caec6SPeter Avalos - 8 /* 5*"%%" + 3*"%c" */);
384855caec6SPeter Avalos sprintf (b, C_ifdef_group_formats,
385855caec6SPeter Avalos 0,
386855caec6SPeter Avalos optarg, optarg, 0,
387855caec6SPeter Avalos optarg, optarg, 0,
388855caec6SPeter Avalos optarg, optarg, optarg);
389855caec6SPeter Avalos for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
39044b87433SJohn Marino {
391855caec6SPeter Avalos specify_value (&group_format[i], b, "-D");
392855caec6SPeter Avalos b += strlen (b) + 1;
393855caec6SPeter Avalos }
394855caec6SPeter Avalos }
395855caec6SPeter Avalos break;
396855caec6SPeter Avalos
397855caec6SPeter Avalos case 'e':
398855caec6SPeter Avalos specify_style (OUTPUT_ED);
399855caec6SPeter Avalos break;
400855caec6SPeter Avalos
401855caec6SPeter Avalos case 'E':
402855caec6SPeter Avalos if (ignore_white_space < IGNORE_SPACE_CHANGE)
403008e37b6SJohn Marino ignore_white_space |= IGNORE_TAB_EXPANSION;
404008e37b6SJohn Marino break;
405855caec6SPeter Avalos
406855caec6SPeter Avalos case 'f':
407855caec6SPeter Avalos specify_style (OUTPUT_FORWARD_ED);
408855caec6SPeter Avalos break;
409855caec6SPeter Avalos
410855caec6SPeter Avalos case 'F':
411855caec6SPeter Avalos add_regexp (&function_regexp_list, optarg);
412855caec6SPeter Avalos break;
413855caec6SPeter Avalos
414855caec6SPeter Avalos case 'h':
415855caec6SPeter Avalos /* Split the files into chunks for faster processing.
416855caec6SPeter Avalos Usually does not change the result.
417855caec6SPeter Avalos
418855caec6SPeter Avalos This currently has no effect. */
419855caec6SPeter Avalos break;
420855caec6SPeter Avalos
421855caec6SPeter Avalos case 'H':
422855caec6SPeter Avalos speed_large_files = true;
423855caec6SPeter Avalos break;
424855caec6SPeter Avalos
425855caec6SPeter Avalos case 'i':
426855caec6SPeter Avalos ignore_case = true;
427855caec6SPeter Avalos break;
428855caec6SPeter Avalos
429855caec6SPeter Avalos case 'I':
430855caec6SPeter Avalos add_regexp (&ignore_regexp_list, optarg);
431855caec6SPeter Avalos break;
432855caec6SPeter Avalos
433855caec6SPeter Avalos case 'l':
434855caec6SPeter Avalos if (!pr_program[0])
435855caec6SPeter Avalos try_help ("pagination not supported on this host", NULL);
43644b87433SJohn Marino paginate = true;
437855caec6SPeter Avalos #ifdef SIGCHLD
438855caec6SPeter Avalos /* Pagination requires forking and waiting, and
439855caec6SPeter Avalos System V fork+wait does not work if SIGCHLD is ignored. */
440855caec6SPeter Avalos signal (SIGCHLD, SIG_DFL);
441855caec6SPeter Avalos #endif
442855caec6SPeter Avalos break;
443855caec6SPeter Avalos
444855caec6SPeter Avalos case 'L':
445855caec6SPeter Avalos if (!file_label[0])
446855caec6SPeter Avalos file_label[0] = optarg;
447855caec6SPeter Avalos else if (!file_label[1])
448855caec6SPeter Avalos file_label[1] = optarg;
449855caec6SPeter Avalos else
450855caec6SPeter Avalos fatal ("too many file label options");
451855caec6SPeter Avalos break;
452855caec6SPeter Avalos
453855caec6SPeter Avalos case 'n':
454855caec6SPeter Avalos specify_style (OUTPUT_RCS);
455855caec6SPeter Avalos break;
456855caec6SPeter Avalos
457855caec6SPeter Avalos case 'N':
458855caec6SPeter Avalos new_file = true;
459855caec6SPeter Avalos break;
460855caec6SPeter Avalos
461855caec6SPeter Avalos case 'p':
462855caec6SPeter Avalos show_c_function = true;
463855caec6SPeter Avalos add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
464855caec6SPeter Avalos break;
465855caec6SPeter Avalos
466855caec6SPeter Avalos case 'P':
467855caec6SPeter Avalos unidirectional_new_file = true;
468855caec6SPeter Avalos break;
469855caec6SPeter Avalos
470855caec6SPeter Avalos case 'q':
471855caec6SPeter Avalos brief = true;
472855caec6SPeter Avalos break;
473855caec6SPeter Avalos
474855caec6SPeter Avalos case 'r':
475855caec6SPeter Avalos recursive = true;
476855caec6SPeter Avalos break;
477855caec6SPeter Avalos
478855caec6SPeter Avalos case 's':
479855caec6SPeter Avalos report_identical_files = true;
480855caec6SPeter Avalos break;
481855caec6SPeter Avalos
482855caec6SPeter Avalos case 'S':
483855caec6SPeter Avalos specify_value (&starting_file, optarg, "-S");
484855caec6SPeter Avalos break;
485855caec6SPeter Avalos
486855caec6SPeter Avalos case 't':
487855caec6SPeter Avalos expand_tabs = true;
488855caec6SPeter Avalos break;
489855caec6SPeter Avalos
490855caec6SPeter Avalos case 'T':
491855caec6SPeter Avalos initial_tab = true;
492855caec6SPeter Avalos break;
493855caec6SPeter Avalos
494855caec6SPeter Avalos case 'u':
495855caec6SPeter Avalos specify_style (OUTPUT_UNIFIED);
496855caec6SPeter Avalos if (context < 3)
497855caec6SPeter Avalos context = 3;
498855caec6SPeter Avalos break;
499855caec6SPeter Avalos
500855caec6SPeter Avalos case 'v':
501855caec6SPeter Avalos version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
502008e37b6SJohn Marino AUTHORS, (char *) NULL);
50344b87433SJohn Marino check_stdout ();
504855caec6SPeter Avalos return EXIT_SUCCESS;
505855caec6SPeter Avalos
506855caec6SPeter Avalos case 'w':
507855caec6SPeter Avalos ignore_white_space = IGNORE_ALL_SPACE;
508855caec6SPeter Avalos break;
509855caec6SPeter Avalos
510855caec6SPeter Avalos case 'x':
511855caec6SPeter Avalos add_exclude (excluded, optarg, exclude_options ());
512855caec6SPeter Avalos break;
513855caec6SPeter Avalos
514855caec6SPeter Avalos case 'X':
515855caec6SPeter Avalos if (add_exclude_file (add_exclude, excluded, optarg,
516855caec6SPeter Avalos exclude_options (), '\n'))
517855caec6SPeter Avalos pfatal_with_name (optarg);
518855caec6SPeter Avalos break;
519855caec6SPeter Avalos
520855caec6SPeter Avalos case 'y':
521855caec6SPeter Avalos specify_style (OUTPUT_SDIFF);
522855caec6SPeter Avalos break;
523855caec6SPeter Avalos
524855caec6SPeter Avalos case 'W':
525855caec6SPeter Avalos numval = strtoumax (optarg, &numend, 10);
526855caec6SPeter Avalos if (! (0 < numval && numval <= SIZE_MAX) || *numend)
527855caec6SPeter Avalos try_help ("invalid width '%s'", optarg);
5284536c563SJohn Marino if (width != numval)
529855caec6SPeter Avalos {
530855caec6SPeter Avalos if (width)
531855caec6SPeter Avalos fatal ("conflicting width options");
532855caec6SPeter Avalos width = numval;
533855caec6SPeter Avalos }
534855caec6SPeter Avalos break;
535855caec6SPeter Avalos
536855caec6SPeter Avalos case BINARY_OPTION:
537855caec6SPeter Avalos #if O_BINARY
53844b87433SJohn Marino binary = true;
539855caec6SPeter Avalos if (! isatty (STDOUT_FILENO))
54044b87433SJohn Marino set_binary_mode (STDOUT_FILENO, O_BINARY);
5414536c563SJohn Marino #endif
542855caec6SPeter Avalos break;
543855caec6SPeter Avalos
544855caec6SPeter Avalos case FROM_FILE_OPTION:
545855caec6SPeter Avalos specify_value (&from_file, optarg, "--from-file");
546855caec6SPeter Avalos break;
547855caec6SPeter Avalos
548855caec6SPeter Avalos case HELP_OPTION:
549855caec6SPeter Avalos usage ();
550855caec6SPeter Avalos check_stdout ();
551855caec6SPeter Avalos return EXIT_SUCCESS;
552855caec6SPeter Avalos
553855caec6SPeter Avalos case HORIZON_LINES_OPTION:
554855caec6SPeter Avalos numval = strtoumax (optarg, &numend, 10);
555855caec6SPeter Avalos if (*numend)
556855caec6SPeter Avalos try_help ("invalid horizon length '%s'", optarg);
5574536c563SJohn Marino horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
558855caec6SPeter Avalos break;
559855caec6SPeter Avalos
560855caec6SPeter Avalos case IGNORE_FILE_NAME_CASE_OPTION:
561855caec6SPeter Avalos ignore_file_name_case = true;
562855caec6SPeter Avalos break;
563855caec6SPeter Avalos
564855caec6SPeter Avalos case INHIBIT_HUNK_MERGE_OPTION:
565855caec6SPeter Avalos /* This option is obsolete, but accept it for backward
566855caec6SPeter Avalos compatibility. */
567855caec6SPeter Avalos break;
568855caec6SPeter Avalos
569855caec6SPeter Avalos case LEFT_COLUMN_OPTION:
570855caec6SPeter Avalos left_column = true;
571855caec6SPeter Avalos break;
572855caec6SPeter Avalos
573855caec6SPeter Avalos case LINE_FORMAT_OPTION:
574855caec6SPeter Avalos specify_style (OUTPUT_IFDEF);
575855caec6SPeter Avalos for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
57644b87433SJohn Marino specify_value (&line_format[i], optarg, "--line-format");
577855caec6SPeter Avalos break;
578855caec6SPeter Avalos
579855caec6SPeter Avalos case NO_DEREFERENCE_OPTION:
5804536c563SJohn Marino no_dereference_symlinks = true;
5814536c563SJohn Marino break;
5824536c563SJohn Marino
5834536c563SJohn Marino case NO_IGNORE_FILE_NAME_CASE_OPTION:
584855caec6SPeter Avalos ignore_file_name_case = false;
585855caec6SPeter Avalos break;
586855caec6SPeter Avalos
587855caec6SPeter Avalos case NORMAL_OPTION:
588855caec6SPeter Avalos specify_style (OUTPUT_NORMAL);
589855caec6SPeter Avalos break;
590855caec6SPeter Avalos
591855caec6SPeter Avalos case SDIFF_MERGE_ASSIST_OPTION:
592855caec6SPeter Avalos specify_style (OUTPUT_SDIFF);
593855caec6SPeter Avalos sdiff_merge_assist = true;
594855caec6SPeter Avalos break;
595855caec6SPeter Avalos
596855caec6SPeter Avalos case STRIP_TRAILING_CR_OPTION:
597855caec6SPeter Avalos strip_trailing_cr = true;
598855caec6SPeter Avalos break;
599855caec6SPeter Avalos
600855caec6SPeter Avalos case SUPPRESS_BLANK_EMPTY_OPTION:
60144b87433SJohn Marino suppress_blank_empty = true;
60244b87433SJohn Marino break;
60344b87433SJohn Marino
60444b87433SJohn Marino case SUPPRESS_COMMON_LINES_OPTION:
605855caec6SPeter Avalos suppress_common_lines = true;
606855caec6SPeter Avalos break;
607855caec6SPeter Avalos
608855caec6SPeter Avalos case TABSIZE_OPTION:
609855caec6SPeter Avalos numval = strtoumax (optarg, &numend, 10);
610855caec6SPeter Avalos if (! (0 < numval && numval <= SIZE_MAX - GUTTER_WIDTH_MINIMUM)
611*6ea1f93eSDaniel Fojt || *numend)
612*6ea1f93eSDaniel Fojt try_help ("invalid tabsize '%s'", optarg);
6134536c563SJohn Marino if (tabsize != numval)
614855caec6SPeter Avalos {
615855caec6SPeter Avalos if (tabsize)
616855caec6SPeter Avalos fatal ("conflicting tabsize options");
617855caec6SPeter Avalos tabsize = numval;
618855caec6SPeter Avalos }
619855caec6SPeter Avalos break;
620855caec6SPeter Avalos
621855caec6SPeter Avalos case TO_FILE_OPTION:
622855caec6SPeter Avalos specify_value (&to_file, optarg, "--to-file");
623855caec6SPeter Avalos break;
624855caec6SPeter Avalos
625855caec6SPeter Avalos case UNCHANGED_LINE_FORMAT_OPTION:
626855caec6SPeter Avalos case OLD_LINE_FORMAT_OPTION:
627855caec6SPeter Avalos case NEW_LINE_FORMAT_OPTION:
628855caec6SPeter Avalos specify_style (OUTPUT_IFDEF);
629855caec6SPeter Avalos c -= UNCHANGED_LINE_FORMAT_OPTION;
630855caec6SPeter Avalos specify_value (&line_format[c], optarg, line_format_option[c]);
631855caec6SPeter Avalos break;
632855caec6SPeter Avalos
633855caec6SPeter Avalos case UNCHANGED_GROUP_FORMAT_OPTION:
634855caec6SPeter Avalos case OLD_GROUP_FORMAT_OPTION:
635855caec6SPeter Avalos case NEW_GROUP_FORMAT_OPTION:
636855caec6SPeter Avalos case CHANGED_GROUP_FORMAT_OPTION:
637855caec6SPeter Avalos specify_style (OUTPUT_IFDEF);
638855caec6SPeter Avalos c -= UNCHANGED_GROUP_FORMAT_OPTION;
639855caec6SPeter Avalos specify_value (&group_format[c], optarg, group_format_option[c]);
640855caec6SPeter Avalos break;
641855caec6SPeter Avalos
642855caec6SPeter Avalos case COLOR_OPTION:
643*6ea1f93eSDaniel Fojt specify_colors_style (optarg);
644*6ea1f93eSDaniel Fojt break;
645*6ea1f93eSDaniel Fojt
646*6ea1f93eSDaniel Fojt case COLOR_PALETTE_OPTION:
647*6ea1f93eSDaniel Fojt set_color_palette (optarg);
648*6ea1f93eSDaniel Fojt break;
649*6ea1f93eSDaniel Fojt
650*6ea1f93eSDaniel Fojt case PRESUME_OUTPUT_TTY_OPTION:
651*6ea1f93eSDaniel Fojt presume_output_tty = true;
652*6ea1f93eSDaniel Fojt break;
653*6ea1f93eSDaniel Fojt
654*6ea1f93eSDaniel Fojt default:
655855caec6SPeter Avalos try_help (NULL, NULL);
65644b87433SJohn Marino }
657855caec6SPeter Avalos prev = c;
658855caec6SPeter Avalos }
659855caec6SPeter Avalos
660855caec6SPeter Avalos if (colors_style == AUTO)
661*6ea1f93eSDaniel Fojt {
662*6ea1f93eSDaniel Fojt char const *t = getenv ("TERM");
663*6ea1f93eSDaniel Fojt if (t && STREQ (t, "dumb"))
664*6ea1f93eSDaniel Fojt colors_style = NEVER;
665*6ea1f93eSDaniel Fojt }
666*6ea1f93eSDaniel Fojt
667*6ea1f93eSDaniel Fojt if (output_style == OUTPUT_UNSPECIFIED)
668855caec6SPeter Avalos {
669855caec6SPeter Avalos if (show_c_function)
670855caec6SPeter Avalos {
671855caec6SPeter Avalos specify_style (OUTPUT_CONTEXT);
672855caec6SPeter Avalos if (ocontext < 0)
673855caec6SPeter Avalos context = 3;
674855caec6SPeter Avalos }
675855caec6SPeter Avalos else
676855caec6SPeter Avalos specify_style (OUTPUT_NORMAL);
677855caec6SPeter Avalos }
678855caec6SPeter Avalos
679855caec6SPeter Avalos if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
680855caec6SPeter Avalos {
681855caec6SPeter Avalos #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
68244b87433SJohn Marino || defined HAVE_STRUCT_STAT_ST_SPARE1)
68344b87433SJohn Marino time_format = "%Y-%m-%d %H:%M:%S.%N %z";
684855caec6SPeter Avalos #else
685855caec6SPeter Avalos time_format = "%Y-%m-%d %H:%M:%S %z";
686855caec6SPeter Avalos #endif
687855caec6SPeter Avalos }
688855caec6SPeter Avalos else
689855caec6SPeter Avalos {
690855caec6SPeter Avalos /* See POSIX 1003.1-2001 for this format. */
691855caec6SPeter Avalos time_format = "%a %b %e %T %Y";
692855caec6SPeter Avalos }
693855caec6SPeter Avalos
694855caec6SPeter Avalos if (0 <= ocontext
69544b87433SJohn Marino && (output_style == OUTPUT_CONTEXT
69644b87433SJohn Marino || output_style == OUTPUT_UNIFIED)
697855caec6SPeter Avalos && (context < ocontext
698855caec6SPeter Avalos || (ocontext < context && ! explicit_context)))
699855caec6SPeter Avalos context = ocontext;
700855caec6SPeter Avalos
701855caec6SPeter Avalos if (! tabsize)
702855caec6SPeter Avalos tabsize = 8;
703855caec6SPeter Avalos if (! width)
704855caec6SPeter Avalos width = 130;
705855caec6SPeter Avalos
706855caec6SPeter Avalos {
707855caec6SPeter Avalos /* Maximize first the half line width, and then the gutter width,
708855caec6SPeter Avalos according to the following constraints:
709855caec6SPeter Avalos
710855caec6SPeter Avalos 1. Two half lines plus a gutter must fit in a line.
711855caec6SPeter Avalos 2. If the half line width is nonzero:
712855caec6SPeter Avalos a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
713855caec6SPeter Avalos b. If tabs are not expanded to spaces,
714855caec6SPeter Avalos a half line plus a gutter is an integral number of tabs,
715855caec6SPeter Avalos so that tabs in the right column line up. */
716855caec6SPeter Avalos
717855caec6SPeter Avalos size_t t = expand_tabs ? 1 : tabsize;
718*6ea1f93eSDaniel Fojt size_t w = width;
719*6ea1f93eSDaniel Fojt size_t t_plus_g = t + GUTTER_WIDTH_MINIMUM;
720*6ea1f93eSDaniel Fojt size_t unaligned_off = (w >> 1) + (t_plus_g >> 1) + (w & t_plus_g & 1);
721*6ea1f93eSDaniel Fojt size_t off = unaligned_off - unaligned_off % t;
722*6ea1f93eSDaniel Fojt sdiff_half_width = (off <= GUTTER_WIDTH_MINIMUM || w <= off
723*6ea1f93eSDaniel Fojt ? 0
724*6ea1f93eSDaniel Fojt : MIN (off - GUTTER_WIDTH_MINIMUM, w - off));
725*6ea1f93eSDaniel Fojt sdiff_column2_offset = sdiff_half_width ? off : w;
726855caec6SPeter Avalos }
727855caec6SPeter Avalos
728855caec6SPeter Avalos /* Make the horizon at least as large as the context, so that
729855caec6SPeter Avalos shift_boundaries has more freedom to shift the first and last hunks. */
730855caec6SPeter Avalos if (horizon_lines < context)
731855caec6SPeter Avalos horizon_lines = context;
732855caec6SPeter Avalos
733855caec6SPeter Avalos summarize_regexp_list (&function_regexp_list);
734855caec6SPeter Avalos summarize_regexp_list (&ignore_regexp_list);
735855caec6SPeter Avalos
736855caec6SPeter Avalos if (output_style == OUTPUT_IFDEF)
737855caec6SPeter Avalos {
738855caec6SPeter Avalos for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
73944b87433SJohn Marino if (!line_format[i])
740855caec6SPeter Avalos line_format[i] = "%l\n";
741855caec6SPeter Avalos if (!group_format[OLD])
742855caec6SPeter Avalos group_format[OLD]
743855caec6SPeter Avalos = group_format[CHANGED] ? group_format[CHANGED] : "%<";
744855caec6SPeter Avalos if (!group_format[NEW])
745855caec6SPeter Avalos group_format[NEW]
746855caec6SPeter Avalos = group_format[CHANGED] ? group_format[CHANGED] : "%>";
747855caec6SPeter Avalos if (!group_format[UNCHANGED])
748855caec6SPeter Avalos group_format[UNCHANGED] = "%=";
749855caec6SPeter Avalos if (!group_format[CHANGED])
750855caec6SPeter Avalos group_format[CHANGED] = concat (group_format[OLD],
751855caec6SPeter Avalos group_format[NEW], "");
752855caec6SPeter Avalos }
753855caec6SPeter Avalos
754855caec6SPeter Avalos no_diff_means_no_output =
755855caec6SPeter Avalos (output_style == OUTPUT_IFDEF ?
756855caec6SPeter Avalos (!*group_format[UNCHANGED]
757855caec6SPeter Avalos || (STREQ (group_format[UNCHANGED], "%=")
75844b87433SJohn Marino && !*line_format[UNCHANGED]))
759855caec6SPeter Avalos : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
760855caec6SPeter Avalos
761855caec6SPeter Avalos files_can_be_treated_as_binary =
762855caec6SPeter Avalos (brief & binary
763855caec6SPeter Avalos & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
764855caec6SPeter Avalos | (ignore_regexp_list.regexps || ignore_white_space)));
765855caec6SPeter Avalos
766855caec6SPeter Avalos switch_string = option_list (argv + 1, optind - 1);
767855caec6SPeter Avalos
768855caec6SPeter Avalos if (from_file)
769855caec6SPeter Avalos {
770855caec6SPeter Avalos if (to_file)
771855caec6SPeter Avalos fatal ("--from-file and --to-file both specified");
772855caec6SPeter Avalos else
773855caec6SPeter Avalos for (; optind < argc; optind++)
774855caec6SPeter Avalos {
775855caec6SPeter Avalos int status = compare_files (NULL, from_file, argv[optind]);
77644b87433SJohn Marino if (exit_status < status)
777855caec6SPeter Avalos exit_status = status;
778855caec6SPeter Avalos }
779855caec6SPeter Avalos }
780855caec6SPeter Avalos else
781855caec6SPeter Avalos {
782855caec6SPeter Avalos if (to_file)
783855caec6SPeter Avalos for (; optind < argc; optind++)
784855caec6SPeter Avalos {
785855caec6SPeter Avalos int status = compare_files (NULL, argv[optind], to_file);
78644b87433SJohn Marino if (exit_status < status)
787855caec6SPeter Avalos exit_status = status;
788855caec6SPeter Avalos }
789855caec6SPeter Avalos else
790855caec6SPeter Avalos {
791855caec6SPeter Avalos if (argc - optind != 2)
792855caec6SPeter Avalos {
793855caec6SPeter Avalos if (argc - optind < 2)
794855caec6SPeter Avalos try_help ("missing operand after '%s'", argv[argc - 1]);
7954536c563SJohn Marino else
796855caec6SPeter Avalos try_help ("extra operand '%s'", argv[optind + 2]);
7974536c563SJohn Marino }
798855caec6SPeter Avalos
799855caec6SPeter Avalos exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
80044b87433SJohn Marino }
801855caec6SPeter Avalos }
802855caec6SPeter Avalos
803855caec6SPeter Avalos /* Print any messages that were saved up for last. */
804855caec6SPeter Avalos print_message_queue ();
805855caec6SPeter Avalos
806855caec6SPeter Avalos check_stdout ();
807855caec6SPeter Avalos exit (exit_status);
808855caec6SPeter Avalos return exit_status;
809855caec6SPeter Avalos }
810855caec6SPeter Avalos
811855caec6SPeter Avalos /* Append to REGLIST the regexp PATTERN. */
812855caec6SPeter Avalos
813855caec6SPeter Avalos static void
add_regexp(struct regexp_list * reglist,char const * pattern)814855caec6SPeter Avalos add_regexp (struct regexp_list *reglist, char const *pattern)
815855caec6SPeter Avalos {
816855caec6SPeter Avalos size_t patlen = strlen (pattern);
817855caec6SPeter Avalos char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
818855caec6SPeter Avalos
819855caec6SPeter Avalos if (m != 0)
820855caec6SPeter Avalos error (EXIT_TROUBLE, 0, "%s: %s", pattern, m);
821*6ea1f93eSDaniel Fojt else
822855caec6SPeter Avalos {
823855caec6SPeter Avalos char *regexps = reglist->regexps;
824855caec6SPeter Avalos size_t len = reglist->len;
825855caec6SPeter Avalos bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
826855caec6SPeter Avalos size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
827855caec6SPeter Avalos size_t size = reglist->size;
828855caec6SPeter Avalos
829855caec6SPeter Avalos if (size <= newlen)
830855caec6SPeter Avalos {
831855caec6SPeter Avalos if (!size)
832855caec6SPeter Avalos size = 1;
833855caec6SPeter Avalos
834855caec6SPeter Avalos do size *= 2;
835855caec6SPeter Avalos while (size <= newlen);
836855caec6SPeter Avalos
837855caec6SPeter Avalos reglist->size = size;
838855caec6SPeter Avalos reglist->regexps = regexps = xrealloc (regexps, size);
839855caec6SPeter Avalos }
840855caec6SPeter Avalos if (multiple_regexps)
841855caec6SPeter Avalos {
842855caec6SPeter Avalos regexps[len++] = '\\';
843855caec6SPeter Avalos regexps[len++] = '|';
844855caec6SPeter Avalos }
845855caec6SPeter Avalos memcpy (regexps + len, pattern, patlen + 1);
846855caec6SPeter Avalos }
847855caec6SPeter Avalos }
848855caec6SPeter Avalos
849855caec6SPeter Avalos /* Ensure that REGLIST represents the disjunction of its regexps.
850855caec6SPeter Avalos This is done here, rather than earlier, to avoid O(N^2) behavior. */
851855caec6SPeter Avalos
852855caec6SPeter Avalos static void
summarize_regexp_list(struct regexp_list * reglist)853855caec6SPeter Avalos summarize_regexp_list (struct regexp_list *reglist)
854855caec6SPeter Avalos {
855855caec6SPeter Avalos if (reglist->regexps)
856855caec6SPeter Avalos {
857855caec6SPeter Avalos /* At least one regexp was specified. Allocate a fastmap for it. */
858855caec6SPeter Avalos reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
859855caec6SPeter Avalos if (reglist->multiple_regexps)
860855caec6SPeter Avalos {
861855caec6SPeter Avalos /* Compile the disjunction of the regexps.
862855caec6SPeter Avalos (If just one regexp was specified, it is already compiled.) */
863855caec6SPeter Avalos char const *m = re_compile_pattern (reglist->regexps, reglist->len,
864855caec6SPeter Avalos reglist->buf);
865855caec6SPeter Avalos if (m)
86644b87433SJohn Marino die (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
867*6ea1f93eSDaniel Fojt }
868855caec6SPeter Avalos }
869855caec6SPeter Avalos }
870855caec6SPeter Avalos
871855caec6SPeter Avalos static void
try_help(char const * reason_msgid,char const * operand)872855caec6SPeter Avalos try_help (char const *reason_msgid, char const *operand)
873855caec6SPeter Avalos {
874855caec6SPeter Avalos if (reason_msgid)
875855caec6SPeter Avalos error (0, 0, _(reason_msgid), operand);
876855caec6SPeter Avalos die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
877*6ea1f93eSDaniel Fojt program_name);
878855caec6SPeter Avalos }
879855caec6SPeter Avalos
880855caec6SPeter Avalos static void
check_stdout(void)881855caec6SPeter Avalos check_stdout (void)
882855caec6SPeter Avalos {
883855caec6SPeter Avalos if (ferror (stdout))
884855caec6SPeter Avalos fatal ("write failed");
885855caec6SPeter Avalos else if (fclose (stdout) != 0)
886855caec6SPeter Avalos pfatal_with_name (_("standard output"));
887855caec6SPeter Avalos }
888855caec6SPeter Avalos
889855caec6SPeter Avalos static char const * const option_help_msgid[] = {
890855caec6SPeter Avalos N_(" --normal output a normal diff (the default)"),
891008e37b6SJohn Marino N_("-q, --brief report only when files differ"),
892008e37b6SJohn Marino N_("-s, --report-identical-files report when two files are the same"),
893008e37b6SJohn Marino N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
894008e37b6SJohn Marino N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
895008e37b6SJohn Marino N_("-e, --ed output an ed script"),
896008e37b6SJohn Marino N_("-n, --rcs output an RCS format diff"),
897008e37b6SJohn Marino N_("-y, --side-by-side output in two columns"),
898008e37b6SJohn Marino N_("-W, --width=NUM output at most NUM (default 130) print columns"),
899008e37b6SJohn Marino N_(" --left-column output only the left column of common lines"),
900008e37b6SJohn Marino N_(" --suppress-common-lines do not output common lines"),
901008e37b6SJohn Marino "",
902855caec6SPeter Avalos N_("-p, --show-c-function show which C function each change is in"),
903008e37b6SJohn Marino N_("-F, --show-function-line=RE show the most recent line matching RE"),
904008e37b6SJohn Marino N_(" --label LABEL use LABEL instead of file name and timestamp\n"
905*6ea1f93eSDaniel Fojt " (can be repeated)"),
906008e37b6SJohn Marino "",
907008e37b6SJohn Marino N_("-t, --expand-tabs expand tabs to spaces in output"),
908008e37b6SJohn Marino N_("-T, --initial-tab make tabs line up by prepending a tab"),
909008e37b6SJohn Marino N_(" --tabsize=NUM tab stops every NUM (default 8) print columns"),
910008e37b6SJohn Marino N_(" --suppress-blank-empty suppress space or tab before empty output lines"),
911008e37b6SJohn Marino N_("-l, --paginate pass output through 'pr' to paginate it"),
9124536c563SJohn Marino "",
913008e37b6SJohn Marino N_("-r, --recursive recursively compare any subdirectories found"),
914008e37b6SJohn Marino N_(" --no-dereference don't follow symbolic links"),
9154536c563SJohn Marino N_("-N, --new-file treat absent files as empty"),
916008e37b6SJohn Marino N_(" --unidirectional-new-file treat absent first files as empty"),
917008e37b6SJohn Marino N_(" --ignore-file-name-case ignore case when comparing file names"),
918008e37b6SJohn Marino N_(" --no-ignore-file-name-case consider case when comparing file names"),
919008e37b6SJohn Marino N_("-x, --exclude=PAT exclude files that match PAT"),
920008e37b6SJohn Marino N_("-X, --exclude-from=FILE exclude files that match any pattern in FILE"),
921008e37b6SJohn Marino N_("-S, --starting-file=FILE start with FILE when comparing directories"),
922008e37b6SJohn Marino N_(" --from-file=FILE1 compare FILE1 to all operands;\n"
923008e37b6SJohn Marino " FILE1 can be a directory"),
924008e37b6SJohn Marino N_(" --to-file=FILE2 compare all operands to FILE2;\n"
925008e37b6SJohn Marino " FILE2 can be a directory"),
926008e37b6SJohn Marino "",
927008e37b6SJohn Marino N_("-i, --ignore-case ignore case differences in file contents"),
928008e37b6SJohn Marino N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
929008e37b6SJohn Marino N_("-Z, --ignore-trailing-space ignore white space at line end"),
930008e37b6SJohn Marino N_("-b, --ignore-space-change ignore changes in the amount of white space"),
931008e37b6SJohn Marino N_("-w, --ignore-all-space ignore all white space"),
932008e37b6SJohn Marino N_("-B, --ignore-blank-lines ignore changes where lines are all blank"),
9334536c563SJohn Marino N_("-I, --ignore-matching-lines=RE ignore changes where all lines match RE"),
9344536c563SJohn Marino "",
935008e37b6SJohn Marino N_("-a, --text treat all files as text"),
936008e37b6SJohn Marino N_(" --strip-trailing-cr strip trailing carriage return on input"),
937008e37b6SJohn Marino #if O_BINARY
93844b87433SJohn Marino N_(" --binary read and write data in binary mode"),
939008e37b6SJohn Marino #endif
940855caec6SPeter Avalos "",
941855caec6SPeter Avalos N_("-D, --ifdef=NAME output merged file with '#ifdef NAME' diffs"),
9424536c563SJohn Marino N_(" --GTYPE-group-format=GFMT format GTYPE input groups with GFMT"),
943008e37b6SJohn Marino N_(" --line-format=LFMT format all input lines with LFMT"),
944008e37b6SJohn Marino N_(" --LTYPE-line-format=LFMT format LTYPE input lines with LFMT"),
945008e37b6SJohn Marino N_(" These format options provide fine-grained control over the output\n"
946008e37b6SJohn Marino " of diff, generalizing -D/--ifdef."),
947008e37b6SJohn Marino N_(" LTYPE is 'old', 'new', or 'unchanged'. GTYPE is LTYPE or 'changed'."),
9484536c563SJohn Marino N_(" GFMT (only) may contain:\n\
949008e37b6SJohn Marino %< lines from FILE1\n\
950855caec6SPeter Avalos %> lines from FILE2\n\
951855caec6SPeter Avalos %= lines common to FILE1 and FILE2\n\
952855caec6SPeter Avalos %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
953855caec6SPeter Avalos LETTERs are as follows for new group, lower case for old group:\n\
954855caec6SPeter Avalos F first line number\n\
955855caec6SPeter Avalos L last line number\n\
956855caec6SPeter Avalos N number of lines = L-F+1\n\
957855caec6SPeter Avalos E F-1\n\
958855caec6SPeter Avalos M L+1\n\
959008e37b6SJohn Marino %(A=B?T:E) if A equals B then T else E"),
960008e37b6SJohn Marino N_(" LFMT (only) may contain:\n\
961008e37b6SJohn Marino %L contents of line\n\
962855caec6SPeter Avalos %l contents of line, excluding any trailing newline\n\
963855caec6SPeter Avalos %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
964855caec6SPeter Avalos N_(" Both GFMT and LFMT may contain:\n\
965008e37b6SJohn Marino %% %\n\
966855caec6SPeter Avalos %c'C' the single character C\n\
967855caec6SPeter Avalos %c'\\OOO' the character with octal code OOO\n\
968008e37b6SJohn Marino C the character C (other characters represent themselves)"),
969008e37b6SJohn Marino "",
970855caec6SPeter Avalos N_("-d, --minimal try hard to find a smaller set of changes"),
971008e37b6SJohn Marino N_(" --horizon-lines=NUM keep NUM lines of the common prefix and suffix"),
972008e37b6SJohn Marino N_(" --speed-large-files assume large files and many scattered small changes"),
973008e37b6SJohn Marino N_(" --color[=WHEN] colorize the output; WHEN can be 'never', 'always',\n"
974*6ea1f93eSDaniel Fojt " or 'auto' (the default)"),
975*6ea1f93eSDaniel Fojt N_(" --palette=PALETTE the colors to use when --color is active; PALETTE is\n"
976*6ea1f93eSDaniel Fojt " a colon-separated list of terminfo capabilities"),
977*6ea1f93eSDaniel Fojt "",
978855caec6SPeter Avalos N_(" --help display this help and exit"),
979008e37b6SJohn Marino N_("-v, --version output version information and exit"),
980008e37b6SJohn Marino "",
981855caec6SPeter Avalos N_("FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'."),
982*6ea1f93eSDaniel Fojt N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
983008e37b6SJohn Marino N_("If a FILE is '-', read standard input."),
9844536c563SJohn Marino N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
985855caec6SPeter Avalos 0
986855caec6SPeter Avalos };
987855caec6SPeter Avalos
988855caec6SPeter Avalos static void
usage(void)989855caec6SPeter Avalos usage (void)
990855caec6SPeter Avalos {
991855caec6SPeter Avalos char const * const *p;
992855caec6SPeter Avalos
993855caec6SPeter Avalos printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
994855caec6SPeter Avalos printf ("%s\n\n", _("Compare FILES line by line."));
995008e37b6SJohn Marino
996008e37b6SJohn Marino fputs (_("\
997008e37b6SJohn Marino Mandatory arguments to long options are mandatory for short options too.\n\
998008e37b6SJohn Marino "), stdout);
999008e37b6SJohn Marino
1000855caec6SPeter Avalos for (p = option_help_msgid; *p; p++)
1001855caec6SPeter Avalos {
1002855caec6SPeter Avalos if (!**p)
1003855caec6SPeter Avalos putchar ('\n');
1004855caec6SPeter Avalos else
1005855caec6SPeter Avalos {
1006855caec6SPeter Avalos char const *msg = _(*p);
1007855caec6SPeter Avalos char const *nl;
1008855caec6SPeter Avalos while ((nl = strchr (msg, '\n')))
1009855caec6SPeter Avalos {
1010855caec6SPeter Avalos int msglen = nl + 1 - msg;
1011855caec6SPeter Avalos /* This assertion is solely to avoid a warning from
1012*6ea1f93eSDaniel Fojt gcc's -Wformat-overflow=. */
1013*6ea1f93eSDaniel Fojt assert (msglen < 4096);
1014*6ea1f93eSDaniel Fojt printf (" %.*s", msglen, msg);
1015855caec6SPeter Avalos msg = nl + 1;
1016855caec6SPeter Avalos }
1017855caec6SPeter Avalos
1018855caec6SPeter Avalos printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
1019855caec6SPeter Avalos }
1020855caec6SPeter Avalos }
1021855caec6SPeter Avalos emit_bug_reporting_address ();
102244b87433SJohn Marino }
1023855caec6SPeter Avalos
1024855caec6SPeter Avalos /* Set VAR to VALUE, reporting an OPTION error if this is a
1025855caec6SPeter Avalos conflict. */
1026855caec6SPeter Avalos static void
specify_value(char const ** var,char const * value,char const * option)1027855caec6SPeter Avalos specify_value (char const **var, char const *value, char const *option)
1028855caec6SPeter Avalos {
1029855caec6SPeter Avalos if (*var && ! STREQ (*var, value))
1030008e37b6SJohn Marino {
1031855caec6SPeter Avalos error (0, 0, _("conflicting %s option value '%s'"), option, value);
10324536c563SJohn Marino try_help (NULL, NULL);
103344b87433SJohn Marino }
1034855caec6SPeter Avalos *var = value;
1035855caec6SPeter Avalos }
1036855caec6SPeter Avalos
1037855caec6SPeter Avalos /* Set the output style to STYLE, diagnosing conflicts. */
1038855caec6SPeter Avalos static void
specify_style(enum output_style style)1039855caec6SPeter Avalos specify_style (enum output_style style)
1040855caec6SPeter Avalos {
1041855caec6SPeter Avalos if (output_style != style)
1042855caec6SPeter Avalos {
1043855caec6SPeter Avalos if (output_style != OUTPUT_UNSPECIFIED)
1044855caec6SPeter Avalos try_help ("conflicting output style options", NULL);
104544b87433SJohn Marino output_style = style;
1046855caec6SPeter Avalos }
1047855caec6SPeter Avalos }
1048855caec6SPeter Avalos
1049*6ea1f93eSDaniel Fojt /* Set the color mode. */
1050*6ea1f93eSDaniel Fojt static void
specify_colors_style(char const * value)1051*6ea1f93eSDaniel Fojt specify_colors_style (char const *value)
1052*6ea1f93eSDaniel Fojt {
1053*6ea1f93eSDaniel Fojt if (value == NULL || STREQ (value, "auto"))
1054*6ea1f93eSDaniel Fojt colors_style = AUTO;
1055*6ea1f93eSDaniel Fojt else if (STREQ (value, "always"))
1056*6ea1f93eSDaniel Fojt colors_style = ALWAYS;
1057*6ea1f93eSDaniel Fojt else if (STREQ (value, "never"))
1058*6ea1f93eSDaniel Fojt colors_style = NEVER;
1059*6ea1f93eSDaniel Fojt else
1060*6ea1f93eSDaniel Fojt try_help ("invalid color '%s'", value);
1061*6ea1f93eSDaniel Fojt }
1062*6ea1f93eSDaniel Fojt
1063*6ea1f93eSDaniel Fojt
1064855caec6SPeter Avalos /* Set the last-modified time of *ST to be the current time. */
1065855caec6SPeter Avalos
1066855caec6SPeter Avalos static void
set_mtime_to_now(struct stat * st)1067855caec6SPeter Avalos set_mtime_to_now (struct stat *st)
1068855caec6SPeter Avalos {
1069855caec6SPeter Avalos #ifdef STAT_TIMESPEC
107044b87433SJohn Marino gettime (&STAT_TIMESPEC (st, st_mtim));
107144b87433SJohn Marino #else
107244b87433SJohn Marino struct timespec t;
107344b87433SJohn Marino gettime (&t);
107444b87433SJohn Marino st->st_mtime = t.tv_sec;
107544b87433SJohn Marino # if defined STAT_TIMESPEC_NS
107644b87433SJohn Marino STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
107744b87433SJohn Marino # elif defined HAVE_STRUCT_STAT_ST_SPARE1
107844b87433SJohn Marino st->st_spare1 = t.tv_nsec / 1000;
107944b87433SJohn Marino # endif
1080855caec6SPeter Avalos #endif
1081855caec6SPeter Avalos }
1082855caec6SPeter Avalos
1083855caec6SPeter Avalos /* Compare two files (or dirs) with parent comparison PARENT
1084855caec6SPeter Avalos and names NAME0 and NAME1.
1085855caec6SPeter Avalos (If PARENT is null, then the first name is just NAME0, etc.)
108644b87433SJohn Marino This is self-contained; it opens the files and closes them.
1087855caec6SPeter Avalos
1088855caec6SPeter Avalos Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
1089855caec6SPeter Avalos different, EXIT_TROUBLE if there is a problem opening them. */
1090855caec6SPeter Avalos
1091855caec6SPeter Avalos static int
compare_files(struct comparison const * parent,char const * name0,char const * name1)1092855caec6SPeter Avalos compare_files (struct comparison const *parent,
1093855caec6SPeter Avalos char const *name0,
1094855caec6SPeter Avalos char const *name1)
1095855caec6SPeter Avalos {
1096855caec6SPeter Avalos struct comparison cmp;
1097855caec6SPeter Avalos #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
1098855caec6SPeter Avalos register int f;
1099855caec6SPeter Avalos int status = EXIT_SUCCESS;
1100855caec6SPeter Avalos bool same_files;
1101855caec6SPeter Avalos char *free0;
110244b87433SJohn Marino char *free1;
110344b87433SJohn Marino
1104855caec6SPeter Avalos /* If this is directory comparison, perhaps we have a file
1105855caec6SPeter Avalos that exists only in one of the directories.
1106855caec6SPeter Avalos If so, just print a message to that effect. */
1107855caec6SPeter Avalos
1108855caec6SPeter Avalos if (! ((name0 && name1)
1109855caec6SPeter Avalos || (unidirectional_new_file && name1)
1110855caec6SPeter Avalos || new_file))
1111855caec6SPeter Avalos {
1112855caec6SPeter Avalos char const *name = name0 ? name0 : name1;
111344b87433SJohn Marino char const *dir = parent->file[!name0].name;
111444b87433SJohn Marino
1115855caec6SPeter Avalos /* See POSIX 1003.1-2001 for this format. */
1116855caec6SPeter Avalos message ("Only in %s: %s\n", dir, name);
1117855caec6SPeter Avalos
1118855caec6SPeter Avalos /* Return EXIT_FAILURE so that diff_dirs will return
1119855caec6SPeter Avalos EXIT_FAILURE ("some files differ"). */
1120855caec6SPeter Avalos return EXIT_FAILURE;
1121855caec6SPeter Avalos }
1122855caec6SPeter Avalos
1123855caec6SPeter Avalos memset (cmp.file, 0, sizeof cmp.file);
1124855caec6SPeter Avalos cmp.parent = parent;
1125855caec6SPeter Avalos
1126855caec6SPeter Avalos /* cmp.file[f].desc markers */
1127855caec6SPeter Avalos #define NONEXISTENT (-1) /* nonexistent file */
1128855caec6SPeter Avalos #define UNOPENED (-2) /* unopened file (e.g. directory) */
1129855caec6SPeter Avalos #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
1130855caec6SPeter Avalos
1131855caec6SPeter Avalos #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
1132855caec6SPeter Avalos
1133855caec6SPeter Avalos cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
113444b87433SJohn Marino cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
113544b87433SJohn Marino
1136855caec6SPeter Avalos /* Now record the full name of each file, including nonexistent ones. */
1137855caec6SPeter Avalos
1138855caec6SPeter Avalos if (!name0)
113944b87433SJohn Marino name0 = name1;
1140855caec6SPeter Avalos if (!name1)
114144b87433SJohn Marino name1 = name0;
1142855caec6SPeter Avalos
1143855caec6SPeter Avalos if (!parent)
1144855caec6SPeter Avalos {
1145855caec6SPeter Avalos free0 = NULL;
114644b87433SJohn Marino free1 = NULL;
114744b87433SJohn Marino cmp.file[0].name = name0;
1148855caec6SPeter Avalos cmp.file[1].name = name1;
1149855caec6SPeter Avalos }
1150855caec6SPeter Avalos else
1151855caec6SPeter Avalos {
1152855caec6SPeter Avalos cmp.file[0].name = free0
1153855caec6SPeter Avalos = file_name_concat (parent->file[0].name, name0, NULL);
1154008e37b6SJohn Marino cmp.file[1].name = free1
1155855caec6SPeter Avalos = file_name_concat (parent->file[1].name, name1, NULL);
1156008e37b6SJohn Marino }
1157855caec6SPeter Avalos
1158855caec6SPeter Avalos /* Stat the files. */
1159855caec6SPeter Avalos
1160855caec6SPeter Avalos for (f = 0; f < 2; f++)
1161855caec6SPeter Avalos {
1162855caec6SPeter Avalos if (cmp.file[f].desc != NONEXISTENT)
1163855caec6SPeter Avalos {
1164855caec6SPeter Avalos if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
1165855caec6SPeter Avalos {
1166855caec6SPeter Avalos cmp.file[f].desc = cmp.file[0].desc;
1167855caec6SPeter Avalos cmp.file[f].stat = cmp.file[0].stat;
1168855caec6SPeter Avalos }
1169855caec6SPeter Avalos else if (STREQ (cmp.file[f].name, "-"))
117044b87433SJohn Marino {
1171855caec6SPeter Avalos cmp.file[f].desc = STDIN_FILENO;
1172855caec6SPeter Avalos if (binary && ! isatty (STDIN_FILENO))
11734536c563SJohn Marino set_binary_mode (STDIN_FILENO, O_BINARY);
11744536c563SJohn Marino if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
1175855caec6SPeter Avalos cmp.file[f].desc = ERRNO_ENCODE (errno);
1176855caec6SPeter Avalos else
1177855caec6SPeter Avalos {
1178855caec6SPeter Avalos if (S_ISREG (cmp.file[f].stat.st_mode))
1179855caec6SPeter Avalos {
1180855caec6SPeter Avalos off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
118144b87433SJohn Marino if (pos < 0)
1182855caec6SPeter Avalos cmp.file[f].desc = ERRNO_ENCODE (errno);
1183855caec6SPeter Avalos else
1184855caec6SPeter Avalos cmp.file[f].stat.st_size =
1185855caec6SPeter Avalos MAX (0, cmp.file[f].stat.st_size - pos);
1186855caec6SPeter Avalos }
1187855caec6SPeter Avalos
1188855caec6SPeter Avalos /* POSIX 1003.1-2001 requires current time for
1189855caec6SPeter Avalos stdin. */
1190855caec6SPeter Avalos set_mtime_to_now (&cmp.file[f].stat);
1191855caec6SPeter Avalos }
1192855caec6SPeter Avalos }
1193855caec6SPeter Avalos else if ((no_dereference_symlinks
11944536c563SJohn Marino ? lstat (cmp.file[f].name, &cmp.file[f].stat)
11954536c563SJohn Marino : stat (cmp.file[f].name, &cmp.file[f].stat))
11964536c563SJohn Marino != 0)
11974536c563SJohn Marino cmp.file[f].desc = ERRNO_ENCODE (errno);
1198855caec6SPeter Avalos }
1199855caec6SPeter Avalos }
1200855caec6SPeter Avalos
1201855caec6SPeter Avalos /* Mark files as nonexistent as needed for -N and -P, if they are
1202855caec6SPeter Avalos inaccessible empty regular files (the kind of files that 'patch'
1203855caec6SPeter Avalos creates to indicate nonexistent backups), or if they are
1204855caec6SPeter Avalos top-level files that do not exist but their counterparts do
1205855caec6SPeter Avalos exist. */
1206855caec6SPeter Avalos for (f = 0; f < 2; f++)
1207855caec6SPeter Avalos if ((new_file || (f == 0 && unidirectional_new_file))
1208855caec6SPeter Avalos && (cmp.file[f].desc == UNOPENED
1209855caec6SPeter Avalos ? (S_ISREG (cmp.file[f].stat.st_mode)
1210855caec6SPeter Avalos && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
1211855caec6SPeter Avalos && cmp.file[f].stat.st_size == 0)
1212855caec6SPeter Avalos : ((cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
12134536c563SJohn Marino || cmp.file[f].desc == ERRNO_ENCODE (EBADF))
12144536c563SJohn Marino && ! parent
1215855caec6SPeter Avalos && (cmp.file[1 - f].desc == UNOPENED
12164536c563SJohn Marino || cmp.file[1 - f].desc == STDIN_FILENO))))
12174536c563SJohn Marino cmp.file[f].desc = NONEXISTENT;
1218855caec6SPeter Avalos
1219855caec6SPeter Avalos for (f = 0; f < 2; f++)
1220855caec6SPeter Avalos if (cmp.file[f].desc == NONEXISTENT)
1221855caec6SPeter Avalos {
1222855caec6SPeter Avalos memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
1223855caec6SPeter Avalos cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
1224855caec6SPeter Avalos }
1225855caec6SPeter Avalos
1226855caec6SPeter Avalos for (f = 0; f < 2; f++)
1227855caec6SPeter Avalos {
1228855caec6SPeter Avalos int e = ERRNO_DECODE (cmp.file[f].desc);
1229855caec6SPeter Avalos if (0 <= e)
1230855caec6SPeter Avalos {
1231855caec6SPeter Avalos errno = e;
1232855caec6SPeter Avalos perror_with_name (cmp.file[f].name);
1233855caec6SPeter Avalos status = EXIT_TROUBLE;
1234855caec6SPeter Avalos }
1235855caec6SPeter Avalos }
1236855caec6SPeter Avalos
1237855caec6SPeter Avalos if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
1238855caec6SPeter Avalos {
1239855caec6SPeter Avalos /* If one is a directory, and it was specified in the command line,
1240855caec6SPeter Avalos use the file in that dir with the other file's basename. */
1241855caec6SPeter Avalos
1242855caec6SPeter Avalos int fnm_arg = DIR_P (0);
1243855caec6SPeter Avalos int dir_arg = 1 - fnm_arg;
1244855caec6SPeter Avalos char const *fnm = cmp.file[fnm_arg].name;
1245855caec6SPeter Avalos char const *dir = cmp.file[dir_arg].name;
1246855caec6SPeter Avalos char const *filename = cmp.file[dir_arg].name = free0
1247855caec6SPeter Avalos = find_dir_file_pathname (dir, last_component (fnm));
1248008e37b6SJohn Marino
1249855caec6SPeter Avalos if (STREQ (fnm, "-"))
125044b87433SJohn Marino fatal ("cannot compare '-' to a directory");
12514536c563SJohn Marino
1252855caec6SPeter Avalos if ((no_dereference_symlinks
12534536c563SJohn Marino ? lstat (filename, &cmp.file[dir_arg].stat)
12544536c563SJohn Marino : stat (filename, &cmp.file[dir_arg].stat))
12554536c563SJohn Marino != 0)
12564536c563SJohn Marino {
1257855caec6SPeter Avalos perror_with_name (filename);
1258855caec6SPeter Avalos status = EXIT_TROUBLE;
1259855caec6SPeter Avalos }
1260855caec6SPeter Avalos }
1261855caec6SPeter Avalos
1262855caec6SPeter Avalos if (status != EXIT_SUCCESS)
1263855caec6SPeter Avalos {
1264855caec6SPeter Avalos /* One of the files should exist but does not. */
1265855caec6SPeter Avalos }
1266855caec6SPeter Avalos else if (cmp.file[0].desc == NONEXISTENT
1267855caec6SPeter Avalos && cmp.file[1].desc == NONEXISTENT)
1268855caec6SPeter Avalos {
1269855caec6SPeter Avalos /* Neither file "exists", so there's nothing to compare. */
1270855caec6SPeter Avalos }
1271855caec6SPeter Avalos else if ((same_files
1272855caec6SPeter Avalos = (cmp.file[0].desc != NONEXISTENT
1273855caec6SPeter Avalos && cmp.file[1].desc != NONEXISTENT
1274855caec6SPeter Avalos && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
1275855caec6SPeter Avalos && same_file_attributes (&cmp.file[0].stat,
1276855caec6SPeter Avalos &cmp.file[1].stat)))
1277855caec6SPeter Avalos && no_diff_means_no_output)
1278855caec6SPeter Avalos {
1279855caec6SPeter Avalos /* The two named files are actually the same physical file.
1280855caec6SPeter Avalos We know they are identical without actually reading them. */
1281855caec6SPeter Avalos }
1282855caec6SPeter Avalos else if (DIR_P (0) & DIR_P (1))
1283855caec6SPeter Avalos {
1284855caec6SPeter Avalos if (output_style == OUTPUT_IFDEF)
1285855caec6SPeter Avalos fatal ("-D option not supported with directories");
1286855caec6SPeter Avalos
1287855caec6SPeter Avalos /* If both are directories, compare the files in them. */
1288855caec6SPeter Avalos
1289855caec6SPeter Avalos if (parent && !recursive)
1290855caec6SPeter Avalos {
1291855caec6SPeter Avalos /* But don't compare dir contents one level down
1292855caec6SPeter Avalos unless -r was specified.
1293855caec6SPeter Avalos See POSIX 1003.1-2001 for this format. */
1294855caec6SPeter Avalos message ("Common subdirectories: %s and %s\n",
1295855caec6SPeter Avalos cmp.file[0].name, cmp.file[1].name);
1296855caec6SPeter Avalos }
1297855caec6SPeter Avalos else
1298855caec6SPeter Avalos status = diff_dirs (&cmp, compare_files);
1299855caec6SPeter Avalos }
1300855caec6SPeter Avalos else if ((DIR_P (0) | DIR_P (1))
1301855caec6SPeter Avalos || (parent
1302855caec6SPeter Avalos && !((S_ISREG (cmp.file[0].stat.st_mode)
13034536c563SJohn Marino || S_ISLNK (cmp.file[0].stat.st_mode))
13044536c563SJohn Marino && (S_ISREG (cmp.file[1].stat.st_mode)
13054536c563SJohn Marino || S_ISLNK (cmp.file[1].stat.st_mode)))))
13064536c563SJohn Marino {
1307855caec6SPeter Avalos if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
1308855caec6SPeter Avalos {
1309855caec6SPeter Avalos /* We have a subdirectory that exists only in one directory. */
1310855caec6SPeter Avalos
1311855caec6SPeter Avalos if ((DIR_P (0) | DIR_P (1))
1312855caec6SPeter Avalos && recursive
1313855caec6SPeter Avalos && (new_file
1314855caec6SPeter Avalos || (unidirectional_new_file
1315855caec6SPeter Avalos && cmp.file[0].desc == NONEXISTENT)))
1316855caec6SPeter Avalos status = diff_dirs (&cmp, compare_files);
1317855caec6SPeter Avalos else
1318855caec6SPeter Avalos {
1319855caec6SPeter Avalos char const *dir;
132044b87433SJohn Marino
132144b87433SJohn Marino /* PARENT must be non-NULL here. */
132244b87433SJohn Marino assert (parent);
132344b87433SJohn Marino dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
132444b87433SJohn Marino
1325855caec6SPeter Avalos /* See POSIX 1003.1-2001 for this format. */
1326855caec6SPeter Avalos message ("Only in %s: %s\n", dir, name0);
1327855caec6SPeter Avalos
1328855caec6SPeter Avalos status = EXIT_FAILURE;
1329855caec6SPeter Avalos }
1330855caec6SPeter Avalos }
1331855caec6SPeter Avalos else
1332855caec6SPeter Avalos {
1333855caec6SPeter Avalos /* We have two files that are not to be compared. */
1334855caec6SPeter Avalos
1335855caec6SPeter Avalos /* See POSIX 1003.1-2001 for this format. */
1336855caec6SPeter Avalos message5 ("File %s is a %s while file %s is a %s\n",
1337855caec6SPeter Avalos file_label[0] ? file_label[0] : cmp.file[0].name,
1338855caec6SPeter Avalos file_type (&cmp.file[0].stat),
1339855caec6SPeter Avalos file_label[1] ? file_label[1] : cmp.file[1].name,
1340855caec6SPeter Avalos file_type (&cmp.file[1].stat));
1341855caec6SPeter Avalos
1342855caec6SPeter Avalos /* This is a difference. */
1343855caec6SPeter Avalos status = EXIT_FAILURE;
1344855caec6SPeter Avalos }
1345855caec6SPeter Avalos }
1346855caec6SPeter Avalos else if (S_ISLNK (cmp.file[0].stat.st_mode)
13474536c563SJohn Marino || S_ISLNK (cmp.file[1].stat.st_mode))
13484536c563SJohn Marino {
13494536c563SJohn Marino /* We get here only if we use lstat(), not stat(). */
13504536c563SJohn Marino assert (no_dereference_symlinks);
13514536c563SJohn Marino
13524536c563SJohn Marino if (S_ISLNK (cmp.file[0].stat.st_mode)
13534536c563SJohn Marino && S_ISLNK (cmp.file[1].stat.st_mode))
13544536c563SJohn Marino {
13554536c563SJohn Marino /* Compare the values of the symbolic links. */
13564536c563SJohn Marino char *link_value[2] = { NULL, NULL };
13574536c563SJohn Marino
13584536c563SJohn Marino for (f = 0; f < 2; f++)
13594536c563SJohn Marino {
13604536c563SJohn Marino link_value[f] = xreadlink (cmp.file[f].name);
13614536c563SJohn Marino if (link_value[f] == NULL)
13624536c563SJohn Marino {
13634536c563SJohn Marino perror_with_name (cmp.file[f].name);
13644536c563SJohn Marino status = EXIT_TROUBLE;
13654536c563SJohn Marino break;
13664536c563SJohn Marino }
13674536c563SJohn Marino }
13684536c563SJohn Marino if (status == EXIT_SUCCESS)
13694536c563SJohn Marino {
13704536c563SJohn Marino if ( ! STREQ (link_value[0], link_value[1]))
13714536c563SJohn Marino {
13724536c563SJohn Marino message ("Symbolic links %s and %s differ\n",
13734536c563SJohn Marino cmp.file[0].name, cmp.file[1].name);
13744536c563SJohn Marino /* This is a difference. */
13754536c563SJohn Marino status = EXIT_FAILURE;
13764536c563SJohn Marino }
13774536c563SJohn Marino }
13784536c563SJohn Marino for (f = 0; f < 2; f++)
13794536c563SJohn Marino free (link_value[f]);
13804536c563SJohn Marino }
13814536c563SJohn Marino else
13824536c563SJohn Marino {
13834536c563SJohn Marino /* We have two files that are not to be compared, because
13844536c563SJohn Marino one of them is a symbolic link and the other one is not. */
13854536c563SJohn Marino
13864536c563SJohn Marino message5 ("File %s is a %s while file %s is a %s\n",
13874536c563SJohn Marino file_label[0] ? file_label[0] : cmp.file[0].name,
13884536c563SJohn Marino file_type (&cmp.file[0].stat),
13894536c563SJohn Marino file_label[1] ? file_label[1] : cmp.file[1].name,
13904536c563SJohn Marino file_type (&cmp.file[1].stat));
13914536c563SJohn Marino
13924536c563SJohn Marino /* This is a difference. */
13934536c563SJohn Marino status = EXIT_FAILURE;
13944536c563SJohn Marino }
13954536c563SJohn Marino }
13964536c563SJohn Marino else if (files_can_be_treated_as_binary
1397855caec6SPeter Avalos && S_ISREG (cmp.file[0].stat.st_mode)
1398855caec6SPeter Avalos && S_ISREG (cmp.file[1].stat.st_mode)
1399855caec6SPeter Avalos && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
1400*6ea1f93eSDaniel Fojt && 0 < cmp.file[0].stat.st_size
1401*6ea1f93eSDaniel Fojt && 0 < cmp.file[1].stat.st_size)
1402*6ea1f93eSDaniel Fojt {
1403855caec6SPeter Avalos message ("Files %s and %s differ\n",
1404855caec6SPeter Avalos file_label[0] ? file_label[0] : cmp.file[0].name,
1405855caec6SPeter Avalos file_label[1] ? file_label[1] : cmp.file[1].name);
1406855caec6SPeter Avalos status = EXIT_FAILURE;
1407855caec6SPeter Avalos }
1408855caec6SPeter Avalos else
1409855caec6SPeter Avalos {
1410855caec6SPeter Avalos /* Both exist and neither is a directory. */
1411855caec6SPeter Avalos
1412855caec6SPeter Avalos /* Open the files and record their descriptors. */
1413855caec6SPeter Avalos
1414855caec6SPeter Avalos int oflags = O_RDONLY | (binary ? O_BINARY : 0);
141544b87433SJohn Marino
141644b87433SJohn Marino if (cmp.file[0].desc == UNOPENED)
1417855caec6SPeter Avalos if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
141844b87433SJohn Marino {
1419855caec6SPeter Avalos perror_with_name (cmp.file[0].name);
1420855caec6SPeter Avalos status = EXIT_TROUBLE;
1421855caec6SPeter Avalos }
1422855caec6SPeter Avalos if (cmp.file[1].desc == UNOPENED)
1423855caec6SPeter Avalos {
1424855caec6SPeter Avalos if (same_files)
1425855caec6SPeter Avalos cmp.file[1].desc = cmp.file[0].desc;
1426855caec6SPeter Avalos else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
142744b87433SJohn Marino {
1428855caec6SPeter Avalos perror_with_name (cmp.file[1].name);
1429855caec6SPeter Avalos status = EXIT_TROUBLE;
1430855caec6SPeter Avalos }
1431855caec6SPeter Avalos }
1432855caec6SPeter Avalos
1433855caec6SPeter Avalos /* Compare the files, if no error was found. */
1434855caec6SPeter Avalos
1435855caec6SPeter Avalos if (status == EXIT_SUCCESS)
1436855caec6SPeter Avalos status = diff_2_files (&cmp);
1437855caec6SPeter Avalos
1438855caec6SPeter Avalos /* Close the file descriptors. */
1439855caec6SPeter Avalos
1440855caec6SPeter Avalos if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
1441855caec6SPeter Avalos {
1442855caec6SPeter Avalos perror_with_name (cmp.file[0].name);
1443855caec6SPeter Avalos status = EXIT_TROUBLE;
1444855caec6SPeter Avalos }
1445855caec6SPeter Avalos if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
1446855caec6SPeter Avalos && close (cmp.file[1].desc) != 0)
1447855caec6SPeter Avalos {
1448855caec6SPeter Avalos perror_with_name (cmp.file[1].name);
1449855caec6SPeter Avalos status = EXIT_TROUBLE;
1450855caec6SPeter Avalos }
1451855caec6SPeter Avalos }
1452855caec6SPeter Avalos
1453855caec6SPeter Avalos /* Now the comparison has been done, if no error prevented it,
1454855caec6SPeter Avalos and STATUS is the value this function will return. */
1455855caec6SPeter Avalos
1456855caec6SPeter Avalos if (status == EXIT_SUCCESS)
1457855caec6SPeter Avalos {
1458855caec6SPeter Avalos if (report_identical_files && !DIR_P (0))
1459855caec6SPeter Avalos message ("Files %s and %s are identical\n",
1460855caec6SPeter Avalos file_label[0] ? file_label[0] : cmp.file[0].name,
1461855caec6SPeter Avalos file_label[1] ? file_label[1] : cmp.file[1].name);
1462855caec6SPeter Avalos }
1463855caec6SPeter Avalos else
1464855caec6SPeter Avalos {
1465855caec6SPeter Avalos /* Flush stdout so that the user sees differences immediately.
1466855caec6SPeter Avalos This can hurt performance, unfortunately. */
1467855caec6SPeter Avalos if (fflush (stdout) != 0)
1468855caec6SPeter Avalos pfatal_with_name (_("standard output"));
1469855caec6SPeter Avalos }
1470855caec6SPeter Avalos
1471855caec6SPeter Avalos free (free0);
1472855caec6SPeter Avalos free (free1);
1473855caec6SPeter Avalos
1474855caec6SPeter Avalos return status;
1475855caec6SPeter Avalos }
1476855caec6SPeter Avalos