xref: /freebsd/contrib/diff/src/diff.c (revision 6f5e3243)
118fd37a7SXin LI /* diff - compare files line by line
218fd37a7SXin LI 
318fd37a7SXin LI    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002,
418fd37a7SXin LI    2004 Free Software Foundation, Inc.
518fd37a7SXin LI 
618fd37a7SXin LI    This file is part of GNU DIFF.
718fd37a7SXin LI 
818fd37a7SXin LI    GNU DIFF is free software; you can redistribute it and/or modify
918fd37a7SXin LI    it under the terms of the GNU General Public License as published by
1018fd37a7SXin LI    the Free Software Foundation; either version 2, or (at your option)
1118fd37a7SXin LI    any later version.
1218fd37a7SXin LI 
1318fd37a7SXin LI    GNU DIFF is distributed in the hope that it will be useful,
1418fd37a7SXin LI    but WITHOUT ANY WARRANTY; without even the implied warranty of
1518fd37a7SXin LI    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1618fd37a7SXin LI    See the GNU General Public License for more details.
1718fd37a7SXin LI 
1818fd37a7SXin LI    You should have received a copy of the GNU General Public License
1918fd37a7SXin LI    along with GNU DIFF; see the file COPYING.
2018fd37a7SXin LI    If not, write to the Free Software Foundation,
2118fd37a7SXin LI    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2218fd37a7SXin LI 
2318fd37a7SXin LI #define GDIFF_MAIN
2418fd37a7SXin LI #include "diff.h"
2518fd37a7SXin LI #include "paths.h"
2618fd37a7SXin LI #include <c-stack.h>
2718fd37a7SXin LI #include <dirname.h>
2818fd37a7SXin LI #include <error.h>
2918fd37a7SXin LI #include <exclude.h>
3018fd37a7SXin LI #include <exit.h>
3118fd37a7SXin LI #include <exitfail.h>
3218fd37a7SXin LI #include <file-type.h>
3318fd37a7SXin LI #include <fnmatch.h>
3418fd37a7SXin LI #include <getopt.h>
3518fd37a7SXin LI #include <hard-locale.h>
3618fd37a7SXin LI #include <posixver.h>
3718fd37a7SXin LI #include <prepargs.h>
3818fd37a7SXin LI #include <quotesys.h>
3918fd37a7SXin LI #include <setmode.h>
4018fd37a7SXin LI #include <version-etc.h>
4118fd37a7SXin LI #include <xalloc.h>
4218fd37a7SXin LI 
4318fd37a7SXin LI #ifndef GUTTER_WIDTH_MINIMUM
4418fd37a7SXin LI # define GUTTER_WIDTH_MINIMUM 3
4518fd37a7SXin LI #endif
4618fd37a7SXin LI 
4718fd37a7SXin LI struct regexp_list
4818fd37a7SXin LI {
4918fd37a7SXin LI   char *regexps;	/* chars representing disjunction of the regexps */
5018fd37a7SXin LI   size_t len;		/* chars used in `regexps' */
5118fd37a7SXin LI   size_t size;		/* size malloc'ed for `regexps'; 0 if not malloc'ed */
5218fd37a7SXin LI   bool multiple_regexps;/* Does `regexps' represent a disjunction?  */
5318fd37a7SXin LI   struct re_pattern_buffer *buf;
5418fd37a7SXin LI };
5518fd37a7SXin LI 
5618fd37a7SXin LI static int compare_files (struct comparison const *, char const *, char const *);
5718fd37a7SXin LI static void add_regexp (struct regexp_list *, char const *);
5818fd37a7SXin LI static void summarize_regexp_list (struct regexp_list *);
5918fd37a7SXin LI static void specify_style (enum output_style);
6018fd37a7SXin LI static void specify_value (char const **, char const *, char const *);
6118fd37a7SXin LI static void try_help (char const *, char const *) __attribute__((noreturn));
6218fd37a7SXin LI static void check_stdout (void);
6318fd37a7SXin LI static void usage (void);
6418fd37a7SXin LI 
6518fd37a7SXin LI /* If comparing directories, compare their common subdirectories
6618fd37a7SXin LI    recursively.  */
6718fd37a7SXin LI static bool recursive;
6818fd37a7SXin LI 
6918fd37a7SXin LI /* In context diffs, show previous lines that match these regexps.  */
7018fd37a7SXin LI static struct regexp_list function_regexp_list;
7118fd37a7SXin LI 
7218fd37a7SXin LI /* Ignore changes affecting only lines that match these regexps.  */
7318fd37a7SXin LI static struct regexp_list ignore_regexp_list;
7418fd37a7SXin LI 
7518fd37a7SXin LI #if HAVE_SETMODE_DOS
7618fd37a7SXin LI /* Use binary I/O when reading and writing data (--binary).
7718fd37a7SXin LI    On POSIX hosts, this has no effect.  */
7818fd37a7SXin LI static bool binary;
7918fd37a7SXin LI #else
8018fd37a7SXin LI enum { binary = true };
8118fd37a7SXin LI #endif
8218fd37a7SXin LI 
8318fd37a7SXin LI /* When comparing directories, if a file appears only in one
8418fd37a7SXin LI    directory, treat it as present but empty in the other (-N).
8518fd37a7SXin LI    Then `patch' would create the file with appropriate contents.  */
8618fd37a7SXin LI static bool new_file;
8718fd37a7SXin LI 
8818fd37a7SXin LI /* When comparing directories, if a file appears only in the second
8918fd37a7SXin LI    directory of the two, treat it as present but empty in the other
9018fd37a7SXin LI    (--unidirectional-new-file).
9118fd37a7SXin LI    Then `patch' would create the file with appropriate contents.  */
9218fd37a7SXin LI static bool unidirectional_new_file;
9318fd37a7SXin LI 
9418fd37a7SXin LI /* Report files compared that are the same (-s).
9518fd37a7SXin LI    Normally nothing is output when that happens.  */
9618fd37a7SXin LI static bool report_identical_files;
9718fd37a7SXin LI 
9818fd37a7SXin LI 
9918fd37a7SXin LI /* Return a string containing the command options with which diff was invoked.
10018fd37a7SXin LI    Spaces appear between what were separate ARGV-elements.
10118fd37a7SXin LI    There is a space at the beginning but none at the end.
10218fd37a7SXin LI    If there were no options, the result is an empty string.
10318fd37a7SXin LI 
10418fd37a7SXin LI    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
10518fd37a7SXin LI    the length of that vector.  */
10618fd37a7SXin LI 
10718fd37a7SXin LI static char *
option_list(char ** optionvec,int count)10818fd37a7SXin LI option_list (char **optionvec, int count)
10918fd37a7SXin LI {
11018fd37a7SXin LI   int i;
11118fd37a7SXin LI   size_t size = 1;
11218fd37a7SXin LI   char *result;
11318fd37a7SXin LI   char *p;
11418fd37a7SXin LI 
11518fd37a7SXin LI   for (i = 0; i < count; i++)
11618fd37a7SXin LI     size += 1 + quote_system_arg ((char *) 0, optionvec[i]);
11718fd37a7SXin LI 
11818fd37a7SXin LI   p = result = xmalloc (size);
11918fd37a7SXin LI 
12018fd37a7SXin LI   for (i = 0; i < count; i++)
12118fd37a7SXin LI     {
12218fd37a7SXin LI       *p++ = ' ';
12318fd37a7SXin LI       p += quote_system_arg (p, optionvec[i]);
12418fd37a7SXin LI     }
12518fd37a7SXin LI 
12618fd37a7SXin LI   *p = 0;
12718fd37a7SXin LI   return result;
12818fd37a7SXin LI }
12918fd37a7SXin LI 
13018fd37a7SXin LI 
13118fd37a7SXin LI /* Return an option value suitable for add_exclude.  */
13218fd37a7SXin LI 
13318fd37a7SXin LI static int
exclude_options(void)13418fd37a7SXin LI exclude_options (void)
13518fd37a7SXin LI {
13618fd37a7SXin LI   return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
13718fd37a7SXin LI }
13818fd37a7SXin LI 
13918fd37a7SXin LI static char const shortopts[] =
1406f5e3243SDavid E. O'Brien "0123456789abBcC:dD:eEfF:hHiI:lL:nNopPqrsS:tTuU:vwW:x:X:y";
14118fd37a7SXin LI 
14218fd37a7SXin LI /* Values for long options that do not have single-letter equivalents.  */
14318fd37a7SXin LI enum
14418fd37a7SXin LI {
14518fd37a7SXin LI   BINARY_OPTION = CHAR_MAX + 1,
14618fd37a7SXin LI   FROM_FILE_OPTION,
14718fd37a7SXin LI   HELP_OPTION,
14818fd37a7SXin LI   HORIZON_LINES_OPTION,
14918fd37a7SXin LI   IGNORE_FILE_NAME_CASE_OPTION,
15018fd37a7SXin LI   INHIBIT_HUNK_MERGE_OPTION,
15118fd37a7SXin LI   LEFT_COLUMN_OPTION,
15218fd37a7SXin LI   LINE_FORMAT_OPTION,
15318fd37a7SXin LI   NO_IGNORE_FILE_NAME_CASE_OPTION,
15418fd37a7SXin LI   NORMAL_OPTION,
15518fd37a7SXin LI   SDIFF_MERGE_ASSIST_OPTION,
15618fd37a7SXin LI   STRIP_TRAILING_CR_OPTION,
15718fd37a7SXin LI   SUPPRESS_COMMON_LINES_OPTION,
15818fd37a7SXin LI   TABSIZE_OPTION,
15918fd37a7SXin LI   TO_FILE_OPTION,
16018fd37a7SXin LI 
16118fd37a7SXin LI   /* These options must be in sequence.  */
16218fd37a7SXin LI   UNCHANGED_LINE_FORMAT_OPTION,
16318fd37a7SXin LI   OLD_LINE_FORMAT_OPTION,
16418fd37a7SXin LI   NEW_LINE_FORMAT_OPTION,
16518fd37a7SXin LI 
16618fd37a7SXin LI   /* These options must be in sequence.  */
16718fd37a7SXin LI   UNCHANGED_GROUP_FORMAT_OPTION,
16818fd37a7SXin LI   OLD_GROUP_FORMAT_OPTION,
16918fd37a7SXin LI   NEW_GROUP_FORMAT_OPTION,
17018fd37a7SXin LI   CHANGED_GROUP_FORMAT_OPTION
17118fd37a7SXin LI };
17218fd37a7SXin LI 
17318fd37a7SXin LI static char const group_format_option[][sizeof "--unchanged-group-format"] =
17418fd37a7SXin LI   {
17518fd37a7SXin LI     "--unchanged-group-format",
17618fd37a7SXin LI     "--old-group-format",
17718fd37a7SXin LI     "--new-group-format",
17818fd37a7SXin LI     "--changed-group-format"
17918fd37a7SXin LI   };
18018fd37a7SXin LI 
18118fd37a7SXin LI static char const line_format_option[][sizeof "--unchanged-line-format"] =
18218fd37a7SXin LI   {
18318fd37a7SXin LI     "--unchanged-line-format",
18418fd37a7SXin LI     "--old-line-format",
18518fd37a7SXin LI     "--new-line-format"
18618fd37a7SXin LI   };
18718fd37a7SXin LI 
18818fd37a7SXin LI static struct option const longopts[] =
18918fd37a7SXin LI {
19018fd37a7SXin LI   {"binary", 0, 0, BINARY_OPTION},
19118fd37a7SXin LI   {"brief", 0, 0, 'q'},
19218fd37a7SXin LI   {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
19318fd37a7SXin LI   {"context", 2, 0, 'C'},
19418fd37a7SXin LI   {"ed", 0, 0, 'e'},
19518fd37a7SXin LI   {"exclude", 1, 0, 'x'},
19618fd37a7SXin LI   {"exclude-from", 1, 0, 'X'},
19718fd37a7SXin LI   {"expand-tabs", 0, 0, 't'},
19818fd37a7SXin LI   {"forward-ed", 0, 0, 'f'},
19918fd37a7SXin LI   {"from-file", 1, 0, FROM_FILE_OPTION},
20018fd37a7SXin LI   {"help", 0, 0, HELP_OPTION},
20118fd37a7SXin LI   {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
20218fd37a7SXin LI   {"ifdef", 1, 0, 'D'},
20318fd37a7SXin LI   {"ignore-all-space", 0, 0, 'w'},
20418fd37a7SXin LI   {"ignore-blank-lines", 0, 0, 'B'},
20518fd37a7SXin LI   {"ignore-case", 0, 0, 'i'},
20618fd37a7SXin LI   {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
20718fd37a7SXin LI   {"ignore-matching-lines", 1, 0, 'I'},
20818fd37a7SXin LI   {"ignore-space-change", 0, 0, 'b'},
20918fd37a7SXin LI   {"ignore-tab-expansion", 0, 0, 'E'},
21018fd37a7SXin LI   {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
21118fd37a7SXin LI   {"initial-tab", 0, 0, 'T'},
21218fd37a7SXin LI   {"label", 1, 0, 'L'},
21318fd37a7SXin LI   {"left-column", 0, 0, LEFT_COLUMN_OPTION},
21418fd37a7SXin LI   {"line-format", 1, 0, LINE_FORMAT_OPTION},
21518fd37a7SXin LI   {"minimal", 0, 0, 'd'},
21618fd37a7SXin LI   {"new-file", 0, 0, 'N'},
21718fd37a7SXin LI   {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
21818fd37a7SXin LI   {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
21918fd37a7SXin LI   {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
22018fd37a7SXin LI   {"normal", 0, 0, NORMAL_OPTION},
22118fd37a7SXin LI   {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
22218fd37a7SXin LI   {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
22318fd37a7SXin LI   {"paginate", 0, 0, 'l'},
22418fd37a7SXin LI   {"rcs", 0, 0, 'n'},
22518fd37a7SXin LI   {"recursive", 0, 0, 'r'},
22618fd37a7SXin LI   {"report-identical-files", 0, 0, 's'},
22718fd37a7SXin LI   {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
22818fd37a7SXin LI   {"show-c-function", 0, 0, 'p'},
22918fd37a7SXin LI   {"show-function-line", 1, 0, 'F'},
23018fd37a7SXin LI   {"side-by-side", 0, 0, 'y'},
23118fd37a7SXin LI   {"speed-large-files", 0, 0, 'H'},
23218fd37a7SXin LI   {"starting-file", 1, 0, 'S'},
23318fd37a7SXin LI   {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
23418fd37a7SXin LI   {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
23518fd37a7SXin LI   {"tabsize", 1, 0, TABSIZE_OPTION},
23618fd37a7SXin LI   {"text", 0, 0, 'a'},
23718fd37a7SXin LI   {"to-file", 1, 0, TO_FILE_OPTION},
23818fd37a7SXin LI   {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
23918fd37a7SXin LI   {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
24018fd37a7SXin LI   {"unidirectional-new-file", 0, 0, 'P'},
24118fd37a7SXin LI   {"unified", 2, 0, 'U'},
24218fd37a7SXin LI   {"version", 0, 0, 'v'},
24318fd37a7SXin LI   {"width", 1, 0, 'W'},
24418fd37a7SXin LI   {0, 0, 0, 0}
24518fd37a7SXin LI };
24618fd37a7SXin LI 
24718fd37a7SXin LI int
main(int argc,char ** argv)24818fd37a7SXin LI main (int argc, char **argv)
24918fd37a7SXin LI {
25018fd37a7SXin LI   int exit_status = EXIT_SUCCESS;
25118fd37a7SXin LI   int c;
25218fd37a7SXin LI   int i;
25318fd37a7SXin LI   int prev = -1;
25418fd37a7SXin LI   lin ocontext = -1;
25518fd37a7SXin LI   bool explicit_context = false;
25618fd37a7SXin LI   size_t width = 0;
25718fd37a7SXin LI   bool show_c_function = false;
25818fd37a7SXin LI   char const *from_file = 0;
25918fd37a7SXin LI   char const *to_file = 0;
26018fd37a7SXin LI   uintmax_t numval;
26118fd37a7SXin LI   char *numend;
26218fd37a7SXin LI 
26318fd37a7SXin LI   /* Do our initializations.  */
26418fd37a7SXin LI   exit_failure = 2;
26518fd37a7SXin LI   initialize_main (&argc, &argv);
26618fd37a7SXin LI   program_name = argv[0];
26718fd37a7SXin LI   setlocale (LC_ALL, "");
26818fd37a7SXin LI   textdomain (PACKAGE);
26918fd37a7SXin LI   c_stack_action (0);
27018fd37a7SXin LI   function_regexp_list.buf = &function_regexp;
27118fd37a7SXin LI   ignore_regexp_list.buf = &ignore_regexp;
2726f5e3243SDavid E. O'Brien   re_set_syntax (RE_SYNTAX_GREP);
27318fd37a7SXin LI   excluded = new_exclude ();
27418fd37a7SXin LI 
2756f5e3243SDavid E. O'Brien   prepend_default_options (getenv ("DIFF_OPTIONS"), &argc, &argv);
2766f5e3243SDavid E. O'Brien 
27718fd37a7SXin LI   /* Decode the options.  */
27818fd37a7SXin LI 
27918fd37a7SXin LI   while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1)
28018fd37a7SXin LI     {
28118fd37a7SXin LI       switch (c)
28218fd37a7SXin LI 	{
28318fd37a7SXin LI 	case 0:
28418fd37a7SXin LI 	  break;
28518fd37a7SXin LI 
28618fd37a7SXin LI 	case '0':
28718fd37a7SXin LI 	case '1':
28818fd37a7SXin LI 	case '2':
28918fd37a7SXin LI 	case '3':
29018fd37a7SXin LI 	case '4':
29118fd37a7SXin LI 	case '5':
29218fd37a7SXin LI 	case '6':
29318fd37a7SXin LI 	case '7':
29418fd37a7SXin LI 	case '8':
29518fd37a7SXin LI 	case '9':
29618fd37a7SXin LI 	  if (! ISDIGIT (prev))
29718fd37a7SXin LI 	    ocontext = c - '0';
29818fd37a7SXin LI 	  else if (LIN_MAX / 10 < ocontext
29918fd37a7SXin LI 		   || ((ocontext = 10 * ocontext + c - '0') < 0))
30018fd37a7SXin LI 	    ocontext = LIN_MAX;
30118fd37a7SXin LI 	  break;
30218fd37a7SXin LI 
30318fd37a7SXin LI 	case 'a':
30418fd37a7SXin LI 	  text = true;
30518fd37a7SXin LI 	  break;
30618fd37a7SXin LI 
30718fd37a7SXin LI 	case 'b':
30818fd37a7SXin LI 	  if (ignore_white_space < IGNORE_SPACE_CHANGE)
30918fd37a7SXin LI 	    ignore_white_space = IGNORE_SPACE_CHANGE;
31018fd37a7SXin LI 	  break;
31118fd37a7SXin LI 
31218fd37a7SXin LI 	case 'B':
31318fd37a7SXin LI 	  ignore_blank_lines = true;
31418fd37a7SXin LI 	  break;
31518fd37a7SXin LI 
31618fd37a7SXin LI 	case 'C':
31718fd37a7SXin LI 	case 'U':
31818fd37a7SXin LI 	  {
31918fd37a7SXin LI 	    if (optarg)
32018fd37a7SXin LI 	      {
32118fd37a7SXin LI 		numval = strtoumax (optarg, &numend, 10);
32218fd37a7SXin LI 		if (*numend)
32318fd37a7SXin LI 		  try_help ("invalid context length `%s'", optarg);
32418fd37a7SXin LI 		if (LIN_MAX < numval)
32518fd37a7SXin LI 		  numval = LIN_MAX;
32618fd37a7SXin LI 	      }
32718fd37a7SXin LI 	    else
32818fd37a7SXin LI 	      numval = 3;
32918fd37a7SXin LI 
33018fd37a7SXin LI 	    specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
33118fd37a7SXin LI 	    if (context < numval)
33218fd37a7SXin LI 	      context = numval;
33318fd37a7SXin LI 	    explicit_context = true;
33418fd37a7SXin LI 	  }
33518fd37a7SXin LI 	  break;
33618fd37a7SXin LI 
33718fd37a7SXin LI 	case 'c':
33818fd37a7SXin LI 	  specify_style (OUTPUT_CONTEXT);
33918fd37a7SXin LI 	  if (context < 3)
34018fd37a7SXin LI 	    context = 3;
34118fd37a7SXin LI 	  break;
34218fd37a7SXin LI 
34318fd37a7SXin LI 	case 'd':
34418fd37a7SXin LI 	  minimal = true;
34518fd37a7SXin LI 	  break;
34618fd37a7SXin LI 
34718fd37a7SXin LI 	case 'D':
34818fd37a7SXin LI 	  specify_style (OUTPUT_IFDEF);
34918fd37a7SXin LI 	  {
35018fd37a7SXin LI 	    static char const C_ifdef_group_formats[] =
35118fd37a7SXin LI 	      "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
35218fd37a7SXin LI 	    char *b = xmalloc (sizeof C_ifdef_group_formats
35318fd37a7SXin LI 			       + 7 * strlen (optarg) - 14 /* 7*"%s" */
35418fd37a7SXin LI 			       - 8 /* 5*"%%" + 3*"%c" */);
35518fd37a7SXin LI 	    sprintf (b, C_ifdef_group_formats,
35618fd37a7SXin LI 		     0,
35718fd37a7SXin LI 		     optarg, optarg, 0,
35818fd37a7SXin LI 		     optarg, optarg, 0,
35918fd37a7SXin LI 		     optarg, optarg, optarg);
36018fd37a7SXin LI 	    for (i = 0; i < sizeof group_format / sizeof *group_format; i++)
36118fd37a7SXin LI 	      {
36218fd37a7SXin LI 		specify_value (&group_format[i], b, "-D");
36318fd37a7SXin LI 		b += strlen (b) + 1;
36418fd37a7SXin LI 	      }
36518fd37a7SXin LI 	  }
36618fd37a7SXin LI 	  break;
36718fd37a7SXin LI 
36818fd37a7SXin LI 	case 'e':
36918fd37a7SXin LI 	  specify_style (OUTPUT_ED);
37018fd37a7SXin LI 	  break;
37118fd37a7SXin LI 
37218fd37a7SXin LI 	case 'E':
37318fd37a7SXin LI 	  if (ignore_white_space < IGNORE_TAB_EXPANSION)
37418fd37a7SXin LI 	    ignore_white_space = IGNORE_TAB_EXPANSION;
37518fd37a7SXin LI 	  break;
37618fd37a7SXin LI 
37718fd37a7SXin LI 	case 'f':
37818fd37a7SXin LI 	  specify_style (OUTPUT_FORWARD_ED);
37918fd37a7SXin LI 	  break;
38018fd37a7SXin LI 
38118fd37a7SXin LI 	case 'F':
38218fd37a7SXin LI 	  add_regexp (&function_regexp_list, optarg);
38318fd37a7SXin LI 	  break;
38418fd37a7SXin LI 
38518fd37a7SXin LI 	case 'h':
38618fd37a7SXin LI 	  /* Split the files into chunks for faster processing.
38718fd37a7SXin LI 	     Usually does not change the result.
38818fd37a7SXin LI 
38918fd37a7SXin LI 	     This currently has no effect.  */
39018fd37a7SXin LI 	  break;
39118fd37a7SXin LI 
39218fd37a7SXin LI 	case 'H':
39318fd37a7SXin LI 	  speed_large_files = true;
39418fd37a7SXin LI 	  break;
39518fd37a7SXin LI 
39618fd37a7SXin LI 	case 'i':
39718fd37a7SXin LI 	  ignore_case = true;
39818fd37a7SXin LI 	  break;
39918fd37a7SXin LI 
40018fd37a7SXin LI 	case 'I':
40118fd37a7SXin LI 	  add_regexp (&ignore_regexp_list, optarg);
40218fd37a7SXin LI 	  break;
40318fd37a7SXin LI 
40418fd37a7SXin LI 	case 'l':
40518fd37a7SXin LI 	  if (!pr_program[0])
40618fd37a7SXin LI 	    try_help ("pagination not supported on this host", 0);
40718fd37a7SXin LI 	  paginate = true;
40818fd37a7SXin LI #ifdef SIGCHLD
40918fd37a7SXin LI 	  /* Pagination requires forking and waiting, and
41018fd37a7SXin LI 	     System V fork+wait does not work if SIGCHLD is ignored.  */
41118fd37a7SXin LI 	  signal (SIGCHLD, SIG_DFL);
41218fd37a7SXin LI #endif
41318fd37a7SXin LI 	  break;
41418fd37a7SXin LI 
41518fd37a7SXin LI 	case 'L':
41618fd37a7SXin LI 	  if (!file_label[0])
41718fd37a7SXin LI 	    file_label[0] = optarg;
41818fd37a7SXin LI 	  else if (!file_label[1])
41918fd37a7SXin LI 	    file_label[1] = optarg;
42018fd37a7SXin LI 	  else
42118fd37a7SXin LI 	    fatal ("too many file label options");
42218fd37a7SXin LI 	  break;
42318fd37a7SXin LI 
42418fd37a7SXin LI 	case 'n':
42518fd37a7SXin LI 	  specify_style (OUTPUT_RCS);
42618fd37a7SXin LI 	  break;
42718fd37a7SXin LI 
42818fd37a7SXin LI 	case 'N':
42918fd37a7SXin LI 	  new_file = true;
43018fd37a7SXin LI 	  break;
43118fd37a7SXin LI 
4326f5e3243SDavid E. O'Brien 	case 'o':
4336f5e3243SDavid E. O'Brien 	  /* Output in the old tradition style.  */
4346f5e3243SDavid E. O'Brien 	  specify_style (OUTPUT_NORMAL);
4356f5e3243SDavid E. O'Brien 	  break;
4366f5e3243SDavid E. O'Brien 
43718fd37a7SXin LI 	case 'p':
43818fd37a7SXin LI 	  show_c_function = true;
43918fd37a7SXin LI 	  add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
44018fd37a7SXin LI 	  break;
44118fd37a7SXin LI 
44218fd37a7SXin LI 	case 'P':
44318fd37a7SXin LI 	  unidirectional_new_file = true;
44418fd37a7SXin LI 	  break;
44518fd37a7SXin LI 
44618fd37a7SXin LI 	case 'q':
44718fd37a7SXin LI 	  brief = true;
44818fd37a7SXin LI 	  break;
44918fd37a7SXin LI 
45018fd37a7SXin LI 	case 'r':
45118fd37a7SXin LI 	  recursive = true;
45218fd37a7SXin LI 	  break;
45318fd37a7SXin LI 
45418fd37a7SXin LI 	case 's':
45518fd37a7SXin LI 	  report_identical_files = true;
45618fd37a7SXin LI 	  break;
45718fd37a7SXin LI 
45818fd37a7SXin LI 	case 'S':
45918fd37a7SXin LI 	  specify_value (&starting_file, optarg, "-S");
46018fd37a7SXin LI 	  break;
46118fd37a7SXin LI 
46218fd37a7SXin LI 	case 't':
46318fd37a7SXin LI 	  expand_tabs = true;
46418fd37a7SXin LI 	  break;
46518fd37a7SXin LI 
46618fd37a7SXin LI 	case 'T':
46718fd37a7SXin LI 	  initial_tab = true;
46818fd37a7SXin LI 	  break;
46918fd37a7SXin LI 
47018fd37a7SXin LI 	case 'u':
47118fd37a7SXin LI 	  specify_style (OUTPUT_UNIFIED);
47218fd37a7SXin LI 	  if (context < 3)
47318fd37a7SXin LI 	    context = 3;
47418fd37a7SXin LI 	  break;
47518fd37a7SXin LI 
47618fd37a7SXin LI 	case 'v':
47718fd37a7SXin LI 	  version_etc (stdout, "diff", PACKAGE_NAME, PACKAGE_VERSION,
47818fd37a7SXin LI 		       "Paul Eggert", "Mike Haertel", "David Hayes",
47918fd37a7SXin LI 		       "Richard Stallman", "Len Tower", (char *) 0);
48018fd37a7SXin LI 	  check_stdout ();
48118fd37a7SXin LI 	  return EXIT_SUCCESS;
48218fd37a7SXin LI 
48318fd37a7SXin LI 	case 'w':
48418fd37a7SXin LI 	  ignore_white_space = IGNORE_ALL_SPACE;
48518fd37a7SXin LI 	  break;
48618fd37a7SXin LI 
48718fd37a7SXin LI 	case 'x':
48818fd37a7SXin LI 	  add_exclude (excluded, optarg, exclude_options ());
48918fd37a7SXin LI 	  break;
49018fd37a7SXin LI 
49118fd37a7SXin LI 	case 'X':
49218fd37a7SXin LI 	  if (add_exclude_file (add_exclude, excluded, optarg,
49318fd37a7SXin LI 				exclude_options (), '\n'))
49418fd37a7SXin LI 	    pfatal_with_name (optarg);
49518fd37a7SXin LI 	  break;
49618fd37a7SXin LI 
49718fd37a7SXin LI 	case 'y':
49818fd37a7SXin LI 	  specify_style (OUTPUT_SDIFF);
49918fd37a7SXin LI 	  break;
50018fd37a7SXin LI 
50118fd37a7SXin LI 	case 'W':
50218fd37a7SXin LI 	  numval = strtoumax (optarg, &numend, 10);
50318fd37a7SXin LI 	  if (! (0 < numval && numval <= SIZE_MAX) || *numend)
50418fd37a7SXin LI 	    try_help ("invalid width `%s'", optarg);
50518fd37a7SXin LI 	  if (width != numval)
50618fd37a7SXin LI 	    {
50718fd37a7SXin LI 	      if (width)
50818fd37a7SXin LI 		fatal ("conflicting width options");
50918fd37a7SXin LI 	      width = numval;
51018fd37a7SXin LI 	    }
51118fd37a7SXin LI 	  break;
51218fd37a7SXin LI 
51318fd37a7SXin LI 	case BINARY_OPTION:
51418fd37a7SXin LI #if HAVE_SETMODE_DOS
51518fd37a7SXin LI 	  binary = true;
51618fd37a7SXin LI 	  set_binary_mode (STDOUT_FILENO, true);
51718fd37a7SXin LI #endif
51818fd37a7SXin LI 	  break;
51918fd37a7SXin LI 
52018fd37a7SXin LI 	case FROM_FILE_OPTION:
52118fd37a7SXin LI 	  specify_value (&from_file, optarg, "--from-file");
52218fd37a7SXin LI 	  break;
52318fd37a7SXin LI 
52418fd37a7SXin LI 	case HELP_OPTION:
52518fd37a7SXin LI 	  usage ();
52618fd37a7SXin LI 	  check_stdout ();
52718fd37a7SXin LI 	  return EXIT_SUCCESS;
52818fd37a7SXin LI 
52918fd37a7SXin LI 	case HORIZON_LINES_OPTION:
53018fd37a7SXin LI 	  numval = strtoumax (optarg, &numend, 10);
53118fd37a7SXin LI 	  if (*numend)
53218fd37a7SXin LI 	    try_help ("invalid horizon length `%s'", optarg);
53318fd37a7SXin LI 	  horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
53418fd37a7SXin LI 	  break;
53518fd37a7SXin LI 
53618fd37a7SXin LI 	case IGNORE_FILE_NAME_CASE_OPTION:
53718fd37a7SXin LI 	  ignore_file_name_case = true;
53818fd37a7SXin LI 	  break;
53918fd37a7SXin LI 
54018fd37a7SXin LI 	case INHIBIT_HUNK_MERGE_OPTION:
54118fd37a7SXin LI 	  /* This option is obsolete, but accept it for backward
54218fd37a7SXin LI              compatibility.  */
54318fd37a7SXin LI 	  break;
54418fd37a7SXin LI 
54518fd37a7SXin LI 	case LEFT_COLUMN_OPTION:
54618fd37a7SXin LI 	  left_column = true;
54718fd37a7SXin LI 	  break;
54818fd37a7SXin LI 
54918fd37a7SXin LI 	case LINE_FORMAT_OPTION:
55018fd37a7SXin LI 	  specify_style (OUTPUT_IFDEF);
55118fd37a7SXin LI 	  for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
55218fd37a7SXin LI 	    specify_value (&line_format[i], optarg, "--line-format");
55318fd37a7SXin LI 	  break;
55418fd37a7SXin LI 
55518fd37a7SXin LI 	case NO_IGNORE_FILE_NAME_CASE_OPTION:
55618fd37a7SXin LI 	  ignore_file_name_case = false;
55718fd37a7SXin LI 	  break;
55818fd37a7SXin LI 
55918fd37a7SXin LI 	case NORMAL_OPTION:
56018fd37a7SXin LI 	  specify_style (OUTPUT_NORMAL);
56118fd37a7SXin LI 	  break;
56218fd37a7SXin LI 
56318fd37a7SXin LI 	case SDIFF_MERGE_ASSIST_OPTION:
56418fd37a7SXin LI 	  specify_style (OUTPUT_SDIFF);
56518fd37a7SXin LI 	  sdiff_merge_assist = true;
56618fd37a7SXin LI 	  break;
56718fd37a7SXin LI 
56818fd37a7SXin LI 	case STRIP_TRAILING_CR_OPTION:
56918fd37a7SXin LI 	  strip_trailing_cr = true;
57018fd37a7SXin LI 	  break;
57118fd37a7SXin LI 
57218fd37a7SXin LI 	case SUPPRESS_COMMON_LINES_OPTION:
57318fd37a7SXin LI 	  suppress_common_lines = true;
57418fd37a7SXin LI 	  break;
57518fd37a7SXin LI 
57618fd37a7SXin LI 	case TABSIZE_OPTION:
57718fd37a7SXin LI 	  numval = strtoumax (optarg, &numend, 10);
57818fd37a7SXin LI 	  if (! (0 < numval && numval <= SIZE_MAX) || *numend)
57918fd37a7SXin LI 	    try_help ("invalid tabsize `%s'", optarg);
58018fd37a7SXin LI 	  if (tabsize != numval)
58118fd37a7SXin LI 	    {
58218fd37a7SXin LI 	      if (tabsize)
58318fd37a7SXin LI 		fatal ("conflicting tabsize options");
58418fd37a7SXin LI 	      tabsize = numval;
58518fd37a7SXin LI 	    }
58618fd37a7SXin LI 	  break;
58718fd37a7SXin LI 
58818fd37a7SXin LI 	case TO_FILE_OPTION:
58918fd37a7SXin LI 	  specify_value (&to_file, optarg, "--to-file");
59018fd37a7SXin LI 	  break;
59118fd37a7SXin LI 
59218fd37a7SXin LI 	case UNCHANGED_LINE_FORMAT_OPTION:
59318fd37a7SXin LI 	case OLD_LINE_FORMAT_OPTION:
59418fd37a7SXin LI 	case NEW_LINE_FORMAT_OPTION:
59518fd37a7SXin LI 	  specify_style (OUTPUT_IFDEF);
59618fd37a7SXin LI 	  c -= UNCHANGED_LINE_FORMAT_OPTION;
59718fd37a7SXin LI 	  specify_value (&line_format[c], optarg, line_format_option[c]);
59818fd37a7SXin LI 	  break;
59918fd37a7SXin LI 
60018fd37a7SXin LI 	case UNCHANGED_GROUP_FORMAT_OPTION:
60118fd37a7SXin LI 	case OLD_GROUP_FORMAT_OPTION:
60218fd37a7SXin LI 	case NEW_GROUP_FORMAT_OPTION:
60318fd37a7SXin LI 	case CHANGED_GROUP_FORMAT_OPTION:
60418fd37a7SXin LI 	  specify_style (OUTPUT_IFDEF);
60518fd37a7SXin LI 	  c -= UNCHANGED_GROUP_FORMAT_OPTION;
60618fd37a7SXin LI 	  specify_value (&group_format[c], optarg, group_format_option[c]);
60718fd37a7SXin LI 	  break;
60818fd37a7SXin LI 
60918fd37a7SXin LI 	default:
61018fd37a7SXin LI 	  try_help (0, 0);
61118fd37a7SXin LI 	}
61218fd37a7SXin LI       prev = c;
61318fd37a7SXin LI     }
61418fd37a7SXin LI 
61518fd37a7SXin LI   if (output_style == OUTPUT_UNSPECIFIED)
61618fd37a7SXin LI     {
61718fd37a7SXin LI       if (show_c_function)
61818fd37a7SXin LI 	{
61918fd37a7SXin LI 	  specify_style (OUTPUT_CONTEXT);
62018fd37a7SXin LI 	  if (ocontext < 0)
62118fd37a7SXin LI 	    context = 3;
62218fd37a7SXin LI 	}
62318fd37a7SXin LI       else
62418fd37a7SXin LI 	specify_style (OUTPUT_NORMAL);
62518fd37a7SXin LI     }
62618fd37a7SXin LI 
62718fd37a7SXin LI   if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
62818fd37a7SXin LI     {
62918fd37a7SXin LI #ifdef ST_MTIM_NSEC
63018fd37a7SXin LI       time_format = "%Y-%m-%d %H:%M:%S.%N %z";
63118fd37a7SXin LI #else
63218fd37a7SXin LI       time_format = "%Y-%m-%d %H:%M:%S %z";
63318fd37a7SXin LI #endif
63418fd37a7SXin LI     }
63518fd37a7SXin LI   else
63618fd37a7SXin LI     {
63718fd37a7SXin LI       /* See POSIX 1003.1-2001 for this format.  */
63818fd37a7SXin LI       time_format = "%a %b %e %T %Y";
63918fd37a7SXin LI     }
64018fd37a7SXin LI 
64118fd37a7SXin LI   if (0 <= ocontext)
64218fd37a7SXin LI     {
64318fd37a7SXin LI       bool modern_usage = 200112 <= posix2_version ();
64418fd37a7SXin LI 
64518fd37a7SXin LI       if ((output_style == OUTPUT_CONTEXT
64618fd37a7SXin LI 	   || output_style == OUTPUT_UNIFIED)
64718fd37a7SXin LI 	  && (context < ocontext
64818fd37a7SXin LI 	      || (ocontext < context && ! explicit_context)))
64918fd37a7SXin LI 	{
65018fd37a7SXin LI 	  if (modern_usage)
65118fd37a7SXin LI 	    {
65218fd37a7SXin LI 	      error (0, 0,
65318fd37a7SXin LI 		     _("`-%ld' option is obsolete; use `-%c %ld'"),
65418fd37a7SXin LI 		     (long int) ocontext,
65518fd37a7SXin LI 		     output_style == OUTPUT_CONTEXT ? 'C' : 'U',
65618fd37a7SXin LI 		     (long int) ocontext);
65718fd37a7SXin LI 	      try_help (0, 0);
65818fd37a7SXin LI 	    }
65918fd37a7SXin LI 	  context = ocontext;
66018fd37a7SXin LI 	}
66118fd37a7SXin LI       else
66218fd37a7SXin LI 	{
66318fd37a7SXin LI 	  if (modern_usage)
66418fd37a7SXin LI 	    {
66518fd37a7SXin LI 	      error (0, 0, _("`-%ld' option is obsolete; omit it"),
66618fd37a7SXin LI 		     (long int) ocontext);
66718fd37a7SXin LI 	      try_help (0, 0);
66818fd37a7SXin LI 	    }
66918fd37a7SXin LI 	}
67018fd37a7SXin LI     }
67118fd37a7SXin LI 
67218fd37a7SXin LI   if (! tabsize)
67318fd37a7SXin LI     tabsize = 8;
67418fd37a7SXin LI   if (! width)
67518fd37a7SXin LI     width = 130;
67618fd37a7SXin LI 
67718fd37a7SXin LI   {
67818fd37a7SXin LI     /* Maximize first the half line width, and then the gutter width,
67918fd37a7SXin LI        according to the following constraints:
68018fd37a7SXin LI 
68118fd37a7SXin LI 	1.  Two half lines plus a gutter must fit in a line.
68218fd37a7SXin LI 	2.  If the half line width is nonzero:
68318fd37a7SXin LI 	    a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
68418fd37a7SXin LI 	    b.  If tabs are not expanded to spaces,
68518fd37a7SXin LI 		a half line plus a gutter is an integral number of tabs,
68618fd37a7SXin LI 		so that tabs in the right column line up.  */
68718fd37a7SXin LI 
68818fd37a7SXin LI     intmax_t t = expand_tabs ? 1 : tabsize;
68918fd37a7SXin LI     intmax_t w = width;
69018fd37a7SXin LI     intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t)  *  t;
69118fd37a7SXin LI     sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
69218fd37a7SXin LI     sdiff_column2_offset = sdiff_half_width ? off : w;
69318fd37a7SXin LI   }
69418fd37a7SXin LI 
69518fd37a7SXin LI   /* Make the horizon at least as large as the context, so that
69618fd37a7SXin LI      shift_boundaries has more freedom to shift the first and last hunks.  */
69718fd37a7SXin LI   if (horizon_lines < context)
69818fd37a7SXin LI     horizon_lines = context;
69918fd37a7SXin LI 
70018fd37a7SXin LI   summarize_regexp_list (&function_regexp_list);
70118fd37a7SXin LI   summarize_regexp_list (&ignore_regexp_list);
70218fd37a7SXin LI 
70318fd37a7SXin LI   if (output_style == OUTPUT_IFDEF)
70418fd37a7SXin LI     {
70518fd37a7SXin LI       for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
70618fd37a7SXin LI 	if (!line_format[i])
70718fd37a7SXin LI 	  line_format[i] = "%l\n";
70818fd37a7SXin LI       if (!group_format[OLD])
70918fd37a7SXin LI 	group_format[OLD]
71018fd37a7SXin LI 	  = group_format[CHANGED] ? group_format[CHANGED] : "%<";
71118fd37a7SXin LI       if (!group_format[NEW])
71218fd37a7SXin LI 	group_format[NEW]
71318fd37a7SXin LI 	  = group_format[CHANGED] ? group_format[CHANGED] : "%>";
71418fd37a7SXin LI       if (!group_format[UNCHANGED])
71518fd37a7SXin LI 	group_format[UNCHANGED] = "%=";
71618fd37a7SXin LI       if (!group_format[CHANGED])
71718fd37a7SXin LI 	group_format[CHANGED] = concat (group_format[OLD],
71818fd37a7SXin LI 					group_format[NEW], "");
71918fd37a7SXin LI     }
72018fd37a7SXin LI 
72118fd37a7SXin LI   no_diff_means_no_output =
72218fd37a7SXin LI     (output_style == OUTPUT_IFDEF ?
72318fd37a7SXin LI       (!*group_format[UNCHANGED]
72418fd37a7SXin LI        || (strcmp (group_format[UNCHANGED], "%=") == 0
72518fd37a7SXin LI 	   && !*line_format[UNCHANGED]))
72618fd37a7SXin LI      : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
72718fd37a7SXin LI 
72818fd37a7SXin LI   files_can_be_treated_as_binary =
72918fd37a7SXin LI     (brief & binary
73018fd37a7SXin LI      & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
73118fd37a7SXin LI 	  | (ignore_regexp_list.regexps || ignore_white_space)));
73218fd37a7SXin LI 
73318fd37a7SXin LI   switch_string = option_list (argv + 1, optind - 1);
73418fd37a7SXin LI 
73518fd37a7SXin LI   if (from_file)
73618fd37a7SXin LI     {
73718fd37a7SXin LI       if (to_file)
73818fd37a7SXin LI 	fatal ("--from-file and --to-file both specified");
73918fd37a7SXin LI       else
74018fd37a7SXin LI 	for (; optind < argc; optind++)
74118fd37a7SXin LI 	  {
74218fd37a7SXin LI 	    int status = compare_files ((struct comparison *) 0,
74318fd37a7SXin LI 					from_file, argv[optind]);
74418fd37a7SXin LI 	    if (exit_status < status)
74518fd37a7SXin LI 	      exit_status = status;
74618fd37a7SXin LI 	  }
74718fd37a7SXin LI     }
74818fd37a7SXin LI   else
74918fd37a7SXin LI     {
75018fd37a7SXin LI       if (to_file)
75118fd37a7SXin LI 	for (; optind < argc; optind++)
75218fd37a7SXin LI 	  {
75318fd37a7SXin LI 	    int status = compare_files ((struct comparison *) 0,
75418fd37a7SXin LI 					argv[optind], to_file);
75518fd37a7SXin LI 	    if (exit_status < status)
75618fd37a7SXin LI 	      exit_status = status;
75718fd37a7SXin LI 	  }
75818fd37a7SXin LI       else
75918fd37a7SXin LI 	{
76018fd37a7SXin LI 	  if (argc - optind != 2)
76118fd37a7SXin LI 	    {
76218fd37a7SXin LI 	      if (argc - optind < 2)
76318fd37a7SXin LI 		try_help ("missing operand after `%s'", argv[argc - 1]);
76418fd37a7SXin LI 	      else
76518fd37a7SXin LI 		try_help ("extra operand `%s'", argv[optind + 2]);
76618fd37a7SXin LI 	    }
76718fd37a7SXin LI 
76818fd37a7SXin LI 	  exit_status = compare_files ((struct comparison *) 0,
76918fd37a7SXin LI 				       argv[optind], argv[optind + 1]);
77018fd37a7SXin LI 	}
77118fd37a7SXin LI     }
77218fd37a7SXin LI 
77318fd37a7SXin LI   /* Print any messages that were saved up for last.  */
77418fd37a7SXin LI   print_message_queue ();
77518fd37a7SXin LI 
77618fd37a7SXin LI   check_stdout ();
77718fd37a7SXin LI   exit (exit_status);
77818fd37a7SXin LI   return exit_status;
77918fd37a7SXin LI }
78018fd37a7SXin LI 
78118fd37a7SXin LI /* Append to REGLIST the regexp PATTERN.  */
78218fd37a7SXin LI 
78318fd37a7SXin LI static void
add_regexp(struct regexp_list * reglist,char const * pattern)78418fd37a7SXin LI add_regexp (struct regexp_list *reglist, char const *pattern)
78518fd37a7SXin LI {
78618fd37a7SXin LI   size_t patlen = strlen (pattern);
78718fd37a7SXin LI   char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
78818fd37a7SXin LI 
78918fd37a7SXin LI   if (m != 0)
79018fd37a7SXin LI     error (0, 0, "%s: %s", pattern, m);
79118fd37a7SXin LI   else
79218fd37a7SXin LI     {
79318fd37a7SXin LI       char *regexps = reglist->regexps;
79418fd37a7SXin LI       size_t len = reglist->len;
79518fd37a7SXin LI       bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
79618fd37a7SXin LI       size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
79718fd37a7SXin LI       size_t size = reglist->size;
79818fd37a7SXin LI 
79918fd37a7SXin LI       if (size <= newlen)
80018fd37a7SXin LI 	{
80118fd37a7SXin LI 	  if (!size)
80218fd37a7SXin LI 	    size = 1;
80318fd37a7SXin LI 
80418fd37a7SXin LI 	  do size *= 2;
80518fd37a7SXin LI 	  while (size <= newlen);
80618fd37a7SXin LI 
80718fd37a7SXin LI 	  reglist->size = size;
80818fd37a7SXin LI 	  reglist->regexps = regexps = xrealloc (regexps, size);
80918fd37a7SXin LI 	}
81018fd37a7SXin LI       if (multiple_regexps)
81118fd37a7SXin LI 	{
81218fd37a7SXin LI 	  regexps[len++] = '\\';
81318fd37a7SXin LI 	  regexps[len++] = '|';
81418fd37a7SXin LI 	}
81518fd37a7SXin LI       memcpy (regexps + len, pattern, patlen + 1);
81618fd37a7SXin LI     }
81718fd37a7SXin LI }
81818fd37a7SXin LI 
81918fd37a7SXin LI /* Ensure that REGLIST represents the disjunction of its regexps.
82018fd37a7SXin LI    This is done here, rather than earlier, to avoid O(N^2) behavior.  */
82118fd37a7SXin LI 
82218fd37a7SXin LI static void
summarize_regexp_list(struct regexp_list * reglist)82318fd37a7SXin LI summarize_regexp_list (struct regexp_list *reglist)
82418fd37a7SXin LI {
82518fd37a7SXin LI   if (reglist->regexps)
82618fd37a7SXin LI     {
82718fd37a7SXin LI       /* At least one regexp was specified.  Allocate a fastmap for it.  */
82818fd37a7SXin LI       reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
82918fd37a7SXin LI       if (reglist->multiple_regexps)
83018fd37a7SXin LI 	{
83118fd37a7SXin LI 	  /* Compile the disjunction of the regexps.
83218fd37a7SXin LI 	     (If just one regexp was specified, it is already compiled.)  */
83318fd37a7SXin LI 	  char const *m = re_compile_pattern (reglist->regexps, reglist->len,
83418fd37a7SXin LI 					      reglist->buf);
83518fd37a7SXin LI 	  if (m != 0)
83618fd37a7SXin LI 	    error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
83718fd37a7SXin LI 	}
83818fd37a7SXin LI     }
83918fd37a7SXin LI }
84018fd37a7SXin LI 
84118fd37a7SXin LI static void
try_help(char const * reason_msgid,char const * operand)84218fd37a7SXin LI try_help (char const *reason_msgid, char const *operand)
84318fd37a7SXin LI {
84418fd37a7SXin LI   if (reason_msgid)
84518fd37a7SXin LI     error (0, 0, _(reason_msgid), operand);
84618fd37a7SXin LI   error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
84718fd37a7SXin LI 	 program_name);
84818fd37a7SXin LI   abort ();
84918fd37a7SXin LI }
85018fd37a7SXin LI 
85118fd37a7SXin LI static void
check_stdout(void)85218fd37a7SXin LI check_stdout (void)
85318fd37a7SXin LI {
85418fd37a7SXin LI   if (ferror (stdout))
85518fd37a7SXin LI     fatal ("write failed");
85618fd37a7SXin LI   else if (fclose (stdout) != 0)
85718fd37a7SXin LI     pfatal_with_name (_("standard output"));
85818fd37a7SXin LI }
85918fd37a7SXin LI 
86018fd37a7SXin LI static char const * const option_help_msgid[] = {
86118fd37a7SXin LI   N_("Compare files line by line."),
86218fd37a7SXin LI   "",
86318fd37a7SXin LI   N_("-i  --ignore-case  Ignore case differences in file contents."),
86418fd37a7SXin LI   N_("--ignore-file-name-case  Ignore case when comparing file names."),
86518fd37a7SXin LI   N_("--no-ignore-file-name-case  Consider case when comparing file names."),
86618fd37a7SXin LI   N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
86718fd37a7SXin LI   N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
86818fd37a7SXin LI   N_("-w  --ignore-all-space  Ignore all white space."),
86918fd37a7SXin LI   N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
87018fd37a7SXin LI   N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
87118fd37a7SXin LI   N_("--strip-trailing-cr  Strip trailing carriage return on input."),
87218fd37a7SXin LI #if HAVE_SETMODE_DOS
87318fd37a7SXin LI   N_("--binary  Read and write data in binary mode."),
87418fd37a7SXin LI #endif
87518fd37a7SXin LI   N_("-a  --text  Treat all files as text."),
87618fd37a7SXin LI   "",
87718fd37a7SXin LI   N_("-c  -C NUM  --context[=NUM]  Output NUM (default 3) lines of copied context.\n\
87818fd37a7SXin LI -u  -U NUM  --unified[=NUM]  Output NUM (default 3) lines of unified context.\n\
87918fd37a7SXin LI   --label LABEL  Use LABEL instead of file name.\n\
88018fd37a7SXin LI   -p  --show-c-function  Show which C function each change is in.\n\
88118fd37a7SXin LI   -F RE  --show-function-line=RE  Show the most recent line matching RE."),
88218fd37a7SXin LI   N_("-q  --brief  Output only whether files differ."),
88318fd37a7SXin LI   N_("-e  --ed  Output an ed script."),
88418fd37a7SXin LI   N_("--normal  Output a normal diff."),
88518fd37a7SXin LI   N_("-n  --rcs  Output an RCS format diff."),
88618fd37a7SXin LI   N_("-y  --side-by-side  Output in two columns.\n\
88718fd37a7SXin LI   -W NUM  --width=NUM  Output at most NUM (default 130) print columns.\n\
88818fd37a7SXin LI   --left-column  Output only the left column of common lines.\n\
88918fd37a7SXin LI   --suppress-common-lines  Do not output common lines."),
89018fd37a7SXin LI   N_("-D NAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs."),
89118fd37a7SXin LI   N_("--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT."),
89218fd37a7SXin LI   N_("--line-format=LFMT  Similar, but format all input lines with LFMT."),
89318fd37a7SXin LI   N_("--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT."),
89418fd37a7SXin LI   N_("  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'."),
89518fd37a7SXin LI   N_("  GFMT may contain:\n\
89618fd37a7SXin LI     %<  lines from FILE1\n\
89718fd37a7SXin LI     %>  lines from FILE2\n\
89818fd37a7SXin LI     %=  lines common to FILE1 and FILE2\n\
89918fd37a7SXin LI     %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER\n\
90018fd37a7SXin LI       LETTERs are as follows for new group, lower case for old group:\n\
90118fd37a7SXin LI         F  first line number\n\
90218fd37a7SXin LI         L  last line number\n\
90318fd37a7SXin LI         N  number of lines = L-F+1\n\
90418fd37a7SXin LI         E  F-1\n\
90518fd37a7SXin LI         M  L+1"),
90618fd37a7SXin LI   N_("  LFMT may contain:\n\
90718fd37a7SXin LI     %L  contents of line\n\
90818fd37a7SXin LI     %l  contents of line, excluding any trailing newline\n\
90918fd37a7SXin LI     %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number"),
91018fd37a7SXin LI   N_("  Either GFMT or LFMT may contain:\n\
91118fd37a7SXin LI     %%  %\n\
91218fd37a7SXin LI     %c'C'  the single character C\n\
91318fd37a7SXin LI     %c'\\OOO'  the character with octal code OOO"),
91418fd37a7SXin LI   "",
91518fd37a7SXin LI   N_("-l  --paginate  Pass the output through `pr' to paginate it."),
91618fd37a7SXin LI   N_("-t  --expand-tabs  Expand tabs to spaces in output."),
91718fd37a7SXin LI   N_("-T  --initial-tab  Make tabs line up by prepending a tab."),
91818fd37a7SXin LI   N_("--tabsize=NUM  Tab stops are every NUM (default 8) print columns."),
91918fd37a7SXin LI   "",
92018fd37a7SXin LI   N_("-r  --recursive  Recursively compare any subdirectories found."),
92118fd37a7SXin LI   N_("-N  --new-file  Treat absent files as empty."),
92218fd37a7SXin LI   N_("--unidirectional-new-file  Treat absent first files as empty."),
92318fd37a7SXin LI   N_("-s  --report-identical-files  Report when two files are the same."),
92418fd37a7SXin LI   N_("-x PAT  --exclude=PAT  Exclude files that match PAT."),
92518fd37a7SXin LI   N_("-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE."),
92618fd37a7SXin LI   N_("-S FILE  --starting-file=FILE  Start with FILE when comparing directories."),
92718fd37a7SXin LI   N_("--from-file=FILE1  Compare FILE1 to all operands.  FILE1 can be a directory."),
92818fd37a7SXin LI   N_("--to-file=FILE2  Compare all operands to FILE2.  FILE2 can be a directory."),
92918fd37a7SXin LI   "",
93018fd37a7SXin LI   N_("--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix."),
93118fd37a7SXin LI   N_("-d  --minimal  Try hard to find a smaller set of changes."),
93218fd37a7SXin LI   N_("--speed-large-files  Assume large files and many scattered small changes."),
93318fd37a7SXin LI   "",
93418fd37a7SXin LI   N_("-v  --version  Output version info."),
93518fd37a7SXin LI   N_("--help  Output this help."),
93618fd37a7SXin LI   "",
93718fd37a7SXin LI   N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
93818fd37a7SXin LI   N_("If --from-file or --to-file is given, there are no restrictions on FILES."),
93918fd37a7SXin LI   N_("If a FILE is `-', read standard input."),
94018fd37a7SXin LI   N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
94118fd37a7SXin LI   "",
94218fd37a7SXin LI   N_("Report bugs to <bug-gnu-utils@gnu.org>."),
94318fd37a7SXin LI   0
94418fd37a7SXin LI };
94518fd37a7SXin LI 
94618fd37a7SXin LI static void
usage(void)94718fd37a7SXin LI usage (void)
94818fd37a7SXin LI {
94918fd37a7SXin LI   char const * const *p;
95018fd37a7SXin LI 
95118fd37a7SXin LI   printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
95218fd37a7SXin LI 
95318fd37a7SXin LI   for (p = option_help_msgid;  *p;  p++)
95418fd37a7SXin LI     {
95518fd37a7SXin LI       if (!**p)
95618fd37a7SXin LI 	putchar ('\n');
95718fd37a7SXin LI       else
95818fd37a7SXin LI 	{
95918fd37a7SXin LI 	  char const *msg = _(*p);
96018fd37a7SXin LI 	  char const *nl;
96118fd37a7SXin LI 	  while ((nl = strchr (msg, '\n')))
96218fd37a7SXin LI 	    {
96318fd37a7SXin LI 	      int msglen = nl + 1 - msg;
96418fd37a7SXin LI 	      printf ("  %.*s", msglen, msg);
96518fd37a7SXin LI 	      msg = nl + 1;
96618fd37a7SXin LI 	    }
96718fd37a7SXin LI 
96818fd37a7SXin LI 	  printf ("  %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
96918fd37a7SXin LI 	}
97018fd37a7SXin LI     }
97118fd37a7SXin LI }
97218fd37a7SXin LI 
97318fd37a7SXin LI /* Set VAR to VALUE, reporting an OPTION error if this is a
97418fd37a7SXin LI    conflict.  */
97518fd37a7SXin LI static void
specify_value(char const ** var,char const * value,char const * option)97618fd37a7SXin LI specify_value (char const **var, char const *value, char const *option)
97718fd37a7SXin LI {
97818fd37a7SXin LI   if (*var && strcmp (*var, value) != 0)
97918fd37a7SXin LI     {
98018fd37a7SXin LI       error (0, 0, _("conflicting %s option value `%s'"), option, value);
98118fd37a7SXin LI       try_help (0, 0);
98218fd37a7SXin LI     }
98318fd37a7SXin LI   *var = value;
98418fd37a7SXin LI }
98518fd37a7SXin LI 
98618fd37a7SXin LI /* Set the output style to STYLE, diagnosing conflicts.  */
98718fd37a7SXin LI static void
specify_style(enum output_style style)98818fd37a7SXin LI specify_style (enum output_style style)
98918fd37a7SXin LI {
99018fd37a7SXin LI   if (output_style != style)
99118fd37a7SXin LI     {
99218fd37a7SXin LI       output_style = style;
99318fd37a7SXin LI     }
99418fd37a7SXin LI }
99518fd37a7SXin LI 
99618fd37a7SXin LI /* Set the last-modified time of *ST to be the current time.  */
99718fd37a7SXin LI 
99818fd37a7SXin LI static void
set_mtime_to_now(struct stat * st)99918fd37a7SXin LI set_mtime_to_now (struct stat *st)
100018fd37a7SXin LI {
100118fd37a7SXin LI #ifdef ST_MTIM_NSEC
100218fd37a7SXin LI 
100318fd37a7SXin LI # if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
100418fd37a7SXin LI   if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0)
100518fd37a7SXin LI     return;
100618fd37a7SXin LI # endif
100718fd37a7SXin LI 
100818fd37a7SXin LI # if HAVE_GETTIMEOFDAY
100918fd37a7SXin LI   {
101018fd37a7SXin LI     struct timeval timeval;
101118fd37a7SXin LI     if (gettimeofday (&timeval, 0) == 0)
101218fd37a7SXin LI       {
101318fd37a7SXin LI 	st->st_mtime = timeval.tv_sec;
101418fd37a7SXin LI 	st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000;
101518fd37a7SXin LI 	return;
101618fd37a7SXin LI       }
101718fd37a7SXin LI   }
101818fd37a7SXin LI # endif
101918fd37a7SXin LI 
102018fd37a7SXin LI #endif /* ST_MTIM_NSEC */
102118fd37a7SXin LI 
102218fd37a7SXin LI   time (&st->st_mtime);
102318fd37a7SXin LI }
102418fd37a7SXin LI 
102518fd37a7SXin LI /* Compare two files (or dirs) with parent comparison PARENT
102618fd37a7SXin LI    and names NAME0 and NAME1.
102718fd37a7SXin LI    (If PARENT is 0, then the first name is just NAME0, etc.)
102818fd37a7SXin LI    This is self-contained; it opens the files and closes them.
102918fd37a7SXin LI 
103018fd37a7SXin LI    Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
103118fd37a7SXin LI    different, EXIT_TROUBLE if there is a problem opening them.  */
103218fd37a7SXin LI 
103318fd37a7SXin LI static int
compare_files(struct comparison const * parent,char const * name0,char const * name1)103418fd37a7SXin LI compare_files (struct comparison const *parent,
103518fd37a7SXin LI 	       char const *name0,
103618fd37a7SXin LI 	       char const *name1)
103718fd37a7SXin LI {
103818fd37a7SXin LI   struct comparison cmp;
103918fd37a7SXin LI #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
104018fd37a7SXin LI   register int f;
104118fd37a7SXin LI   int status = EXIT_SUCCESS;
104218fd37a7SXin LI   bool same_files;
104318fd37a7SXin LI   char *free0, *free1;
104418fd37a7SXin LI 
104518fd37a7SXin LI   /* If this is directory comparison, perhaps we have a file
104618fd37a7SXin LI      that exists only in one of the directories.
104718fd37a7SXin LI      If so, just print a message to that effect.  */
104818fd37a7SXin LI 
104918fd37a7SXin LI   if (! ((name0 && name1)
105018fd37a7SXin LI 	 || (unidirectional_new_file && name1)
105118fd37a7SXin LI 	 || new_file))
105218fd37a7SXin LI     {
105318fd37a7SXin LI       char const *name = name0 == 0 ? name1 : name0;
105418fd37a7SXin LI       char const *dir = parent->file[name0 == 0].name;
105518fd37a7SXin LI 
105618fd37a7SXin LI       /* See POSIX 1003.1-2001 for this format.  */
105718fd37a7SXin LI       message ("Only in %s: %s\n", dir, name);
105818fd37a7SXin LI 
105918fd37a7SXin LI       /* Return EXIT_FAILURE so that diff_dirs will return
106018fd37a7SXin LI 	 EXIT_FAILURE ("some files differ").  */
106118fd37a7SXin LI       return EXIT_FAILURE;
106218fd37a7SXin LI     }
106318fd37a7SXin LI 
106418fd37a7SXin LI   memset (cmp.file, 0, sizeof cmp.file);
106518fd37a7SXin LI   cmp.parent = parent;
106618fd37a7SXin LI 
106718fd37a7SXin LI   /* cmp.file[f].desc markers */
106818fd37a7SXin LI #define NONEXISTENT (-1) /* nonexistent file */
106918fd37a7SXin LI #define UNOPENED (-2) /* unopened file (e.g. directory) */
107018fd37a7SXin LI #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
107118fd37a7SXin LI 
107218fd37a7SXin LI #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
107318fd37a7SXin LI 
107418fd37a7SXin LI   cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED;
107518fd37a7SXin LI   cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED;
107618fd37a7SXin LI 
107718fd37a7SXin LI   /* Now record the full name of each file, including nonexistent ones.  */
107818fd37a7SXin LI 
107918fd37a7SXin LI   if (name0 == 0)
108018fd37a7SXin LI     name0 = name1;
108118fd37a7SXin LI   if (name1 == 0)
108218fd37a7SXin LI     name1 = name0;
108318fd37a7SXin LI 
108418fd37a7SXin LI   if (!parent)
108518fd37a7SXin LI     {
108618fd37a7SXin LI       free0 = 0;
108718fd37a7SXin LI       free1 = 0;
108818fd37a7SXin LI       cmp.file[0].name = name0;
108918fd37a7SXin LI       cmp.file[1].name = name1;
109018fd37a7SXin LI     }
109118fd37a7SXin LI   else
109218fd37a7SXin LI     {
109318fd37a7SXin LI       cmp.file[0].name = free0
109418fd37a7SXin LI 	= dir_file_pathname (parent->file[0].name, name0);
109518fd37a7SXin LI       cmp.file[1].name = free1
109618fd37a7SXin LI 	= dir_file_pathname (parent->file[1].name, name1);
109718fd37a7SXin LI     }
109818fd37a7SXin LI 
109918fd37a7SXin LI   /* Stat the files.  */
110018fd37a7SXin LI 
110118fd37a7SXin LI   for (f = 0; f < 2; f++)
110218fd37a7SXin LI     {
110318fd37a7SXin LI       if (cmp.file[f].desc != NONEXISTENT)
110418fd37a7SXin LI 	{
110518fd37a7SXin LI 	  if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
110618fd37a7SXin LI 	    {
110718fd37a7SXin LI 	      cmp.file[f].desc = cmp.file[0].desc;
110818fd37a7SXin LI 	      cmp.file[f].stat = cmp.file[0].stat;
110918fd37a7SXin LI 	    }
111018fd37a7SXin LI 	  else if (strcmp (cmp.file[f].name, "-") == 0)
111118fd37a7SXin LI 	    {
111218fd37a7SXin LI 	      cmp.file[f].desc = STDIN_FILENO;
111318fd37a7SXin LI 	      if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
111418fd37a7SXin LI 		cmp.file[f].desc = ERRNO_ENCODE (errno);
111518fd37a7SXin LI 	      else
111618fd37a7SXin LI 		{
111718fd37a7SXin LI 		  if (S_ISREG (cmp.file[f].stat.st_mode))
111818fd37a7SXin LI 		    {
111918fd37a7SXin LI 		      off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
112018fd37a7SXin LI 		      if (pos < 0)
112118fd37a7SXin LI 			cmp.file[f].desc = ERRNO_ENCODE (errno);
112218fd37a7SXin LI 		      else
112318fd37a7SXin LI 			cmp.file[f].stat.st_size =
112418fd37a7SXin LI 			  MAX (0, cmp.file[f].stat.st_size - pos);
112518fd37a7SXin LI 		    }
112618fd37a7SXin LI 
112718fd37a7SXin LI 		  /* POSIX 1003.1-2001 requires current time for
112818fd37a7SXin LI 		     stdin.  */
112918fd37a7SXin LI 		  set_mtime_to_now (&cmp.file[f].stat);
113018fd37a7SXin LI 		}
113118fd37a7SXin LI 	    }
113218fd37a7SXin LI 	  else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
113318fd37a7SXin LI 	    cmp.file[f].desc = ERRNO_ENCODE (errno);
113418fd37a7SXin LI 	}
113518fd37a7SXin LI     }
113618fd37a7SXin LI 
113718fd37a7SXin LI   /* Mark files as nonexistent as needed for -N and -P, if they are
113818fd37a7SXin LI      inaccessible empty regular files (the kind of files that 'patch'
113918fd37a7SXin LI      creates to indicate nonexistent backups), or if they are
114018fd37a7SXin LI      top-level files that do not exist but their counterparts do
114118fd37a7SXin LI      exist.  */
114218fd37a7SXin LI   for (f = 0; f < 2; f++)
114318fd37a7SXin LI     if ((new_file || (f == 0 && unidirectional_new_file))
114418fd37a7SXin LI 	&& (cmp.file[f].desc == UNOPENED
114518fd37a7SXin LI 	    ? (S_ISREG (cmp.file[f].stat.st_mode)
114618fd37a7SXin LI 	       && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
114718fd37a7SXin LI 	       && cmp.file[f].stat.st_size == 0)
114818fd37a7SXin LI 	    : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
114918fd37a7SXin LI 	       && ! parent
115018fd37a7SXin LI 	       && cmp.file[1 - f].desc == UNOPENED)))
115118fd37a7SXin LI       cmp.file[f].desc = NONEXISTENT;
115218fd37a7SXin LI 
115318fd37a7SXin LI   for (f = 0; f < 2; f++)
115418fd37a7SXin LI     if (cmp.file[f].desc == NONEXISTENT)
115518fd37a7SXin LI       {
115618fd37a7SXin LI 	memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
115718fd37a7SXin LI 	cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
115818fd37a7SXin LI       }
115918fd37a7SXin LI 
116018fd37a7SXin LI   for (f = 0; f < 2; f++)
116118fd37a7SXin LI     {
116218fd37a7SXin LI       int e = ERRNO_DECODE (cmp.file[f].desc);
116318fd37a7SXin LI       if (0 <= e)
116418fd37a7SXin LI 	{
116518fd37a7SXin LI 	  errno = e;
116618fd37a7SXin LI 	  perror_with_name (cmp.file[f].name);
116718fd37a7SXin LI 	  status = EXIT_TROUBLE;
116818fd37a7SXin LI 	}
116918fd37a7SXin LI     }
117018fd37a7SXin LI 
117118fd37a7SXin LI   if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
117218fd37a7SXin LI     {
117318fd37a7SXin LI       /* If one is a directory, and it was specified in the command line,
117418fd37a7SXin LI 	 use the file in that dir with the other file's basename.  */
117518fd37a7SXin LI 
117618fd37a7SXin LI       int fnm_arg = DIR_P (0);
117718fd37a7SXin LI       int dir_arg = 1 - fnm_arg;
117818fd37a7SXin LI       char const *fnm = cmp.file[fnm_arg].name;
117918fd37a7SXin LI       char const *dir = cmp.file[dir_arg].name;
118018fd37a7SXin LI       char const *filename = cmp.file[dir_arg].name = free0
118118fd37a7SXin LI 	= dir_file_pathname (dir, base_name (fnm));
118218fd37a7SXin LI 
118318fd37a7SXin LI       if (strcmp (fnm, "-") == 0)
118418fd37a7SXin LI 	fatal ("cannot compare `-' to a directory");
118518fd37a7SXin LI 
118618fd37a7SXin LI       if (stat (filename, &cmp.file[dir_arg].stat) != 0)
118718fd37a7SXin LI 	{
118818fd37a7SXin LI 	  perror_with_name (filename);
118918fd37a7SXin LI 	  status = EXIT_TROUBLE;
119018fd37a7SXin LI 	}
119118fd37a7SXin LI     }
119218fd37a7SXin LI 
119318fd37a7SXin LI   if (status != EXIT_SUCCESS)
119418fd37a7SXin LI     {
119518fd37a7SXin LI       /* One of the files should exist but does not.  */
119618fd37a7SXin LI     }
119718fd37a7SXin LI   else if (cmp.file[0].desc == NONEXISTENT
119818fd37a7SXin LI 	   && cmp.file[1].desc == NONEXISTENT)
119918fd37a7SXin LI     {
120018fd37a7SXin LI       /* Neither file "exists", so there's nothing to compare.  */
120118fd37a7SXin LI     }
120218fd37a7SXin LI   else if ((same_files
120318fd37a7SXin LI 	    = (cmp.file[0].desc != NONEXISTENT
120418fd37a7SXin LI 	       && cmp.file[1].desc != NONEXISTENT
120518fd37a7SXin LI 	       && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
120618fd37a7SXin LI 	       && same_file_attributes (&cmp.file[0].stat,
120718fd37a7SXin LI 					&cmp.file[1].stat)))
120818fd37a7SXin LI 	   && no_diff_means_no_output)
120918fd37a7SXin LI     {
121018fd37a7SXin LI       /* The two named files are actually the same physical file.
121118fd37a7SXin LI 	 We know they are identical without actually reading them.  */
121218fd37a7SXin LI     }
121318fd37a7SXin LI   else if (DIR_P (0) & DIR_P (1))
121418fd37a7SXin LI     {
121518fd37a7SXin LI       if (output_style == OUTPUT_IFDEF)
121618fd37a7SXin LI 	fatal ("-D option not supported with directories");
121718fd37a7SXin LI 
121818fd37a7SXin LI       /* If both are directories, compare the files in them.  */
121918fd37a7SXin LI 
122018fd37a7SXin LI       if (parent && !recursive)
122118fd37a7SXin LI 	{
122218fd37a7SXin LI 	  /* But don't compare dir contents one level down
122318fd37a7SXin LI 	     unless -r was specified.
122418fd37a7SXin LI 	     See POSIX 1003.1-2001 for this format.  */
122518fd37a7SXin LI 	  message ("Common subdirectories: %s and %s\n",
122618fd37a7SXin LI 		   cmp.file[0].name, cmp.file[1].name);
122718fd37a7SXin LI 	}
122818fd37a7SXin LI       else
122918fd37a7SXin LI 	status = diff_dirs (&cmp, compare_files);
123018fd37a7SXin LI     }
123118fd37a7SXin LI   else if ((DIR_P (0) | DIR_P (1))
123218fd37a7SXin LI 	   || (parent
123318fd37a7SXin LI 	       && (! S_ISREG (cmp.file[0].stat.st_mode)
123418fd37a7SXin LI 		   || ! S_ISREG (cmp.file[1].stat.st_mode))))
123518fd37a7SXin LI     {
123618fd37a7SXin LI       if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
123718fd37a7SXin LI 	{
123818fd37a7SXin LI 	  /* We have a subdirectory that exists only in one directory.  */
123918fd37a7SXin LI 
124018fd37a7SXin LI 	  if ((DIR_P (0) | DIR_P (1))
124118fd37a7SXin LI 	      && recursive
124218fd37a7SXin LI 	      && (new_file
124318fd37a7SXin LI 		  || (unidirectional_new_file
124418fd37a7SXin LI 		      && cmp.file[0].desc == NONEXISTENT)))
124518fd37a7SXin LI 	    status = diff_dirs (&cmp, compare_files);
124618fd37a7SXin LI 	  else
124718fd37a7SXin LI 	    {
124818fd37a7SXin LI 	      char const *dir
124918fd37a7SXin LI 		= parent->file[cmp.file[0].desc == NONEXISTENT].name;
125018fd37a7SXin LI 
125118fd37a7SXin LI 	      /* See POSIX 1003.1-2001 for this format.  */
125218fd37a7SXin LI 	      message ("Only in %s: %s\n", dir, name0);
125318fd37a7SXin LI 
125418fd37a7SXin LI 	      status = EXIT_FAILURE;
125518fd37a7SXin LI 	    }
125618fd37a7SXin LI 	}
125718fd37a7SXin LI       else
125818fd37a7SXin LI 	{
125918fd37a7SXin LI 	  /* We have two files that are not to be compared.  */
126018fd37a7SXin LI 
126118fd37a7SXin LI 	  /* See POSIX 1003.1-2001 for this format.  */
126218fd37a7SXin LI 	  message5 ("File %s is a %s while file %s is a %s\n",
126318fd37a7SXin LI 		    file_label[0] ? file_label[0] : cmp.file[0].name,
126418fd37a7SXin LI 		    file_type (&cmp.file[0].stat),
126518fd37a7SXin LI 		    file_label[1] ? file_label[1] : cmp.file[1].name,
126618fd37a7SXin LI 		    file_type (&cmp.file[1].stat));
126718fd37a7SXin LI 
126818fd37a7SXin LI 	  /* This is a difference.  */
126918fd37a7SXin LI 	  status = EXIT_FAILURE;
127018fd37a7SXin LI 	}
127118fd37a7SXin LI     }
127218fd37a7SXin LI   else if (files_can_be_treated_as_binary
127318fd37a7SXin LI 	   && S_ISREG (cmp.file[0].stat.st_mode)
127418fd37a7SXin LI 	   && S_ISREG (cmp.file[1].stat.st_mode)
127518fd37a7SXin LI 	   && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size)
127618fd37a7SXin LI     {
127718fd37a7SXin LI       message ("Files %s and %s differ\n",
127818fd37a7SXin LI 	       file_label[0] ? file_label[0] : cmp.file[0].name,
127918fd37a7SXin LI 	       file_label[1] ? file_label[1] : cmp.file[1].name);
128018fd37a7SXin LI       status = EXIT_FAILURE;
128118fd37a7SXin LI     }
128218fd37a7SXin LI   else
128318fd37a7SXin LI     {
128418fd37a7SXin LI       /* Both exist and neither is a directory.  */
128518fd37a7SXin LI 
128618fd37a7SXin LI       /* Open the files and record their descriptors.  */
128718fd37a7SXin LI 
128818fd37a7SXin LI       if (cmp.file[0].desc == UNOPENED)
128918fd37a7SXin LI 	if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0)
129018fd37a7SXin LI 	  {
129118fd37a7SXin LI 	    perror_with_name (cmp.file[0].name);
129218fd37a7SXin LI 	    status = EXIT_TROUBLE;
129318fd37a7SXin LI 	  }
129418fd37a7SXin LI       if (cmp.file[1].desc == UNOPENED)
129518fd37a7SXin LI 	{
129618fd37a7SXin LI 	  if (same_files)
129718fd37a7SXin LI 	    cmp.file[1].desc = cmp.file[0].desc;
129818fd37a7SXin LI 	  else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0))
129918fd37a7SXin LI 		   < 0)
130018fd37a7SXin LI 	    {
130118fd37a7SXin LI 	      perror_with_name (cmp.file[1].name);
130218fd37a7SXin LI 	      status = EXIT_TROUBLE;
130318fd37a7SXin LI 	    }
130418fd37a7SXin LI 	}
130518fd37a7SXin LI 
130618fd37a7SXin LI #if HAVE_SETMODE_DOS
130718fd37a7SXin LI       if (binary)
130818fd37a7SXin LI 	for (f = 0; f < 2; f++)
130918fd37a7SXin LI 	  if (0 <= cmp.file[f].desc)
131018fd37a7SXin LI 	    set_binary_mode (cmp.file[f].desc, true);
131118fd37a7SXin LI #endif
131218fd37a7SXin LI 
131318fd37a7SXin LI       /* Compare the files, if no error was found.  */
131418fd37a7SXin LI 
131518fd37a7SXin LI       if (status == EXIT_SUCCESS)
131618fd37a7SXin LI 	status = diff_2_files (&cmp);
131718fd37a7SXin LI 
131818fd37a7SXin LI       /* Close the file descriptors.  */
131918fd37a7SXin LI 
132018fd37a7SXin LI       if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
132118fd37a7SXin LI 	{
132218fd37a7SXin LI 	  perror_with_name (cmp.file[0].name);
132318fd37a7SXin LI 	  status = EXIT_TROUBLE;
132418fd37a7SXin LI 	}
132518fd37a7SXin LI       if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
132618fd37a7SXin LI 	  && close (cmp.file[1].desc) != 0)
132718fd37a7SXin LI 	{
132818fd37a7SXin LI 	  perror_with_name (cmp.file[1].name);
132918fd37a7SXin LI 	  status = EXIT_TROUBLE;
133018fd37a7SXin LI 	}
133118fd37a7SXin LI     }
133218fd37a7SXin LI 
133318fd37a7SXin LI   /* Now the comparison has been done, if no error prevented it,
133418fd37a7SXin LI      and STATUS is the value this function will return.  */
133518fd37a7SXin LI 
133618fd37a7SXin LI   if (status == EXIT_SUCCESS)
133718fd37a7SXin LI     {
133818fd37a7SXin LI       if (report_identical_files && !DIR_P (0))
133918fd37a7SXin LI 	message ("Files %s and %s are identical\n",
134018fd37a7SXin LI 		 file_label[0] ? file_label[0] : cmp.file[0].name,
134118fd37a7SXin LI 		 file_label[1] ? file_label[1] : cmp.file[1].name);
134218fd37a7SXin LI     }
134318fd37a7SXin LI   else
134418fd37a7SXin LI     {
134518fd37a7SXin LI       /* Flush stdout so that the user sees differences immediately.
134618fd37a7SXin LI 	 This can hurt performance, unfortunately.  */
134718fd37a7SXin LI       if (fflush (stdout) != 0)
134818fd37a7SXin LI 	pfatal_with_name (_("standard output"));
134918fd37a7SXin LI     }
135018fd37a7SXin LI 
135118fd37a7SXin LI   if (free0)
135218fd37a7SXin LI     free (free0);
135318fd37a7SXin LI   if (free1)
135418fd37a7SXin LI     free (free1);
135518fd37a7SXin LI 
135618fd37a7SXin LI   return status;
135718fd37a7SXin LI }
1358