xref: /dragonfly/contrib/diffutils/src/diff.c (revision 6ea1f93e)
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