xref: /netbsd-src/external/gpl2/diffutils/dist/src/diff.c (revision f8c23a2b94243924f9b7311eb0ad24bf23d5c657)
1*f8c23a2bSchristos /*	$NetBSD: diff.c,v 1.2 2016/01/13 03:39:28 christos Exp $	*/
275f6d617Schristos 
375f6d617Schristos /* diff - compare files line by line
475f6d617Schristos 
575f6d617Schristos    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002
675f6d617Schristos    Free Software Foundation, Inc.
775f6d617Schristos 
875f6d617Schristos    This file is part of GNU DIFF.
975f6d617Schristos 
1075f6d617Schristos    GNU DIFF is free software; you can redistribute it and/or modify
1175f6d617Schristos    it under the terms of the GNU General Public License as published by
1275f6d617Schristos    the Free Software Foundation; either version 2, or (at your option)
1375f6d617Schristos    any later version.
1475f6d617Schristos 
1575f6d617Schristos    GNU DIFF is distributed in the hope that it will be useful,
1675f6d617Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
1775f6d617Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1875f6d617Schristos    See the GNU General Public License for more details.
1975f6d617Schristos 
2075f6d617Schristos    You should have received a copy of the GNU General Public License
2175f6d617Schristos    along with GNU DIFF; see the file COPYING.
2275f6d617Schristos    If not, write to the Free Software Foundation,
2375f6d617Schristos    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2475f6d617Schristos 
2575f6d617Schristos #define GDIFF_MAIN
2675f6d617Schristos #include "diff.h"
2775f6d617Schristos #include <c-stack.h>
2875f6d617Schristos #include <dirname.h>
2975f6d617Schristos #include <error.h>
3075f6d617Schristos #include <exclude.h>
3175f6d617Schristos #include <exitfail.h>
3275f6d617Schristos #include <fnmatch.h>
3375f6d617Schristos #include <freesoft.h>
3475f6d617Schristos #include <getopt.h>
3575f6d617Schristos #include <hard-locale.h>
3675f6d617Schristos #include <prepargs.h>
3775f6d617Schristos #include <quotesys.h>
3875f6d617Schristos #include <regex.h>
3975f6d617Schristos #include <setmode.h>
4075f6d617Schristos #include <xalloc.h>
41*f8c23a2bSchristos #include <posixver.h>
4275f6d617Schristos 
4375f6d617Schristos static char const authorship_msgid[] =
4475f6d617Schristos   N_("Written by Paul Eggert, Mike Haertel, David Hayes,\n\
4575f6d617Schristos Richard Stallman, and Len Tower.");
4675f6d617Schristos 
4775f6d617Schristos static char const copyright_string[] =
4875f6d617Schristos   "Copyright (C) 2002 Free Software Foundation, Inc.";
4975f6d617Schristos 
5075f6d617Schristos #ifndef GUTTER_WIDTH_MINIMUM
5175f6d617Schristos # define GUTTER_WIDTH_MINIMUM 3
5275f6d617Schristos #endif
5375f6d617Schristos 
5475f6d617Schristos struct regexp_list
5575f6d617Schristos {
5675f6d617Schristos   char *regexps;	/* chars representing disjunction of the regexps */
5775f6d617Schristos   size_t len;		/* chars used in `regexps' */
5875f6d617Schristos   size_t size;		/* size malloc'ed for `regexps'; 0 if not malloc'ed */
5975f6d617Schristos   bool multiple_regexps;/* Does `regexps' represent a disjunction?  */
6075f6d617Schristos   struct re_pattern_buffer *buf;
6175f6d617Schristos };
6275f6d617Schristos 
6375f6d617Schristos static int compare_files (struct comparison const *, char const *, char const *);
6475f6d617Schristos static void add_regexp (struct regexp_list *, char const *);
6575f6d617Schristos static void summarize_regexp_list (struct regexp_list *);
6675f6d617Schristos static void specify_style (enum output_style);
6775f6d617Schristos static void specify_value (char const **, char const *, char const *);
6875f6d617Schristos static void try_help (char const *, char const *) __attribute__((noreturn));
6975f6d617Schristos static void check_stdout (void);
7075f6d617Schristos static void usage (void);
7175f6d617Schristos 
7275f6d617Schristos /* If comparing directories, compare their common subdirectories
7375f6d617Schristos    recursively.  */
7475f6d617Schristos static bool recursive;
7575f6d617Schristos 
7675f6d617Schristos /* In context diffs, show previous lines that match these regexps.  */
7775f6d617Schristos static struct regexp_list function_regexp_list;
7875f6d617Schristos 
7975f6d617Schristos /* Ignore changes affecting only lines that match these regexps.  */
8075f6d617Schristos static struct regexp_list ignore_regexp_list;
8175f6d617Schristos 
8275f6d617Schristos #if HAVE_SETMODE_DOS
8375f6d617Schristos /* Use binary I/O when reading and writing data (--binary).
8475f6d617Schristos    On POSIX hosts, this has no effect.  */
8575f6d617Schristos static bool binary;
8675f6d617Schristos #endif
8775f6d617Schristos 
8875f6d617Schristos /* When comparing directories, if a file appears only in one
8975f6d617Schristos    directory, treat it as present but empty in the other (-N).
9075f6d617Schristos    Then `patch' would create the file with appropriate contents.  */
9175f6d617Schristos static bool new_file;
9275f6d617Schristos 
9375f6d617Schristos /* When comparing directories, if a file appears only in the second
9475f6d617Schristos    directory of the two, treat it as present but empty in the other
9575f6d617Schristos    (--unidirectional-new-file).
9675f6d617Schristos    Then `patch' would create the file with appropriate contents.  */
9775f6d617Schristos static bool unidirectional_new_file;
9875f6d617Schristos 
9975f6d617Schristos /* Report files compared that are the same (-s).
10075f6d617Schristos    Normally nothing is output when that happens.  */
10175f6d617Schristos static bool report_identical_files;
10275f6d617Schristos 
10375f6d617Schristos 
10475f6d617Schristos /* Return a string containing the command options with which diff was invoked.
10575f6d617Schristos    Spaces appear between what were separate ARGV-elements.
10675f6d617Schristos    There is a space at the beginning but none at the end.
10775f6d617Schristos    If there were no options, the result is an empty string.
10875f6d617Schristos 
10975f6d617Schristos    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
11075f6d617Schristos    the length of that vector.  */
11175f6d617Schristos 
11275f6d617Schristos static char *
option_list(char ** optionvec,int count)11375f6d617Schristos option_list (char **optionvec, int count)
11475f6d617Schristos {
11575f6d617Schristos   int i;
11675f6d617Schristos   size_t size = 1;
11775f6d617Schristos   char *result;
11875f6d617Schristos   char *p;
11975f6d617Schristos 
12075f6d617Schristos   for (i = 0; i < count; i++)
12175f6d617Schristos     size += 1 + quote_system_arg ((char *) 0, optionvec[i]);
12275f6d617Schristos 
12375f6d617Schristos   p = result = xmalloc (size);
12475f6d617Schristos 
12575f6d617Schristos   for (i = 0; i < count; i++)
12675f6d617Schristos     {
12775f6d617Schristos       *p++ = ' ';
12875f6d617Schristos       p += quote_system_arg (p, optionvec[i]);
12975f6d617Schristos     }
13075f6d617Schristos 
13175f6d617Schristos   *p = 0;
13275f6d617Schristos   return result;
13375f6d617Schristos }
13475f6d617Schristos 
13575f6d617Schristos 
13675f6d617Schristos /* Return an option value suitable for add_exclude.  */
13775f6d617Schristos 
13875f6d617Schristos static int
exclude_options(void)13975f6d617Schristos exclude_options (void)
14075f6d617Schristos {
14175f6d617Schristos   return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
14275f6d617Schristos }
14375f6d617Schristos 
14475f6d617Schristos static char const shortopts[] =
14575f6d617Schristos "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y";
14675f6d617Schristos 
14775f6d617Schristos /* Values for long options that do not have single-letter equivalents.  */
14875f6d617Schristos enum
14975f6d617Schristos {
15075f6d617Schristos   BINARY_OPTION = CHAR_MAX + 1,
15175f6d617Schristos   FROM_FILE_OPTION,
15275f6d617Schristos   HELP_OPTION,
15375f6d617Schristos   HORIZON_LINES_OPTION,
15475f6d617Schristos   IGNORE_FILE_NAME_CASE_OPTION,
15575f6d617Schristos   INHIBIT_HUNK_MERGE_OPTION,
15675f6d617Schristos   LEFT_COLUMN_OPTION,
15775f6d617Schristos   LINE_FORMAT_OPTION,
15875f6d617Schristos   NO_IGNORE_FILE_NAME_CASE_OPTION,
15975f6d617Schristos   NORMAL_OPTION,
16075f6d617Schristos   SDIFF_MERGE_ASSIST_OPTION,
16175f6d617Schristos   STRIP_TRAILING_CR_OPTION,
16275f6d617Schristos   SUPPRESS_COMMON_LINES_OPTION,
16375f6d617Schristos   TO_FILE_OPTION,
16475f6d617Schristos 
16575f6d617Schristos   /* These options must be in sequence.  */
16675f6d617Schristos   UNCHANGED_LINE_FORMAT_OPTION,
16775f6d617Schristos   OLD_LINE_FORMAT_OPTION,
16875f6d617Schristos   NEW_LINE_FORMAT_OPTION,
16975f6d617Schristos 
17075f6d617Schristos   /* These options must be in sequence.  */
17175f6d617Schristos   UNCHANGED_GROUP_FORMAT_OPTION,
17275f6d617Schristos   OLD_GROUP_FORMAT_OPTION,
17375f6d617Schristos   NEW_GROUP_FORMAT_OPTION,
17475f6d617Schristos   CHANGED_GROUP_FORMAT_OPTION
17575f6d617Schristos };
17675f6d617Schristos 
17775f6d617Schristos static char const group_format_option[][sizeof "--unchanged-group-format"] =
17875f6d617Schristos   {
17975f6d617Schristos     "--unchanged-group-format",
18075f6d617Schristos     "--old-group-format",
18175f6d617Schristos     "--new-group-format",
18275f6d617Schristos     "--changed-group-format"
18375f6d617Schristos   };
18475f6d617Schristos 
18575f6d617Schristos static char const line_format_option[][sizeof "--unchanged-line-format"] =
18675f6d617Schristos   {
18775f6d617Schristos     "--unchanged-line-format",
18875f6d617Schristos     "--old-line-format",
18975f6d617Schristos     "--new-line-format"
19075f6d617Schristos   };
19175f6d617Schristos 
19275f6d617Schristos static struct option const longopts[] =
19375f6d617Schristos {
19475f6d617Schristos   {"binary", 0, 0, BINARY_OPTION},
19575f6d617Schristos   {"brief", 0, 0, 'q'},
19675f6d617Schristos   {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
19775f6d617Schristos   {"context", 2, 0, 'C'},
19875f6d617Schristos   {"ed", 0, 0, 'e'},
19975f6d617Schristos   {"exclude", 1, 0, 'x'},
20075f6d617Schristos   {"exclude-from", 1, 0, 'X'},
20175f6d617Schristos   {"expand-tabs", 0, 0, 't'},
20275f6d617Schristos   {"forward-ed", 0, 0, 'f'},
20375f6d617Schristos   {"from-file", 1, 0, FROM_FILE_OPTION},
20475f6d617Schristos   {"help", 0, 0, HELP_OPTION},
20575f6d617Schristos   {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
20675f6d617Schristos   {"ifdef", 1, 0, 'D'},
20775f6d617Schristos   {"ignore-all-space", 0, 0, 'w'},
20875f6d617Schristos   {"ignore-blank-lines", 0, 0, 'B'},
20975f6d617Schristos   {"ignore-case", 0, 0, 'i'},
21075f6d617Schristos   {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
21175f6d617Schristos   {"ignore-matching-lines", 1, 0, 'I'},
21275f6d617Schristos   {"ignore-space-change", 0, 0, 'b'},
21375f6d617Schristos   {"ignore-tab-expansion", 0, 0, 'E'},
21475f6d617Schristos   {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
21575f6d617Schristos   {"initial-tab", 0, 0, 'T'},
21675f6d617Schristos   {"label", 1, 0, 'L'},
21775f6d617Schristos   {"left-column", 0, 0, LEFT_COLUMN_OPTION},
21875f6d617Schristos   {"line-format", 1, 0, LINE_FORMAT_OPTION},
21975f6d617Schristos   {"minimal", 0, 0, 'd'},
22075f6d617Schristos   {"new-file", 0, 0, 'N'},
22175f6d617Schristos   {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
22275f6d617Schristos   {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
22375f6d617Schristos   {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
22475f6d617Schristos   {"normal", 0, 0, NORMAL_OPTION},
22575f6d617Schristos   {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
22675f6d617Schristos   {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
22775f6d617Schristos   {"paginate", 0, 0, 'l'},
22875f6d617Schristos   {"rcs", 0, 0, 'n'},
22975f6d617Schristos   {"recursive", 0, 0, 'r'},
23075f6d617Schristos   {"report-identical-files", 0, 0, 's'},
23175f6d617Schristos   {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
23275f6d617Schristos   {"show-c-function", 0, 0, 'p'},
23375f6d617Schristos   {"show-function-line", 1, 0, 'F'},
23475f6d617Schristos   {"side-by-side", 0, 0, 'y'},
23575f6d617Schristos   {"speed-large-files", 0, 0, 'H'},
23675f6d617Schristos   {"starting-file", 1, 0, 'S'},
23775f6d617Schristos   {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
23875f6d617Schristos   {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
23975f6d617Schristos   {"text", 0, 0, 'a'},
24075f6d617Schristos   {"to-file", 1, 0, TO_FILE_OPTION},
24175f6d617Schristos   {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
24275f6d617Schristos   {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
24375f6d617Schristos   {"unidirectional-new-file", 0, 0, 'P'},
24475f6d617Schristos   {"unified", 2, 0, 'U'},
24575f6d617Schristos   {"version", 0, 0, 'v'},
24675f6d617Schristos   {"width", 1, 0, 'W'},
24775f6d617Schristos   {0, 0, 0, 0}
24875f6d617Schristos };
24975f6d617Schristos 
25075f6d617Schristos int
main(int argc,char ** argv)25175f6d617Schristos main (int argc, char **argv)
25275f6d617Schristos {
25375f6d617Schristos   int exit_status = EXIT_SUCCESS;
25475f6d617Schristos   int c;
25575f6d617Schristos   int i;
25675f6d617Schristos   int prev = -1;
25775f6d617Schristos   lin ocontext = -1;
25875f6d617Schristos   bool explicit_context = 0;
25975f6d617Schristos   int width = 0;
26075f6d617Schristos   bool show_c_function = 0;
26175f6d617Schristos   char const *from_file = 0;
26275f6d617Schristos   char const *to_file = 0;
26375f6d617Schristos   uintmax_t numval;
26475f6d617Schristos   char *numend;
26575f6d617Schristos 
26675f6d617Schristos   /* Do our initializations.  */
26775f6d617Schristos   exit_failure = 2;
26875f6d617Schristos   initialize_main (&argc, &argv);
26975f6d617Schristos   program_name = argv[0];
27075f6d617Schristos   setlocale (LC_ALL, "");
27175f6d617Schristos   bindtextdomain (PACKAGE, LOCALEDIR);
27275f6d617Schristos   textdomain (PACKAGE);
27375f6d617Schristos   c_stack_action (c_stack_die);
27475f6d617Schristos   function_regexp_list.buf = &function_regexp;
27575f6d617Schristos   ignore_regexp_list.buf = &ignore_regexp;
27675f6d617Schristos   re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
27775f6d617Schristos   excluded = new_exclude ();
27875f6d617Schristos 
27975f6d617Schristos   /* Decode the options.  */
28075f6d617Schristos 
28175f6d617Schristos   while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1)
28275f6d617Schristos     {
28375f6d617Schristos       switch (c)
28475f6d617Schristos 	{
28575f6d617Schristos 	case 0:
28675f6d617Schristos 	  break;
28775f6d617Schristos 
28875f6d617Schristos 	case '0':
28975f6d617Schristos 	case '1':
29075f6d617Schristos 	case '2':
29175f6d617Schristos 	case '3':
29275f6d617Schristos 	case '4':
29375f6d617Schristos 	case '5':
29475f6d617Schristos 	case '6':
29575f6d617Schristos 	case '7':
29675f6d617Schristos 	case '8':
29775f6d617Schristos 	case '9':
29875f6d617Schristos 	  if (! ISDIGIT (prev))
29975f6d617Schristos 	    ocontext = c - '0';
30075f6d617Schristos 	  else if (LIN_MAX / 10 < ocontext
30175f6d617Schristos 		   || ((ocontext = 10 * ocontext + c - '0') < 0))
30275f6d617Schristos 	    ocontext = LIN_MAX;
30375f6d617Schristos 	  break;
30475f6d617Schristos 
30575f6d617Schristos 	case 'a':
30675f6d617Schristos 	  text = 1;
30775f6d617Schristos 	  break;
30875f6d617Schristos 
30975f6d617Schristos 	case 'b':
31075f6d617Schristos 	  if (ignore_white_space < IGNORE_SPACE_CHANGE)
31175f6d617Schristos 	    ignore_white_space = IGNORE_SPACE_CHANGE;
31275f6d617Schristos 	  break;
31375f6d617Schristos 
31475f6d617Schristos 	case 'B':
31575f6d617Schristos 	  ignore_blank_lines = 1;
31675f6d617Schristos 	  break;
31775f6d617Schristos 
31875f6d617Schristos 	case 'C':		/* +context[=lines] */
31975f6d617Schristos 	case 'U':		/* +unified[=lines] */
32075f6d617Schristos 	  {
32175f6d617Schristos 	    if (optarg)
32275f6d617Schristos 	      {
32375f6d617Schristos 		numval = strtoumax (optarg, &numend, 10);
32475f6d617Schristos 		if (*numend)
32575f6d617Schristos 		  try_help ("invalid context length `%s'", optarg);
32675f6d617Schristos 		if (LIN_MAX < numval)
32775f6d617Schristos 		  numval = LIN_MAX;
32875f6d617Schristos 	      }
32975f6d617Schristos 	    else
33075f6d617Schristos 	      numval = 3;
33175f6d617Schristos 
33275f6d617Schristos 	    specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
33375f6d617Schristos 	    if (context < numval)
33475f6d617Schristos 	      context = numval;
33575f6d617Schristos 	    explicit_context = 1;
33675f6d617Schristos 	  }
33775f6d617Schristos 	  break;
33875f6d617Schristos 
33975f6d617Schristos 	case 'c':
34075f6d617Schristos 	  specify_style (OUTPUT_CONTEXT);
34175f6d617Schristos 	  if (context < 3)
34275f6d617Schristos 	    context = 3;
34375f6d617Schristos 	  break;
34475f6d617Schristos 
34575f6d617Schristos 	case 'd':
34675f6d617Schristos 	  minimal = 1;
34775f6d617Schristos 	  break;
34875f6d617Schristos 
34975f6d617Schristos 	case 'D':
35075f6d617Schristos 	  specify_style (OUTPUT_IFDEF);
35175f6d617Schristos 	  {
35275f6d617Schristos 	    static char const C_ifdef_group_formats[] =
35375f6d617Schristos 	      "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
35475f6d617Schristos 	    char *b = xmalloc (sizeof C_ifdef_group_formats
35575f6d617Schristos 			       + 7 * strlen (optarg) - 14 /* 7*"%s" */
35675f6d617Schristos 			       - 8 /* 5*"%%" + 3*"%c" */);
35775f6d617Schristos 	    sprintf (b, C_ifdef_group_formats,
35875f6d617Schristos 		     0,
35975f6d617Schristos 		     optarg, optarg, 0,
36075f6d617Schristos 		     optarg, optarg, 0,
36175f6d617Schristos 		     optarg, optarg, optarg);
36275f6d617Schristos 	    for (i = 0; i < sizeof group_format / sizeof *group_format; i++)
36375f6d617Schristos 	      {
36475f6d617Schristos 		specify_value (&group_format[i], b, "-D");
36575f6d617Schristos 		b += strlen (b) + 1;
36675f6d617Schristos 	      }
36775f6d617Schristos 	  }
36875f6d617Schristos 	  break;
36975f6d617Schristos 
37075f6d617Schristos 	case 'e':
37175f6d617Schristos 	  specify_style (OUTPUT_ED);
37275f6d617Schristos 	  break;
37375f6d617Schristos 
37475f6d617Schristos 	case 'E':
37575f6d617Schristos 	  if (ignore_white_space < IGNORE_TAB_EXPANSION)
37675f6d617Schristos 	    ignore_white_space = IGNORE_TAB_EXPANSION;
37775f6d617Schristos 	  break;
37875f6d617Schristos 
37975f6d617Schristos 	case 'f':
38075f6d617Schristos 	  specify_style (OUTPUT_FORWARD_ED);
38175f6d617Schristos 	  break;
38275f6d617Schristos 
38375f6d617Schristos 	case 'F':
38475f6d617Schristos 	  add_regexp (&function_regexp_list, optarg);
38575f6d617Schristos 	  break;
38675f6d617Schristos 
38775f6d617Schristos 	case 'h':
38875f6d617Schristos 	  /* Split the files into chunks for faster processing.
38975f6d617Schristos 	     Usually does not change the result.
39075f6d617Schristos 
39175f6d617Schristos 	     This currently has no effect.  */
39275f6d617Schristos 	  break;
39375f6d617Schristos 
39475f6d617Schristos 	case 'H':
39575f6d617Schristos 	  speed_large_files = 1;
39675f6d617Schristos 	  break;
39775f6d617Schristos 
39875f6d617Schristos 	case 'i':
39975f6d617Schristos 	  ignore_case = 1;
40075f6d617Schristos 	  break;
40175f6d617Schristos 
40275f6d617Schristos 	case 'I':
40375f6d617Schristos 	  add_regexp (&ignore_regexp_list, optarg);
40475f6d617Schristos 	  break;
40575f6d617Schristos 
40675f6d617Schristos 	case 'l':
40775f6d617Schristos 	  if (!pr_program[0])
40875f6d617Schristos 	    try_help ("pagination not supported on this host", 0);
40975f6d617Schristos 	  paginate = 1;
41075f6d617Schristos #ifdef SIGCHLD
41175f6d617Schristos 	  /* Pagination requires forking and waiting, and
41275f6d617Schristos 	     System V fork+wait does not work if SIGCHLD is ignored.  */
41375f6d617Schristos 	  signal (SIGCHLD, SIG_DFL);
41475f6d617Schristos #endif
41575f6d617Schristos 	  break;
41675f6d617Schristos 
41775f6d617Schristos 	case 'L':
41875f6d617Schristos 	  if (!file_label[0])
41975f6d617Schristos 	    file_label[0] = optarg;
42075f6d617Schristos 	  else if (!file_label[1])
42175f6d617Schristos 	    file_label[1] = optarg;
42275f6d617Schristos 	  else
42375f6d617Schristos 	    fatal ("too many file label options");
42475f6d617Schristos 	  break;
42575f6d617Schristos 
42675f6d617Schristos 	case 'n':
42775f6d617Schristos 	  specify_style (OUTPUT_RCS);
42875f6d617Schristos 	  break;
42975f6d617Schristos 
43075f6d617Schristos 	case 'N':
43175f6d617Schristos 	  new_file = 1;
43275f6d617Schristos 	  break;
43375f6d617Schristos 
43475f6d617Schristos 	case 'p':
43575f6d617Schristos 	  show_c_function = 1;
43675f6d617Schristos 	  add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
43775f6d617Schristos 	  break;
43875f6d617Schristos 
43975f6d617Schristos 	case 'P':
44075f6d617Schristos 	  unidirectional_new_file = 1;
44175f6d617Schristos 	  break;
44275f6d617Schristos 
44375f6d617Schristos 	case 'q':
44475f6d617Schristos 	  brief = 1;
44575f6d617Schristos 	  break;
44675f6d617Schristos 
44775f6d617Schristos 	case 'r':
44875f6d617Schristos 	  recursive = 1;
44975f6d617Schristos 	  break;
45075f6d617Schristos 
45175f6d617Schristos 	case 's':
45275f6d617Schristos 	  report_identical_files = 1;
45375f6d617Schristos 	  break;
45475f6d617Schristos 
45575f6d617Schristos 	case 'S':
45675f6d617Schristos 	  specify_value (&starting_file, optarg, "-S");
45775f6d617Schristos 	  break;
45875f6d617Schristos 
45975f6d617Schristos 	case 't':
46075f6d617Schristos 	  expand_tabs = 1;
46175f6d617Schristos 	  break;
46275f6d617Schristos 
46375f6d617Schristos 	case 'T':
46475f6d617Schristos 	  initial_tab = 1;
46575f6d617Schristos 	  break;
46675f6d617Schristos 
46775f6d617Schristos 	case 'u':
46875f6d617Schristos 	  specify_style (OUTPUT_UNIFIED);
46975f6d617Schristos 	  if (context < 3)
47075f6d617Schristos 	    context = 3;
47175f6d617Schristos 	  break;
47275f6d617Schristos 
47375f6d617Schristos 	case 'v':
47475f6d617Schristos 	  printf ("diff %s\n%s\n\n%s\n\n%s\n",
47575f6d617Schristos 		  version_string, copyright_string,
47675f6d617Schristos 		  _(free_software_msgid), _(authorship_msgid));
47775f6d617Schristos 	  check_stdout ();
47875f6d617Schristos 	  return EXIT_SUCCESS;
47975f6d617Schristos 
48075f6d617Schristos 	case 'w':
48175f6d617Schristos 	  ignore_white_space = IGNORE_ALL_SPACE;
48275f6d617Schristos 	  break;
48375f6d617Schristos 
48475f6d617Schristos 	case 'x':
48575f6d617Schristos 	  add_exclude (excluded, optarg, exclude_options ());
48675f6d617Schristos 	  break;
48775f6d617Schristos 
48875f6d617Schristos 	case 'X':
48975f6d617Schristos 	  if (add_exclude_file (add_exclude, excluded, optarg,
49075f6d617Schristos 				exclude_options (), '\n'))
49175f6d617Schristos 	    pfatal_with_name (optarg);
49275f6d617Schristos 	  break;
49375f6d617Schristos 
49475f6d617Schristos 	case 'y':
49575f6d617Schristos 	  specify_style (OUTPUT_SDIFF);
49675f6d617Schristos 	  break;
49775f6d617Schristos 
49875f6d617Schristos 	case 'W':
49975f6d617Schristos 	  numval = strtoumax (optarg, &numend, 10);
50075f6d617Schristos 	  if (! (0 < numval && numval <= INT_MAX) || *numend)
50175f6d617Schristos 	    try_help ("invalid width `%s'", optarg);
50275f6d617Schristos 	  if (width != numval)
50375f6d617Schristos 	    {
50475f6d617Schristos 	      if (width)
50575f6d617Schristos 		fatal ("conflicting width options");
50675f6d617Schristos 	      width = numval;
50775f6d617Schristos 	    }
50875f6d617Schristos 	  break;
50975f6d617Schristos 
51075f6d617Schristos 	case BINARY_OPTION:
51175f6d617Schristos #if HAVE_SETMODE_DOS
51275f6d617Schristos 	  binary = 1;
51375f6d617Schristos 	  set_binary_mode (STDOUT_FILENO, 1);
51475f6d617Schristos #endif
51575f6d617Schristos 	  break;
51675f6d617Schristos 
51775f6d617Schristos 	case FROM_FILE_OPTION:
51875f6d617Schristos 	  specify_value (&from_file, optarg, "--from-file");
51975f6d617Schristos 	  break;
52075f6d617Schristos 
52175f6d617Schristos 	case HELP_OPTION:
52275f6d617Schristos 	  usage ();
52375f6d617Schristos 	  check_stdout ();
52475f6d617Schristos 	  return EXIT_SUCCESS;
52575f6d617Schristos 
52675f6d617Schristos 	case HORIZON_LINES_OPTION:
52775f6d617Schristos 	  numval = strtoumax (optarg, &numend, 10);
52875f6d617Schristos 	  if (*numend)
52975f6d617Schristos 	    try_help ("invalid horizon length `%s'", optarg);
53075f6d617Schristos 	  horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
53175f6d617Schristos 	  break;
53275f6d617Schristos 
53375f6d617Schristos 	case IGNORE_FILE_NAME_CASE_OPTION:
53475f6d617Schristos 	  ignore_file_name_case = 1;
53575f6d617Schristos 	  break;
53675f6d617Schristos 
53775f6d617Schristos 	case INHIBIT_HUNK_MERGE_OPTION:
53875f6d617Schristos 	  /* This option is obsolete, but accept it for backward
53975f6d617Schristos              compatibility.  */
54075f6d617Schristos 	  break;
54175f6d617Schristos 
54275f6d617Schristos 	case LEFT_COLUMN_OPTION:
54375f6d617Schristos 	  left_column = 1;
54475f6d617Schristos 	  break;
54575f6d617Schristos 
54675f6d617Schristos 	case LINE_FORMAT_OPTION:
54775f6d617Schristos 	  specify_style (OUTPUT_IFDEF);
54875f6d617Schristos 	  for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
54975f6d617Schristos 	    specify_value (&line_format[i], optarg, "--line-format");
55075f6d617Schristos 	  break;
55175f6d617Schristos 
55275f6d617Schristos 	case NO_IGNORE_FILE_NAME_CASE_OPTION:
55375f6d617Schristos 	  ignore_file_name_case = 0;
55475f6d617Schristos 	  break;
55575f6d617Schristos 
55675f6d617Schristos 	case NORMAL_OPTION:
55775f6d617Schristos 	  specify_style (OUTPUT_NORMAL);
55875f6d617Schristos 	  break;
55975f6d617Schristos 
56075f6d617Schristos 	case SDIFF_MERGE_ASSIST_OPTION:
56175f6d617Schristos 	  specify_style (OUTPUT_SDIFF);
56275f6d617Schristos 	  sdiff_merge_assist = 1;
56375f6d617Schristos 	  break;
56475f6d617Schristos 
56575f6d617Schristos 	case STRIP_TRAILING_CR_OPTION:
56675f6d617Schristos 	  strip_trailing_cr = 1;
56775f6d617Schristos 	  break;
56875f6d617Schristos 
56975f6d617Schristos 	case SUPPRESS_COMMON_LINES_OPTION:
57075f6d617Schristos 	  suppress_common_lines = 1;
57175f6d617Schristos 	  break;
57275f6d617Schristos 
57375f6d617Schristos 	case TO_FILE_OPTION:
57475f6d617Schristos 	  specify_value (&to_file, optarg, "--to-file");
57575f6d617Schristos 	  break;
57675f6d617Schristos 
57775f6d617Schristos 	case UNCHANGED_LINE_FORMAT_OPTION:
57875f6d617Schristos 	case OLD_LINE_FORMAT_OPTION:
57975f6d617Schristos 	case NEW_LINE_FORMAT_OPTION:
58075f6d617Schristos 	  specify_style (OUTPUT_IFDEF);
58175f6d617Schristos 	  c -= UNCHANGED_LINE_FORMAT_OPTION;
58275f6d617Schristos 	  specify_value (&line_format[c], optarg, line_format_option[c]);
58375f6d617Schristos 	  break;
58475f6d617Schristos 
58575f6d617Schristos 	case UNCHANGED_GROUP_FORMAT_OPTION:
58675f6d617Schristos 	case OLD_GROUP_FORMAT_OPTION:
58775f6d617Schristos 	case NEW_GROUP_FORMAT_OPTION:
58875f6d617Schristos 	case CHANGED_GROUP_FORMAT_OPTION:
58975f6d617Schristos 	  specify_style (OUTPUT_IFDEF);
59075f6d617Schristos 	  c -= UNCHANGED_GROUP_FORMAT_OPTION;
59175f6d617Schristos 	  specify_value (&group_format[c], optarg, group_format_option[c]);
59275f6d617Schristos 	  break;
59375f6d617Schristos 
59475f6d617Schristos 	default:
59575f6d617Schristos 	  try_help (0, 0);
59675f6d617Schristos 	}
59775f6d617Schristos       prev = c;
59875f6d617Schristos     }
59975f6d617Schristos 
60075f6d617Schristos   if (output_style == OUTPUT_UNSPECIFIED)
60175f6d617Schristos     {
60275f6d617Schristos       if (show_c_function)
60375f6d617Schristos 	{
60475f6d617Schristos 	  specify_style (OUTPUT_CONTEXT);
60575f6d617Schristos 	  if (ocontext < 0)
60675f6d617Schristos 	    context = 3;
60775f6d617Schristos 	}
60875f6d617Schristos       else
60975f6d617Schristos 	specify_style (OUTPUT_NORMAL);
61075f6d617Schristos     }
61175f6d617Schristos 
61275f6d617Schristos   if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
61375f6d617Schristos     time_format = "%Y-%m-%d %H:%M:%S.%N %z";
61475f6d617Schristos   else
61575f6d617Schristos     {
61675f6d617Schristos       /* See POSIX 1003.1-2001 for this format.  */
61775f6d617Schristos       time_format = "%a %b %e %T %Y";
61875f6d617Schristos     }
61975f6d617Schristos 
62075f6d617Schristos   if (0 <= ocontext)
62175f6d617Schristos     {
62275f6d617Schristos       bool modern_usage = 200112 <= posix2_version ();
62375f6d617Schristos 
62475f6d617Schristos       if ((output_style == OUTPUT_CONTEXT
62575f6d617Schristos 	   || output_style == OUTPUT_UNIFIED)
62675f6d617Schristos 	  && (context < ocontext
62775f6d617Schristos 	      || (ocontext < context && ! explicit_context)))
62875f6d617Schristos 	{
62975f6d617Schristos 	  if (modern_usage)
63075f6d617Schristos 	    {
63175f6d617Schristos 	      error (0, 0,
63275f6d617Schristos 		     _("`-%ld' option is obsolete; use `-%c %ld'"),
63375f6d617Schristos 		     (long) ocontext,
63475f6d617Schristos 		     output_style == OUTPUT_CONTEXT ? 'C' : 'U',
63575f6d617Schristos 		     (long) ocontext);
63675f6d617Schristos 	      try_help (0, 0);
63775f6d617Schristos 	    }
63875f6d617Schristos 	  context = ocontext;
63975f6d617Schristos 	}
64075f6d617Schristos       else
64175f6d617Schristos 	{
64275f6d617Schristos 	  if (modern_usage)
64375f6d617Schristos 	    {
64475f6d617Schristos 	      error (0, 0, _("`-%ld' option is obsolete; omit it"),
64575f6d617Schristos 		     (long) ocontext);
64675f6d617Schristos 	      try_help (0, 0);
64775f6d617Schristos 	    }
64875f6d617Schristos 	}
64975f6d617Schristos     }
65075f6d617Schristos 
65175f6d617Schristos   {
65275f6d617Schristos     /*
65375f6d617Schristos      *	We maximize first the half line width, and then the gutter width,
65475f6d617Schristos      *	according to the following constraints:
65575f6d617Schristos      *	1.  Two half lines plus a gutter must fit in a line.
65675f6d617Schristos      *	2.  If the half line width is nonzero:
65775f6d617Schristos      *	    a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
65875f6d617Schristos      *	    b.  If tabs are not expanded to spaces,
65975f6d617Schristos      *		a half line plus a gutter is an integral number of tabs,
66075f6d617Schristos      *		so that tabs in the right column line up.
66175f6d617Schristos      */
66275f6d617Schristos     unsigned int t = expand_tabs ? 1 : TAB_WIDTH;
66375f6d617Schristos     int w = width ? width : 130;
66475f6d617Schristos     int off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t)  *  t;
66575f6d617Schristos     sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)),
66675f6d617Schristos     sdiff_column2_offset = sdiff_half_width ? off : w;
66775f6d617Schristos   }
66875f6d617Schristos 
66975f6d617Schristos   /* Make the horizon at least as large as the context, so that
67075f6d617Schristos      shift_boundaries has more freedom to shift the first and last hunks.  */
67175f6d617Schristos   if (horizon_lines < context)
67275f6d617Schristos     horizon_lines = context;
67375f6d617Schristos 
67475f6d617Schristos   summarize_regexp_list (&function_regexp_list);
67575f6d617Schristos   summarize_regexp_list (&ignore_regexp_list);
67675f6d617Schristos 
67775f6d617Schristos   if (output_style == OUTPUT_IFDEF)
67875f6d617Schristos     {
67975f6d617Schristos       for (i = 0; i < sizeof line_format / sizeof *line_format; i++)
68075f6d617Schristos 	if (!line_format[i])
68175f6d617Schristos 	  line_format[i] = "%l\n";
68275f6d617Schristos       if (!group_format[OLD])
68375f6d617Schristos 	group_format[OLD]
68475f6d617Schristos 	  = group_format[CHANGED] ? group_format[CHANGED] : "%<";
68575f6d617Schristos       if (!group_format[NEW])
68675f6d617Schristos 	group_format[NEW]
68775f6d617Schristos 	  = group_format[CHANGED] ? group_format[CHANGED] : "%>";
68875f6d617Schristos       if (!group_format[UNCHANGED])
68975f6d617Schristos 	group_format[UNCHANGED] = "%=";
69075f6d617Schristos       if (!group_format[CHANGED])
69175f6d617Schristos 	group_format[CHANGED] = concat (group_format[OLD],
69275f6d617Schristos 					group_format[NEW], "");
69375f6d617Schristos     }
69475f6d617Schristos 
69575f6d617Schristos   no_diff_means_no_output =
69675f6d617Schristos     (output_style == OUTPUT_IFDEF ?
69775f6d617Schristos       (!*group_format[UNCHANGED]
69875f6d617Schristos        || (strcmp (group_format[UNCHANGED], "%=") == 0
69975f6d617Schristos 	   && !*line_format[UNCHANGED]))
70075f6d617Schristos      : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
70175f6d617Schristos 
70275f6d617Schristos   files_can_be_treated_as_binary =
70375f6d617Schristos     (brief
70475f6d617Schristos      & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
70575f6d617Schristos 	  | (ignore_regexp_list.regexps || ignore_white_space)));
70675f6d617Schristos 
70775f6d617Schristos   switch_string = option_list (argv + 1, optind - 1);
70875f6d617Schristos 
70975f6d617Schristos   if (from_file)
71075f6d617Schristos     {
71175f6d617Schristos       if (to_file)
71275f6d617Schristos 	fatal ("--from-file and --to-file both specified");
71375f6d617Schristos       else
71475f6d617Schristos 	for (; optind < argc; optind++)
71575f6d617Schristos 	  {
71675f6d617Schristos 	    int status = compare_files ((struct comparison *) 0,
71775f6d617Schristos 					from_file, argv[optind]);
71875f6d617Schristos 	    if (exit_status < status)
71975f6d617Schristos 	      exit_status = status;
72075f6d617Schristos 	  }
72175f6d617Schristos     }
72275f6d617Schristos   else
72375f6d617Schristos     {
72475f6d617Schristos       if (to_file)
72575f6d617Schristos 	for (; optind < argc; optind++)
72675f6d617Schristos 	  {
72775f6d617Schristos 	    int status = compare_files ((struct comparison *) 0,
72875f6d617Schristos 					argv[optind], to_file);
72975f6d617Schristos 	    if (exit_status < status)
73075f6d617Schristos 	      exit_status = status;
73175f6d617Schristos 	  }
73275f6d617Schristos       else
73375f6d617Schristos 	{
73475f6d617Schristos 	  if (argc - optind != 2)
73575f6d617Schristos 	    {
73675f6d617Schristos 	      if (argc - optind < 2)
73775f6d617Schristos 		try_help ("missing operand after `%s'", argv[argc - 1]);
73875f6d617Schristos 	      else
73975f6d617Schristos 		try_help ("extra operand `%s'", argv[optind + 2]);
74075f6d617Schristos 	    }
74175f6d617Schristos 
74275f6d617Schristos 	  exit_status = compare_files ((struct comparison *) 0,
74375f6d617Schristos 				       argv[optind], argv[optind + 1]);
74475f6d617Schristos 	}
74575f6d617Schristos     }
74675f6d617Schristos 
74775f6d617Schristos   /* Print any messages that were saved up for last.  */
74875f6d617Schristos   print_message_queue ();
74975f6d617Schristos 
75075f6d617Schristos   check_stdout ();
75175f6d617Schristos   exit (exit_status);
75275f6d617Schristos   return exit_status;
75375f6d617Schristos }
75475f6d617Schristos 
75575f6d617Schristos /* Append to REGLIST the regexp PATTERN.  */
75675f6d617Schristos 
75775f6d617Schristos static void
add_regexp(struct regexp_list * reglist,char const * pattern)75875f6d617Schristos add_regexp (struct regexp_list *reglist, char const *pattern)
75975f6d617Schristos {
76075f6d617Schristos   size_t patlen = strlen (pattern);
76175f6d617Schristos   char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
76275f6d617Schristos 
76375f6d617Schristos   if (m != 0)
76475f6d617Schristos     error (0, 0, "%s: %s", pattern, m);
76575f6d617Schristos   else
76675f6d617Schristos     {
76775f6d617Schristos       char *regexps = reglist->regexps;
76875f6d617Schristos       size_t len = reglist->len;
76975f6d617Schristos       bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
77075f6d617Schristos       size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
77175f6d617Schristos       size_t size = reglist->size;
77275f6d617Schristos 
77375f6d617Schristos       if (size <= newlen)
77475f6d617Schristos 	{
77575f6d617Schristos 	  if (!size)
77675f6d617Schristos 	    size = 1;
77775f6d617Schristos 
77875f6d617Schristos 	  do size *= 2;
77975f6d617Schristos 	  while (size <= newlen);
78075f6d617Schristos 
78175f6d617Schristos 	  reglist->size = size;
78275f6d617Schristos 	  reglist->regexps = regexps = xrealloc (regexps, size);
78375f6d617Schristos 	}
78475f6d617Schristos       if (multiple_regexps)
78575f6d617Schristos 	{
78675f6d617Schristos 	  regexps[len++] = '\\';
78775f6d617Schristos 	  regexps[len++] = '|';
78875f6d617Schristos 	}
78975f6d617Schristos       memcpy (regexps + len, pattern, patlen + 1);
79075f6d617Schristos     }
79175f6d617Schristos }
79275f6d617Schristos 
79375f6d617Schristos /* Ensure that REGLIST represents the disjunction of its regexps.
79475f6d617Schristos    This is done here, rather than earlier, to avoid O(N^2) behavior.  */
79575f6d617Schristos 
79675f6d617Schristos static void
summarize_regexp_list(struct regexp_list * reglist)79775f6d617Schristos summarize_regexp_list (struct regexp_list *reglist)
79875f6d617Schristos {
79975f6d617Schristos   if (reglist->regexps)
80075f6d617Schristos     {
80175f6d617Schristos       /* At least one regexp was specified.  Allocate a fastmap for it.  */
80275f6d617Schristos       reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
80375f6d617Schristos       if (reglist->multiple_regexps)
80475f6d617Schristos 	{
80575f6d617Schristos 	  /* Compile the disjunction of the regexps.
80675f6d617Schristos 	     (If just one regexp was specified, it is already compiled.)  */
80775f6d617Schristos 	  char const *m = re_compile_pattern (reglist->regexps, reglist->len,
80875f6d617Schristos 					      reglist->buf);
80975f6d617Schristos 	  if (m != 0)
81075f6d617Schristos 	    error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
81175f6d617Schristos 	}
81275f6d617Schristos     }
81375f6d617Schristos }
81475f6d617Schristos 
81575f6d617Schristos static void
try_help(char const * reason_msgid,char const * operand)81675f6d617Schristos try_help (char const *reason_msgid, char const *operand)
81775f6d617Schristos {
81875f6d617Schristos   if (reason_msgid)
81975f6d617Schristos     error (0, 0, _(reason_msgid), operand);
82075f6d617Schristos   error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
82175f6d617Schristos 	 program_name);
82275f6d617Schristos   abort ();
82375f6d617Schristos }
82475f6d617Schristos 
82575f6d617Schristos static void
check_stdout(void)82675f6d617Schristos check_stdout (void)
82775f6d617Schristos {
82875f6d617Schristos   if (ferror (stdout))
82975f6d617Schristos     fatal ("write failed");
83075f6d617Schristos   else if (fclose (stdout) != 0)
83175f6d617Schristos     pfatal_with_name (_("standard output"));
83275f6d617Schristos }
83375f6d617Schristos 
83475f6d617Schristos static char const * const option_help_msgid[] = {
83575f6d617Schristos   N_("Compare files line by line."),
83675f6d617Schristos   "",
83775f6d617Schristos   N_("-i  --ignore-case  Ignore case differences in file contents."),
83875f6d617Schristos   N_("--ignore-file-name-case  Ignore case when comparing file names."),
83975f6d617Schristos   N_("--no-ignore-file-name-case  Consider case when comparing file names."),
84075f6d617Schristos   N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
84175f6d617Schristos   N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
84275f6d617Schristos   N_("-w  --ignore-all-space  Ignore all white space."),
84375f6d617Schristos   N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
84475f6d617Schristos   N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
84575f6d617Schristos   N_("--strip-trailing-cr  Strip trailing carriage return on input."),
84675f6d617Schristos #if HAVE_SETMODE_DOS
84775f6d617Schristos   N_("--binary  Read and write data in binary mode."),
84875f6d617Schristos #endif
84975f6d617Schristos   N_("-a  --text  Treat all files as text."),
85075f6d617Schristos   "",
85175f6d617Schristos   N_("-c  -C NUM  --context[=NUM]  Output NUM (default 3) lines of copied context.\n\
85275f6d617Schristos -u  -U NUM  --unified[=NUM]  Output NUM (default 3) lines of unified context.\n\
85375f6d617Schristos   --label LABEL  Use LABEL instead of file name.\n\
85475f6d617Schristos   -p  --show-c-function  Show which C function each change is in.\n\
85575f6d617Schristos   -F RE  --show-function-line=RE  Show the most recent line matching RE."),
85675f6d617Schristos   N_("-q  --brief  Output only whether files differ."),
85775f6d617Schristos   N_("-e  --ed  Output an ed script."),
85875f6d617Schristos   N_("--normal  Output a normal diff."),
85975f6d617Schristos   N_("-n  --rcs  Output an RCS format diff."),
86075f6d617Schristos   N_("-y  --side-by-side  Output in two columns.\n\
86175f6d617Schristos   -W NUM  --width=NUM  Output at most NUM (default 130) print columns.\n\
86275f6d617Schristos   --left-column  Output only the left column of common lines.\n\
86375f6d617Schristos   --suppress-common-lines  Do not output common lines."),
86475f6d617Schristos   N_("-D NAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs."),
86575f6d617Schristos   N_("--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT."),
86675f6d617Schristos   N_("--line-format=LFMT  Similar, but format all input lines with LFMT."),
86775f6d617Schristos   N_("--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT."),
86875f6d617Schristos   N_("  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'."),
86975f6d617Schristos   N_("  GFMT may contain:\n\
87075f6d617Schristos     %<  lines from FILE1\n\
87175f6d617Schristos     %>  lines from FILE2\n\
87275f6d617Schristos     %=  lines common to FILE1 and FILE2\n\
87375f6d617Schristos     %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER\n\
87475f6d617Schristos       LETTERs are as follows for new group, lower case for old group:\n\
87575f6d617Schristos         F  first line number\n\
87675f6d617Schristos         L  last line number\n\
87775f6d617Schristos         N  number of lines = L-F+1\n\
87875f6d617Schristos         E  F-1\n\
87975f6d617Schristos         M  L+1"),
88075f6d617Schristos   N_("  LFMT may contain:\n\
88175f6d617Schristos     %L  contents of line\n\
88275f6d617Schristos     %l  contents of line, excluding any trailing newline\n\
88375f6d617Schristos     %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number"),
88475f6d617Schristos   N_("  Either GFMT or LFMT may contain:\n\
88575f6d617Schristos     %%  %\n\
88675f6d617Schristos     %c'C'  the single character C\n\
88775f6d617Schristos     %c'\\OOO'  the character with octal code OOO"),
88875f6d617Schristos   "",
88975f6d617Schristos   N_("-l  --paginate  Pass the output through `pr' to paginate it."),
89075f6d617Schristos   N_("-t  --expand-tabs  Expand tabs to spaces in output."),
89175f6d617Schristos   N_("-T  --initial-tab  Make tabs line up by prepending a tab."),
89275f6d617Schristos   "",
89375f6d617Schristos   N_("-r  --recursive  Recursively compare any subdirectories found."),
89475f6d617Schristos   N_("-N  --new-file  Treat absent files as empty."),
89575f6d617Schristos   N_("--unidirectional-new-file  Treat absent first files as empty."),
89675f6d617Schristos   N_("-s  --report-identical-files  Report when two files are the same."),
89775f6d617Schristos   N_("-x PAT  --exclude=PAT  Exclude files that match PAT."),
89875f6d617Schristos   N_("-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE."),
89975f6d617Schristos   N_("-S FILE  --starting-file=FILE  Start with FILE when comparing directories."),
90075f6d617Schristos   N_("--from-file=FILE1  Compare FILE1 to all operands.  FILE1 can be a directory."),
90175f6d617Schristos   N_("--to-file=FILE2  Compare all operands to FILE2.  FILE2 can be a directory."),
90275f6d617Schristos   "",
90375f6d617Schristos   N_("--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix."),
90475f6d617Schristos   N_("-d  --minimal  Try hard to find a smaller set of changes."),
90575f6d617Schristos   N_("--speed-large-files  Assume large files and many scattered small changes."),
90675f6d617Schristos   "",
90775f6d617Schristos   N_("-v  --version  Output version info."),
90875f6d617Schristos   N_("--help  Output this help."),
90975f6d617Schristos   "",
91075f6d617Schristos   N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."),
91175f6d617Schristos   N_("If --from-file or --to-file is given, there are no restrictions on FILES."),
91275f6d617Schristos   N_("If a FILE is `-', read standard input."),
91375f6d617Schristos   "",
91475f6d617Schristos   N_("Report bugs to <bug-gnu-utils@gnu.org>."),
91575f6d617Schristos   0
91675f6d617Schristos };
91775f6d617Schristos 
91875f6d617Schristos static void
usage(void)91975f6d617Schristos usage (void)
92075f6d617Schristos {
92175f6d617Schristos   char const * const *p;
92275f6d617Schristos 
92375f6d617Schristos   printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
92475f6d617Schristos 
92575f6d617Schristos   for (p = option_help_msgid;  *p;  p++)
92675f6d617Schristos     {
92775f6d617Schristos       if (!**p)
92875f6d617Schristos 	putchar ('\n');
92975f6d617Schristos       else
93075f6d617Schristos 	{
93175f6d617Schristos 	  char const *msg = _(*p);
93275f6d617Schristos 	  char const *nl;
93375f6d617Schristos 	  while ((nl = strchr (msg, '\n')))
93475f6d617Schristos 	    {
93575f6d617Schristos 	      int msglen = nl + 1 - msg;
93675f6d617Schristos 	      printf ("  %.*s", msglen, msg);
93775f6d617Schristos 	      msg = nl + 1;
93875f6d617Schristos 	    }
93975f6d617Schristos 
94075f6d617Schristos 	  printf ("  %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
94175f6d617Schristos 	}
94275f6d617Schristos     }
94375f6d617Schristos }
94475f6d617Schristos 
94575f6d617Schristos /* Set VAR to VALUE, reporting an OPTION error if this is a
94675f6d617Schristos    conflict.  */
94775f6d617Schristos static void
specify_value(char const ** var,char const * value,char const * option)94875f6d617Schristos specify_value (char const **var, char const *value, char const *option)
94975f6d617Schristos {
95075f6d617Schristos   if (*var && strcmp (*var, value) != 0)
95175f6d617Schristos     {
95275f6d617Schristos       error (0, 0, _("conflicting %s option value `%s'"), option, value);
95375f6d617Schristos       try_help (0, 0);
95475f6d617Schristos     }
95575f6d617Schristos   *var = value;
95675f6d617Schristos }
95775f6d617Schristos 
95875f6d617Schristos /* Set the output style to STYLE, diagnosing conflicts.  */
95975f6d617Schristos static void
specify_style(enum output_style style)96075f6d617Schristos specify_style (enum output_style style)
96175f6d617Schristos {
96275f6d617Schristos   if (output_style != style)
96375f6d617Schristos     {
96475f6d617Schristos       if (output_style != OUTPUT_UNSPECIFIED)
96575f6d617Schristos 	try_help ("conflicting output style options", 0);
96675f6d617Schristos       output_style = style;
96775f6d617Schristos     }
96875f6d617Schristos }
96975f6d617Schristos 
97075f6d617Schristos static char const *
filetype(struct stat const * st)97175f6d617Schristos filetype (struct stat const *st)
97275f6d617Schristos {
97375f6d617Schristos   /* See POSIX 1003.1-2001 for these formats.
97475f6d617Schristos 
97575f6d617Schristos      To keep diagnostics grammatical in English, the returned string
97675f6d617Schristos      must start with a consonant.  */
97775f6d617Schristos 
97875f6d617Schristos   if (S_ISREG (st->st_mode))
97975f6d617Schristos     return st->st_size == 0 ? _("regular empty file") : _("regular file");
98075f6d617Schristos 
98175f6d617Schristos   if (S_ISDIR (st->st_mode)) return _("directory");
98275f6d617Schristos 
98375f6d617Schristos #ifdef S_ISBLK
98475f6d617Schristos   if (S_ISBLK (st->st_mode)) return _("block special file");
98575f6d617Schristos #endif
98675f6d617Schristos #ifdef S_ISCHR
98775f6d617Schristos   if (S_ISCHR (st->st_mode)) return _("character special file");
98875f6d617Schristos #endif
98975f6d617Schristos #ifdef S_ISFIFO
99075f6d617Schristos   if (S_ISFIFO (st->st_mode)) return _("fifo");
99175f6d617Schristos #endif
99275f6d617Schristos   /* S_ISLNK is impossible with `fstat' and `stat'.  */
99375f6d617Schristos #ifdef S_ISSOCK
99475f6d617Schristos   if (S_ISSOCK (st->st_mode)) return _("socket");
99575f6d617Schristos #endif
99675f6d617Schristos #ifdef S_TYPEISMQ
99775f6d617Schristos   if (S_TYPEISMQ (st)) return _("message queue");
99875f6d617Schristos #endif
99975f6d617Schristos #ifdef S_TYPEISSEM
100075f6d617Schristos   if (S_TYPEISSEM (st)) return _("semaphore");
100175f6d617Schristos #endif
100275f6d617Schristos #ifdef S_TYPEISSHM
100375f6d617Schristos   if (S_TYPEISSHM (st)) return _("shared memory object");
100475f6d617Schristos #endif
100575f6d617Schristos #ifdef S_TYPEISTMO
100675f6d617Schristos   if (S_TYPEISTMO (st)) return _("typed memory object");
100775f6d617Schristos #endif
100875f6d617Schristos 
100975f6d617Schristos   return _("weird file");
101075f6d617Schristos }
101175f6d617Schristos 
101275f6d617Schristos /* Set the last-modified time of *ST to be the current time.  */
101375f6d617Schristos 
101475f6d617Schristos static void
set_mtime_to_now(struct stat * st)101575f6d617Schristos set_mtime_to_now (struct stat *st)
101675f6d617Schristos {
101775f6d617Schristos #ifdef ST_MTIM_NSEC
101875f6d617Schristos 
101975f6d617Schristos # if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
102075f6d617Schristos   if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0)
102175f6d617Schristos     return;
102275f6d617Schristos # endif
102375f6d617Schristos 
102475f6d617Schristos # if HAVE_GETTIMEOFDAY
102575f6d617Schristos   {
102675f6d617Schristos     struct timeval timeval;
102775f6d617Schristos     if (gettimeofday (&timeval, NULL) == 0)
102875f6d617Schristos       {
102975f6d617Schristos 	st->st_mtime = timeval.tv_sec;
103075f6d617Schristos 	st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000;
103175f6d617Schristos 	return;
103275f6d617Schristos       }
103375f6d617Schristos   }
103475f6d617Schristos # endif
103575f6d617Schristos 
103675f6d617Schristos #endif /* ST_MTIM_NSEC */
103775f6d617Schristos 
103875f6d617Schristos   time (&st->st_mtime);
103975f6d617Schristos }
104075f6d617Schristos 
104175f6d617Schristos /* Compare two files (or dirs) with parent comparison PARENT
104275f6d617Schristos    and names NAME0 and NAME1.
104375f6d617Schristos    (If PARENT is 0, then the first name is just NAME0, etc.)
104475f6d617Schristos    This is self-contained; it opens the files and closes them.
104575f6d617Schristos 
104675f6d617Schristos    Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
104775f6d617Schristos    different, EXIT_TROUBLE if there is a problem opening them.  */
104875f6d617Schristos 
104975f6d617Schristos static int
compare_files(struct comparison const * parent,char const * name0,char const * name1)105075f6d617Schristos compare_files (struct comparison const *parent,
105175f6d617Schristos 	       char const *name0,
105275f6d617Schristos 	       char const *name1)
105375f6d617Schristos {
105475f6d617Schristos   struct comparison cmp;
105575f6d617Schristos #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
105675f6d617Schristos   register int f;
105775f6d617Schristos   int status = EXIT_SUCCESS;
105875f6d617Schristos   bool same_files;
105975f6d617Schristos   char *free0, *free1;
106075f6d617Schristos 
106175f6d617Schristos   /* If this is directory comparison, perhaps we have a file
106275f6d617Schristos      that exists only in one of the directories.
106375f6d617Schristos      If so, just print a message to that effect.  */
106475f6d617Schristos 
106575f6d617Schristos   if (! ((name0 && name1)
106675f6d617Schristos 	 || (unidirectional_new_file && name1)
106775f6d617Schristos 	 || new_file))
106875f6d617Schristos     {
106975f6d617Schristos       char const *name = name0 == 0 ? name1 : name0;
107075f6d617Schristos       char const *dir = parent->file[name0 == 0].name;
107175f6d617Schristos 
107275f6d617Schristos       /* See POSIX 1003.1-2001 for this format.  */
107375f6d617Schristos       message ("Only in %s: %s\n", dir, name);
107475f6d617Schristos 
107575f6d617Schristos       /* Return EXIT_FAILURE so that diff_dirs will return
107675f6d617Schristos 	 EXIT_FAILURE ("some files differ").  */
107775f6d617Schristos       return EXIT_FAILURE;
107875f6d617Schristos     }
107975f6d617Schristos 
108075f6d617Schristos   memset (cmp.file, 0, sizeof cmp.file);
108175f6d617Schristos   cmp.parent = parent;
108275f6d617Schristos 
108375f6d617Schristos   /* cmp.file[f].desc markers */
108475f6d617Schristos #define NONEXISTENT (-1) /* nonexistent file */
108575f6d617Schristos #define UNOPENED (-2) /* unopened file (e.g. directory) */
108675f6d617Schristos #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
108775f6d617Schristos 
108875f6d617Schristos #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
108975f6d617Schristos 
109075f6d617Schristos   cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED;
109175f6d617Schristos   cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED;
109275f6d617Schristos 
109375f6d617Schristos   /* Now record the full name of each file, including nonexistent ones.  */
109475f6d617Schristos 
109575f6d617Schristos   if (name0 == 0)
109675f6d617Schristos     name0 = name1;
109775f6d617Schristos   if (name1 == 0)
109875f6d617Schristos     name1 = name0;
109975f6d617Schristos 
110075f6d617Schristos   if (!parent)
110175f6d617Schristos     {
110275f6d617Schristos       free0 = 0;
110375f6d617Schristos       free1 = 0;
110475f6d617Schristos       cmp.file[0].name = name0;
110575f6d617Schristos       cmp.file[1].name = name1;
110675f6d617Schristos     }
110775f6d617Schristos   else
110875f6d617Schristos     {
110975f6d617Schristos       cmp.file[0].name = free0
111075f6d617Schristos 	= dir_file_pathname (parent->file[0].name, name0);
111175f6d617Schristos       cmp.file[1].name = free1
111275f6d617Schristos 	= dir_file_pathname (parent->file[1].name, name1);
111375f6d617Schristos     }
111475f6d617Schristos 
111575f6d617Schristos   /* Stat the files.  */
111675f6d617Schristos 
111775f6d617Schristos   for (f = 0; f < 2; f++)
111875f6d617Schristos     {
111975f6d617Schristos       if (cmp.file[f].desc != NONEXISTENT)
112075f6d617Schristos 	{
112175f6d617Schristos 	  if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
112275f6d617Schristos 	    {
112375f6d617Schristos 	      cmp.file[f].desc = cmp.file[0].desc;
112475f6d617Schristos 	      cmp.file[f].stat = cmp.file[0].stat;
112575f6d617Schristos 	    }
112675f6d617Schristos 	  else if (strcmp (cmp.file[f].name, "-") == 0)
112775f6d617Schristos 	    {
112875f6d617Schristos 	      cmp.file[f].desc = STDIN_FILENO;
112975f6d617Schristos 	      if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
113075f6d617Schristos 		cmp.file[f].desc = ERRNO_ENCODE (errno);
113175f6d617Schristos 	      else
113275f6d617Schristos 		{
113375f6d617Schristos 		  if (S_ISREG (cmp.file[f].stat.st_mode))
113475f6d617Schristos 		    {
113575f6d617Schristos 		      off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
113675f6d617Schristos 		      if (pos < 0)
113775f6d617Schristos 			cmp.file[f].desc = ERRNO_ENCODE (errno);
113875f6d617Schristos 		      else
113975f6d617Schristos 			cmp.file[f].stat.st_size =
114075f6d617Schristos 			  MAX (0, cmp.file[f].stat.st_size - pos);
114175f6d617Schristos 		    }
114275f6d617Schristos 
114375f6d617Schristos 		  /* POSIX 1003.1-2001 requires current time for
114475f6d617Schristos 		     stdin.  */
114575f6d617Schristos 		  set_mtime_to_now (&cmp.file[f].stat);
114675f6d617Schristos 		}
114775f6d617Schristos 	    }
114875f6d617Schristos 	  else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0)
114975f6d617Schristos 	    cmp.file[f].desc = ERRNO_ENCODE (errno);
115075f6d617Schristos 	}
115175f6d617Schristos     }
115275f6d617Schristos 
115375f6d617Schristos   /* Mark files as nonexistent at the top level as needed for -N and
115475f6d617Schristos      --unidirectional-new-file.  */
115575f6d617Schristos   if (! parent)
115675f6d617Schristos     {
115775f6d617Schristos       if ((new_file | unidirectional_new_file)
115875f6d617Schristos 	  && cmp.file[0].desc == ERRNO_ENCODE (ENOENT)
115975f6d617Schristos 	  && cmp.file[1].desc == UNOPENED)
116075f6d617Schristos 	cmp.file[0].desc = NONEXISTENT;
116175f6d617Schristos 
116275f6d617Schristos       if (new_file
116375f6d617Schristos 	  && cmp.file[0].desc == UNOPENED
116475f6d617Schristos 	  && cmp.file[1].desc == ERRNO_ENCODE (ENOENT))
116575f6d617Schristos 	cmp.file[1].desc = NONEXISTENT;
116675f6d617Schristos     }
116775f6d617Schristos 
116875f6d617Schristos   for (f = 0; f < 2; f++)
116975f6d617Schristos     if (cmp.file[f].desc == NONEXISTENT)
117075f6d617Schristos       cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
117175f6d617Schristos 
117275f6d617Schristos   for (f = 0; f < 2; f++)
117375f6d617Schristos     {
117475f6d617Schristos       int e = ERRNO_DECODE (cmp.file[f].desc);
117575f6d617Schristos       if (0 <= e)
117675f6d617Schristos 	{
117775f6d617Schristos 	  errno = e;
117875f6d617Schristos 	  perror_with_name (cmp.file[f].name);
117975f6d617Schristos 	  status = EXIT_TROUBLE;
118075f6d617Schristos 	}
118175f6d617Schristos     }
118275f6d617Schristos 
118375f6d617Schristos   if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
118475f6d617Schristos     {
118575f6d617Schristos       /* If one is a directory, and it was specified in the command line,
118675f6d617Schristos 	 use the file in that dir with the other file's basename.  */
118775f6d617Schristos 
118875f6d617Schristos       int fnm_arg = DIR_P (0);
118975f6d617Schristos       int dir_arg = 1 - fnm_arg;
119075f6d617Schristos       char const *fnm = cmp.file[fnm_arg].name;
119175f6d617Schristos       char const *dir = cmp.file[dir_arg].name;
119275f6d617Schristos       char const *filename = cmp.file[dir_arg].name = free0
119375f6d617Schristos 	= dir_file_pathname (dir, base_name (fnm));
119475f6d617Schristos 
119575f6d617Schristos       if (strcmp (fnm, "-") == 0)
119675f6d617Schristos 	fatal ("cannot compare `-' to a directory");
119775f6d617Schristos 
119875f6d617Schristos       if (stat (filename, &cmp.file[dir_arg].stat) != 0)
119975f6d617Schristos 	{
120075f6d617Schristos 	  perror_with_name (filename);
120175f6d617Schristos 	  status = EXIT_TROUBLE;
120275f6d617Schristos 	}
120375f6d617Schristos     }
120475f6d617Schristos 
120575f6d617Schristos   if (status != EXIT_SUCCESS)
120675f6d617Schristos     {
120775f6d617Schristos       /* One of the files should exist but does not.  */
120875f6d617Schristos     }
120975f6d617Schristos   else if ((same_files
121075f6d617Schristos 	    = (cmp.file[0].desc != NONEXISTENT
121175f6d617Schristos 	       && cmp.file[1].desc != NONEXISTENT
1212*f8c23a2bSchristos 	       && (same_special_file (&cmp.file[0].stat, &cmp.file[1].stat)
1213*f8c23a2bSchristos 	           || (0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
121475f6d617Schristos 		       && same_file_attributes (&cmp.file[0].stat,
1215*f8c23a2bSchristos 					&cmp.file[1].stat)))))
121675f6d617Schristos 	   && no_diff_means_no_output)
121775f6d617Schristos     {
121875f6d617Schristos       /* The two named files are actually the same physical file.
121975f6d617Schristos 	 We know they are identical without actually reading them.  */
122075f6d617Schristos     }
122175f6d617Schristos   else if (DIR_P (0) & DIR_P (1))
122275f6d617Schristos     {
122375f6d617Schristos       if (output_style == OUTPUT_IFDEF)
122475f6d617Schristos 	fatal ("-D option not supported with directories");
122575f6d617Schristos 
122675f6d617Schristos       /* If both are directories, compare the files in them.  */
122775f6d617Schristos 
122875f6d617Schristos       if (parent && !recursive)
122975f6d617Schristos 	{
123075f6d617Schristos 	  /* But don't compare dir contents one level down
123175f6d617Schristos 	     unless -r was specified.
123275f6d617Schristos 	     See POSIX 1003.1-2001 for this format.  */
123375f6d617Schristos 	  message ("Common subdirectories: %s and %s\n",
123475f6d617Schristos 		   cmp.file[0].name, cmp.file[1].name);
123575f6d617Schristos 	}
123675f6d617Schristos       else
123775f6d617Schristos 	status = diff_dirs (&cmp, compare_files);
123875f6d617Schristos     }
123975f6d617Schristos   else if ((DIR_P (0) | DIR_P (1))
124075f6d617Schristos 	   || (parent
124175f6d617Schristos 	       && (! S_ISREG (cmp.file[0].stat.st_mode)
124275f6d617Schristos 		   || ! S_ISREG (cmp.file[1].stat.st_mode))))
124375f6d617Schristos     {
124475f6d617Schristos       if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
124575f6d617Schristos 	{
124675f6d617Schristos 	  /* We have a subdirectory that exists only in one directory.  */
124775f6d617Schristos 
124875f6d617Schristos 	  if ((DIR_P (0) | DIR_P (1))
124975f6d617Schristos 	      && recursive
125075f6d617Schristos 	      && (new_file
125175f6d617Schristos 		  || (unidirectional_new_file
125275f6d617Schristos 		      && cmp.file[0].desc == NONEXISTENT)))
125375f6d617Schristos 	    status = diff_dirs (&cmp, compare_files);
125475f6d617Schristos 	  else
125575f6d617Schristos 	    {
125675f6d617Schristos 	      char const *dir
125775f6d617Schristos 		= parent->file[cmp.file[0].desc == NONEXISTENT].name;
125875f6d617Schristos 
125975f6d617Schristos 	      /* See POSIX 1003.1-2001 for this format.  */
126075f6d617Schristos 	      message ("Only in %s: %s\n", dir, name0);
126175f6d617Schristos 
126275f6d617Schristos 	      status = EXIT_FAILURE;
126375f6d617Schristos 	    }
126475f6d617Schristos 	}
126575f6d617Schristos       else
126675f6d617Schristos 	{
126775f6d617Schristos 	  /* We have two files that are not to be compared.  */
126875f6d617Schristos 
126975f6d617Schristos 	  /* See POSIX 1003.1-2001 for this format.  */
127075f6d617Schristos 	  message5 ("File %s is a %s while file %s is a %s\n",
127175f6d617Schristos 		    file_label[0] ? file_label[0] : cmp.file[0].name,
127275f6d617Schristos 		    filetype (&cmp.file[0].stat),
127375f6d617Schristos 		    file_label[1] ? file_label[1] : cmp.file[1].name,
127475f6d617Schristos 		    filetype (&cmp.file[1].stat));
127575f6d617Schristos 
127675f6d617Schristos 	  /* This is a difference.  */
127775f6d617Schristos 	  status = EXIT_FAILURE;
127875f6d617Schristos 	}
127975f6d617Schristos     }
128075f6d617Schristos   else if (files_can_be_treated_as_binary
128175f6d617Schristos 	   && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
128275f6d617Schristos 	   && (cmp.file[0].desc == NONEXISTENT
128375f6d617Schristos 	       || S_ISREG (cmp.file[0].stat.st_mode))
128475f6d617Schristos 	   && (cmp.file[1].desc == NONEXISTENT
128575f6d617Schristos 	       || S_ISREG (cmp.file[1].stat.st_mode)))
128675f6d617Schristos     {
128775f6d617Schristos       message ("Files %s and %s differ\n",
128875f6d617Schristos 	       file_label[0] ? file_label[0] : cmp.file[0].name,
128975f6d617Schristos 	       file_label[1] ? file_label[1] : cmp.file[1].name);
129075f6d617Schristos       status = EXIT_FAILURE;
129175f6d617Schristos     }
129275f6d617Schristos   else
129375f6d617Schristos     {
129475f6d617Schristos       /* Both exist and neither is a directory.  */
129575f6d617Schristos 
129675f6d617Schristos       /* Open the files and record their descriptors.  */
129775f6d617Schristos 
129875f6d617Schristos       if (cmp.file[0].desc == UNOPENED)
129975f6d617Schristos 	if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0)
130075f6d617Schristos 	  {
130175f6d617Schristos 	    perror_with_name (cmp.file[0].name);
130275f6d617Schristos 	    status = EXIT_TROUBLE;
130375f6d617Schristos 	  }
130475f6d617Schristos       if (cmp.file[1].desc == UNOPENED)
130575f6d617Schristos 	{
130675f6d617Schristos 	  if (same_files)
130775f6d617Schristos 	    cmp.file[1].desc = cmp.file[0].desc;
130875f6d617Schristos 	  else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0))
130975f6d617Schristos 		   < 0)
131075f6d617Schristos 	    {
131175f6d617Schristos 	      perror_with_name (cmp.file[1].name);
131275f6d617Schristos 	      status = EXIT_TROUBLE;
131375f6d617Schristos 	    }
131475f6d617Schristos 	}
131575f6d617Schristos 
131675f6d617Schristos #if HAVE_SETMODE_DOS
131775f6d617Schristos       if (binary)
131875f6d617Schristos 	for (f = 0; f < 2; f++)
131975f6d617Schristos 	  if (0 <= cmp.file[f].desc)
132075f6d617Schristos 	    set_binary_mode (cmp.file[f].desc, 1);
132175f6d617Schristos #endif
132275f6d617Schristos 
132375f6d617Schristos       /* Compare the files, if no error was found.  */
132475f6d617Schristos 
132575f6d617Schristos       if (status == EXIT_SUCCESS)
132675f6d617Schristos 	status = diff_2_files (&cmp);
132775f6d617Schristos 
132875f6d617Schristos       /* Close the file descriptors.  */
132975f6d617Schristos 
133075f6d617Schristos       if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
133175f6d617Schristos 	{
133275f6d617Schristos 	  perror_with_name (cmp.file[0].name);
133375f6d617Schristos 	  status = EXIT_TROUBLE;
133475f6d617Schristos 	}
133575f6d617Schristos       if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
133675f6d617Schristos 	  && close (cmp.file[1].desc) != 0)
133775f6d617Schristos 	{
133875f6d617Schristos 	  perror_with_name (cmp.file[1].name);
133975f6d617Schristos 	  status = EXIT_TROUBLE;
134075f6d617Schristos 	}
134175f6d617Schristos     }
134275f6d617Schristos 
134375f6d617Schristos   /* Now the comparison has been done, if no error prevented it,
134475f6d617Schristos      and STATUS is the value this function will return.  */
134575f6d617Schristos 
134675f6d617Schristos   if (status == EXIT_SUCCESS)
134775f6d617Schristos     {
134875f6d617Schristos       if (report_identical_files && !DIR_P (0))
134975f6d617Schristos 	message ("Files %s and %s are identical\n",
135075f6d617Schristos 		 file_label[0] ? file_label[0] : cmp.file[0].name,
135175f6d617Schristos 		 file_label[1] ? file_label[1] : cmp.file[1].name);
135275f6d617Schristos     }
135375f6d617Schristos   else
135475f6d617Schristos     {
135575f6d617Schristos       /* Flush stdout so that the user sees differences immediately.
135675f6d617Schristos 	 This can hurt performance, unfortunately.  */
135775f6d617Schristos       if (fflush (stdout) != 0)
135875f6d617Schristos 	pfatal_with_name (_("standard output"));
135975f6d617Schristos     }
136075f6d617Schristos 
136175f6d617Schristos   if (free0)
136275f6d617Schristos     free (free0);
136375f6d617Schristos   if (free1)
136475f6d617Schristos     free (free1);
136575f6d617Schristos 
136675f6d617Schristos   return status;
136775f6d617Schristos }
1368