Blame src/diff.c

Packit 33f14e
/* diff - compare files line by line
Packit 33f14e
Packit 33f14e
   Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
Packit 33f14e
   2009-2013, 2015-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This file is part of GNU DIFF.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation, either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
#define GDIFF_MAIN
Packit 33f14e
#include "diff.h"
Packit 33f14e
#include "die.h"
Packit 33f14e
#include <assert.h>
Packit 33f14e
#include "paths.h"
Packit 33f14e
#include <c-stack.h>
Packit 33f14e
#include <dirname.h>
Packit 33f14e
#include <error.h>
Packit 33f14e
#include <exclude.h>
Packit 33f14e
#include <exitfail.h>
Packit 33f14e
#include <filenamecat.h>
Packit 33f14e
#include <file-type.h>
Packit 33f14e
#include <fnmatch.h>
Packit 33f14e
#include <getopt.h>
Packit 33f14e
#include <hard-locale.h>
Packit 33f14e
#include <prepargs.h>
Packit 33f14e
#include <progname.h>
Packit 33f14e
#include <sh-quote.h>
Packit 33f14e
#include <stat-time.h>
Packit 33f14e
#include <timespec.h>
Packit 33f14e
#include <version-etc.h>
Packit 33f14e
#include <xalloc.h>
Packit 33f14e
#include <xreadlink.h>
Packit 33f14e
#include <binary-io.h>
Packit 33f14e
Packit 33f14e
/* The official name of this program (e.g., no 'g' prefix).  */
Packit 33f14e
#define PROGRAM_NAME "diff"
Packit 33f14e
Packit 33f14e
#define AUTHORS \
Packit 33f14e
  proper_name ("Paul Eggert"), \
Packit 33f14e
  proper_name ("Mike Haertel"), \
Packit 33f14e
  proper_name ("David Hayes"), \
Packit 33f14e
  proper_name ("Richard Stallman"), \
Packit 33f14e
  proper_name ("Len Tower")
Packit 33f14e
Packit 33f14e
#ifndef GUTTER_WIDTH_MINIMUM
Packit 33f14e
# define GUTTER_WIDTH_MINIMUM 3
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
struct regexp_list
Packit 33f14e
{
Packit 33f14e
  char *regexps;	/* chars representing disjunction of the regexps */
Packit 33f14e
  size_t len;		/* chars used in 'regexps' */
Packit 33f14e
  size_t size;		/* size malloc'ed for 'regexps'; 0 if not malloc'ed */
Packit 33f14e
  bool multiple_regexps;/* Does 'regexps' represent a disjunction?  */
Packit 33f14e
  struct re_pattern_buffer *buf;
Packit 33f14e
};
Packit 33f14e
Packit 33f14e
static int compare_files (struct comparison const *, char const *, char const *);
Packit 33f14e
static void add_regexp (struct regexp_list *, char const *);
Packit 33f14e
static void summarize_regexp_list (struct regexp_list *);
Packit 33f14e
static void specify_style (enum output_style);
Packit 33f14e
static void specify_value (char const **, char const *, char const *);
Packit 33f14e
static void specify_colors_style (char const *);
Packit 33f14e
static void try_help (char const *, char const *) __attribute__((noreturn));
Packit 33f14e
static void check_stdout (void);
Packit 33f14e
static void usage (void);
Packit 33f14e
Packit 33f14e
/* If comparing directories, compare their common subdirectories
Packit 33f14e
   recursively.  */
Packit 33f14e
static bool recursive;
Packit 33f14e
Packit 33f14e
/* In context diffs, show previous lines that match these regexps.  */
Packit 33f14e
static struct regexp_list function_regexp_list;
Packit 33f14e
Packit 33f14e
/* Ignore changes affecting only lines that match these regexps.  */
Packit 33f14e
static struct regexp_list ignore_regexp_list;
Packit 33f14e
Packit 33f14e
#if O_BINARY
Packit 33f14e
/* Use binary I/O when reading and writing data (--binary).
Packit 33f14e
   On POSIX hosts, this has no effect.  */
Packit 33f14e
static bool binary;
Packit 33f14e
#else
Packit 33f14e
enum { binary = true };
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* If one file is missing, treat it as present but empty (-N).  */
Packit 33f14e
static bool new_file;
Packit 33f14e
Packit 33f14e
/* If the first file is missing, treat it as present but empty
Packit 33f14e
   (--unidirectional-new-file).  */
Packit 33f14e
static bool unidirectional_new_file;
Packit 33f14e
Packit 33f14e
/* Report files compared that are the same (-s).
Packit 33f14e
   Normally nothing is output when that happens.  */
Packit 33f14e
static bool report_identical_files;
Packit 33f14e

Packit 33f14e
static char const shortopts[] =
Packit 33f14e
"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
Packit 33f14e
Packit 33f14e
/* Values for long options that do not have single-letter equivalents.  */
Packit 33f14e
enum
Packit 33f14e
{
Packit 33f14e
  BINARY_OPTION = CHAR_MAX + 1,
Packit 33f14e
  FROM_FILE_OPTION,
Packit 33f14e
  HELP_OPTION,
Packit 33f14e
  HORIZON_LINES_OPTION,
Packit 33f14e
  IGNORE_FILE_NAME_CASE_OPTION,
Packit 33f14e
  INHIBIT_HUNK_MERGE_OPTION,
Packit 33f14e
  LEFT_COLUMN_OPTION,
Packit 33f14e
  LINE_FORMAT_OPTION,
Packit 33f14e
  NO_DEREFERENCE_OPTION,
Packit 33f14e
  NO_IGNORE_FILE_NAME_CASE_OPTION,
Packit 33f14e
  NORMAL_OPTION,
Packit 33f14e
  SDIFF_MERGE_ASSIST_OPTION,
Packit 33f14e
  STRIP_TRAILING_CR_OPTION,
Packit 33f14e
  SUPPRESS_BLANK_EMPTY_OPTION,
Packit 33f14e
  SUPPRESS_COMMON_LINES_OPTION,
Packit 33f14e
  TABSIZE_OPTION,
Packit 33f14e
  TO_FILE_OPTION,
Packit 33f14e
Packit 33f14e
  /* These options must be in sequence.  */
Packit 33f14e
  UNCHANGED_LINE_FORMAT_OPTION,
Packit 33f14e
  OLD_LINE_FORMAT_OPTION,
Packit 33f14e
  NEW_LINE_FORMAT_OPTION,
Packit 33f14e
Packit 33f14e
  /* These options must be in sequence.  */
Packit 33f14e
  UNCHANGED_GROUP_FORMAT_OPTION,
Packit 33f14e
  OLD_GROUP_FORMAT_OPTION,
Packit 33f14e
  NEW_GROUP_FORMAT_OPTION,
Packit 33f14e
  CHANGED_GROUP_FORMAT_OPTION,
Packit 33f14e
Packit 33f14e
  COLOR_OPTION,
Packit 33f14e
  COLOR_PALETTE_OPTION,
Packit 33f14e
Packit 33f14e
  PRESUME_OUTPUT_TTY_OPTION,
Packit 33f14e
};
Packit 33f14e
Packit 33f14e
static char const group_format_option[][sizeof "--unchanged-group-format"] =
Packit 33f14e
  {
Packit 33f14e
    "--unchanged-group-format",
Packit 33f14e
    "--old-group-format",
Packit 33f14e
    "--new-group-format",
Packit 33f14e
    "--changed-group-format"
Packit 33f14e
  };
Packit 33f14e
Packit 33f14e
static char const line_format_option[][sizeof "--unchanged-line-format"] =
Packit 33f14e
  {
Packit 33f14e
    "--unchanged-line-format",
Packit 33f14e
    "--old-line-format",
Packit 33f14e
    "--new-line-format"
Packit 33f14e
  };
Packit 33f14e
Packit 33f14e
static struct option const longopts[] =
Packit 33f14e
{
Packit 33f14e
  {"binary", 0, 0, BINARY_OPTION},
Packit 33f14e
  {"brief", 0, 0, 'q'},
Packit 33f14e
  {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
Packit 33f14e
  {"color", 2, 0, COLOR_OPTION},
Packit 33f14e
  {"context", 2, 0, 'C'},
Packit 33f14e
  {"ed", 0, 0, 'e'},
Packit 33f14e
  {"exclude", 1, 0, 'x'},
Packit 33f14e
  {"exclude-from", 1, 0, 'X'},
Packit 33f14e
  {"expand-tabs", 0, 0, 't'},
Packit 33f14e
  {"forward-ed", 0, 0, 'f'},
Packit 33f14e
  {"from-file", 1, 0, FROM_FILE_OPTION},
Packit 33f14e
  {"help", 0, 0, HELP_OPTION},
Packit 33f14e
  {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
Packit 33f14e
  {"ifdef", 1, 0, 'D'},
Packit 33f14e
  {"ignore-all-space", 0, 0, 'w'},
Packit 33f14e
  {"ignore-blank-lines", 0, 0, 'B'},
Packit 33f14e
  {"ignore-case", 0, 0, 'i'},
Packit 33f14e
  {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
Packit 33f14e
  {"ignore-matching-lines", 1, 0, 'I'},
Packit 33f14e
  {"ignore-space-change", 0, 0, 'b'},
Packit 33f14e
  {"ignore-tab-expansion", 0, 0, 'E'},
Packit 33f14e
  {"ignore-trailing-space", 0, 0, 'Z'},
Packit 33f14e
  {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
Packit 33f14e
  {"initial-tab", 0, 0, 'T'},
Packit 33f14e
  {"label", 1, 0, 'L'},
Packit 33f14e
  {"left-column", 0, 0, LEFT_COLUMN_OPTION},
Packit 33f14e
  {"line-format", 1, 0, LINE_FORMAT_OPTION},
Packit 33f14e
  {"minimal", 0, 0, 'd'},
Packit 33f14e
  {"new-file", 0, 0, 'N'},
Packit 33f14e
  {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
Packit 33f14e
  {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
Packit 33f14e
  {"no-dereference", 0, 0, NO_DEREFERENCE_OPTION},
Packit 33f14e
  {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
Packit 33f14e
  {"normal", 0, 0, NORMAL_OPTION},
Packit 33f14e
  {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
Packit 33f14e
  {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
Packit 33f14e
  {"paginate", 0, 0, 'l'},
Packit 33f14e
  {"palette", 1, 0, COLOR_PALETTE_OPTION},
Packit 33f14e
  {"rcs", 0, 0, 'n'},
Packit 33f14e
  {"recursive", 0, 0, 'r'},
Packit 33f14e
  {"report-identical-files", 0, 0, 's'},
Packit 33f14e
  {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
Packit 33f14e
  {"show-c-function", 0, 0, 'p'},
Packit 33f14e
  {"show-function-line", 1, 0, 'F'},
Packit 33f14e
  {"side-by-side", 0, 0, 'y'},
Packit 33f14e
  {"speed-large-files", 0, 0, 'H'},
Packit 33f14e
  {"starting-file", 1, 0, 'S'},
Packit 33f14e
  {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
Packit 33f14e
  {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
Packit 33f14e
  {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
Packit 33f14e
  {"tabsize", 1, 0, TABSIZE_OPTION},
Packit 33f14e
  {"text", 0, 0, 'a'},
Packit 33f14e
  {"to-file", 1, 0, TO_FILE_OPTION},
Packit 33f14e
  {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
Packit 33f14e
  {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
Packit 33f14e
  {"unidirectional-new-file", 0, 0, 'P'},
Packit 33f14e
  {"unified", 2, 0, 'U'},
Packit 33f14e
  {"version", 0, 0, 'v'},
Packit 33f14e
  {"width", 1, 0, 'W'},
Packit 33f14e
Packit 33f14e
  /* This is solely for testing.  Do not document.  */
Packit 33f14e
  {"-presume-output-tty", no_argument, NULL, PRESUME_OUTPUT_TTY_OPTION},
Packit 33f14e
  {0, 0, 0, 0}
Packit 33f14e
};
Packit 33f14e
Packit 33f14e
/* Return a string containing the command options with which diff was invoked.
Packit 33f14e
   Spaces appear between what were separate ARGV-elements.
Packit 33f14e
   There is a space at the beginning but none at the end.
Packit 33f14e
   If there were no options, the result is an empty string.
Packit 33f14e
Packit 33f14e
   Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
Packit 33f14e
   the length of that vector.  */
Packit 33f14e
Packit 33f14e
static char *
Packit 33f14e
option_list (char **optionvec, int count)
Packit 33f14e
{
Packit 33f14e
  int i;
Packit 33f14e
  size_t size = 1;
Packit 33f14e
  char *result;
Packit 33f14e
  char *p;
Packit 33f14e
Packit 33f14e
  for (i = 0; i < count; i++)
Packit 33f14e
    size += 1 + shell_quote_length (optionvec[i]);
Packit 33f14e
Packit 33f14e
  p = result = xmalloc (size);
Packit 33f14e
Packit 33f14e
  for (i = 0; i < count; i++)
Packit 33f14e
    {
Packit 33f14e
      *p++ = ' ';
Packit 33f14e
      p = shell_quote_copy (p, optionvec[i]);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  *p = '\0';
Packit 33f14e
  return result;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Return an option value suitable for add_exclude.  */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
exclude_options (void)
Packit 33f14e
{
Packit 33f14e
  return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
Packit 33f14e
}
Packit 33f14e

Packit 33f14e
int
Packit 33f14e
main (int argc, char **argv)
Packit 33f14e
{
Packit 33f14e
  int exit_status = EXIT_SUCCESS;
Packit 33f14e
  int c;
Packit 33f14e
  int i;
Packit 33f14e
  int prev = -1;
Packit 33f14e
  lin ocontext = -1;
Packit 33f14e
  bool explicit_context = false;
Packit 33f14e
  size_t width = 0;
Packit 33f14e
  bool show_c_function = false;
Packit 33f14e
  char const *from_file = NULL;
Packit 33f14e
  char const *to_file = NULL;
Packit 33f14e
  uintmax_t numval;
Packit 33f14e
  char *numend;
Packit 33f14e
Packit 33f14e
  /* Do our initializations.  */
Packit 33f14e
  exit_failure = EXIT_TROUBLE;
Packit 33f14e
  initialize_main (&argc, &argv);
Packit 33f14e
  set_program_name (argv[0]);
Packit 33f14e
  setlocale (LC_ALL, "");
Packit 33f14e
  bindtextdomain (PACKAGE, LOCALEDIR);
Packit 33f14e
  textdomain (PACKAGE);
Packit 33f14e
  c_stack_action (0);
Packit 33f14e
  function_regexp_list.buf = &function_regexp;
Packit 33f14e
  ignore_regexp_list.buf = &ignore_regexp;
Packit 33f14e
  re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
Packit 33f14e
  excluded = new_exclude ();
Packit 33f14e
  presume_output_tty = false;
Packit 33f14e
Packit 33f14e
  /* Decode the options.  */
Packit 33f14e
Packit 33f14e
  while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
Packit 33f14e
    {
Packit 33f14e
      switch (c)
Packit 33f14e
	{
Packit 33f14e
	case 0:
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case '0':
Packit 33f14e
	case '1':
Packit 33f14e
	case '2':
Packit 33f14e
	case '3':
Packit 33f14e
	case '4':
Packit 33f14e
	case '5':
Packit 33f14e
	case '6':
Packit 33f14e
	case '7':
Packit 33f14e
	case '8':
Packit 33f14e
	case '9':
Packit 33f14e
	  ocontext = (! ISDIGIT (prev)
Packit 33f14e
		      ? c - '0'
Packit 33f14e
		      : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
Packit 33f14e
			 < CONTEXT_MAX / 10)
Packit 33f14e
		      ? 10 * ocontext + (c - '0')
Packit 33f14e
		      : CONTEXT_MAX);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'a':
Packit 33f14e
	  text = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'b':
Packit 33f14e
	  if (ignore_white_space < IGNORE_SPACE_CHANGE)
Packit 33f14e
	    ignore_white_space = IGNORE_SPACE_CHANGE;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'Z':
Packit 33f14e
	  if (ignore_white_space < IGNORE_SPACE_CHANGE)
Packit 33f14e
	    ignore_white_space |= IGNORE_TRAILING_SPACE;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'B':
Packit 33f14e
	  ignore_blank_lines = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'C':
Packit 33f14e
	case 'U':
Packit 33f14e
	  {
Packit 33f14e
	    if (optarg)
Packit 33f14e
	      {
Packit 33f14e
		numval = strtoumax (optarg, &numend, 10);
Packit 33f14e
		if (*numend)
Packit 33f14e
		  try_help ("invalid context length '%s'", optarg);
Packit 33f14e
		if (CONTEXT_MAX < numval)
Packit 33f14e
		  numval = CONTEXT_MAX;
Packit 33f14e
	      }
Packit 33f14e
	    else
Packit 33f14e
	      numval = 3;
Packit 33f14e
Packit 33f14e
	    specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
Packit 33f14e
	    if (context < numval)
Packit 33f14e
	      context = numval;
Packit 33f14e
	    explicit_context = true;
Packit 33f14e
	  }
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'c':
Packit 33f14e
	  specify_style (OUTPUT_CONTEXT);
Packit 33f14e
	  if (context < 3)
Packit 33f14e
	    context = 3;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'd':
Packit 33f14e
	  minimal = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'D':
Packit 33f14e
	  specify_style (OUTPUT_IFDEF);
Packit 33f14e
	  {
Packit 33f14e
	    static char const C_ifdef_group_formats[] =
Packit 33f14e
	      "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
Packit 33f14e
	    char *b = xmalloc (sizeof C_ifdef_group_formats
Packit 33f14e
			       + 7 * strlen (optarg) - 14 /* 7*"%s" */
Packit 33f14e
			       - 8 /* 5*"%%" + 3*"%c" */);
Packit 33f14e
	    sprintf (b, C_ifdef_group_formats,
Packit 33f14e
		     0,
Packit 33f14e
		     optarg, optarg, 0,
Packit 33f14e
		     optarg, optarg, 0,
Packit 33f14e
		     optarg, optarg, optarg);
Packit 33f14e
	    for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
Packit 33f14e
	      {
Packit 33f14e
		specify_value (&group_format[i], b, "-D");
Packit 33f14e
		b += strlen (b) + 1;
Packit 33f14e
	      }
Packit 33f14e
	  }
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'e':
Packit 33f14e
	  specify_style (OUTPUT_ED);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'E':
Packit 33f14e
	  if (ignore_white_space < IGNORE_SPACE_CHANGE)
Packit 33f14e
	    ignore_white_space |= IGNORE_TAB_EXPANSION;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'f':
Packit 33f14e
	  specify_style (OUTPUT_FORWARD_ED);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'F':
Packit 33f14e
	  add_regexp (&function_regexp_list, optarg);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'h':
Packit 33f14e
	  /* Split the files into chunks for faster processing.
Packit 33f14e
	     Usually does not change the result.
Packit 33f14e
Packit 33f14e
	     This currently has no effect.  */
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'H':
Packit 33f14e
	  speed_large_files = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'i':
Packit 33f14e
	  ignore_case = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'I':
Packit 33f14e
	  add_regexp (&ignore_regexp_list, optarg);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'l':
Packit 33f14e
	  if (!pr_program[0])
Packit 33f14e
	    try_help ("pagination not supported on this host", NULL);
Packit 33f14e
	  paginate = true;
Packit 33f14e
#ifdef SIGCHLD
Packit 33f14e
	  /* Pagination requires forking and waiting, and
Packit 33f14e
	     System V fork+wait does not work if SIGCHLD is ignored.  */
Packit 33f14e
	  signal (SIGCHLD, SIG_DFL);
Packit 33f14e
#endif
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'L':
Packit 33f14e
	  if (!file_label[0])
Packit 33f14e
	    file_label[0] = optarg;
Packit 33f14e
	  else if (!file_label[1])
Packit 33f14e
	    file_label[1] = optarg;
Packit 33f14e
	  else
Packit 33f14e
	    fatal ("too many file label options");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'n':
Packit 33f14e
	  specify_style (OUTPUT_RCS);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'N':
Packit 33f14e
	  new_file = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'p':
Packit 33f14e
	  show_c_function = true;
Packit 33f14e
	  add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'P':
Packit 33f14e
	  unidirectional_new_file = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'q':
Packit 33f14e
	  brief = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'r':
Packit 33f14e
	  recursive = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 's':
Packit 33f14e
	  report_identical_files = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'S':
Packit 33f14e
	  specify_value (&starting_file, optarg, "-S");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 't':
Packit 33f14e
	  expand_tabs = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'T':
Packit 33f14e
	  initial_tab = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'u':
Packit 33f14e
	  specify_style (OUTPUT_UNIFIED);
Packit 33f14e
	  if (context < 3)
Packit 33f14e
	    context = 3;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'v':
Packit 33f14e
	  version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
Packit 33f14e
		       AUTHORS, (char *) NULL);
Packit 33f14e
	  check_stdout ();
Packit 33f14e
	  return EXIT_SUCCESS;
Packit 33f14e
Packit 33f14e
	case 'w':
Packit 33f14e
	  ignore_white_space = IGNORE_ALL_SPACE;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'x':
Packit 33f14e
	  add_exclude (excluded, optarg, exclude_options ());
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'X':
Packit 33f14e
	  if (add_exclude_file (add_exclude, excluded, optarg,
Packit 33f14e
				exclude_options (), '\n'))
Packit 33f14e
	    pfatal_with_name (optarg);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'y':
Packit 33f14e
	  specify_style (OUTPUT_SDIFF);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case 'W':
Packit 33f14e
	  numval = strtoumax (optarg, &numend, 10);
Packit 33f14e
	  if (! (0 < numval && numval <= SIZE_MAX) || *numend)
Packit 33f14e
	    try_help ("invalid width '%s'", optarg);
Packit 33f14e
	  if (width != numval)
Packit 33f14e
	    {
Packit 33f14e
	      if (width)
Packit 33f14e
		fatal ("conflicting width options");
Packit 33f14e
	      width = numval;
Packit 33f14e
	    }
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case BINARY_OPTION:
Packit 33f14e
#if O_BINARY
Packit 33f14e
	  binary = true;
Packit 33f14e
	  if (! isatty (STDOUT_FILENO))
Packit 33f14e
	    set_binary_mode (STDOUT_FILENO, O_BINARY);
Packit 33f14e
#endif
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case FROM_FILE_OPTION:
Packit 33f14e
	  specify_value (&from_file, optarg, "--from-file");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case HELP_OPTION:
Packit 33f14e
	  usage ();
Packit 33f14e
	  check_stdout ();
Packit 33f14e
	  return EXIT_SUCCESS;
Packit 33f14e
Packit 33f14e
	case HORIZON_LINES_OPTION:
Packit 33f14e
	  numval = strtoumax (optarg, &numend, 10);
Packit 33f14e
	  if (*numend)
Packit 33f14e
	    try_help ("invalid horizon length '%s'", optarg);
Packit 33f14e
	  horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case IGNORE_FILE_NAME_CASE_OPTION:
Packit 33f14e
	  ignore_file_name_case = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case INHIBIT_HUNK_MERGE_OPTION:
Packit 33f14e
	  /* This option is obsolete, but accept it for backward
Packit 33f14e
             compatibility.  */
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case LEFT_COLUMN_OPTION:
Packit 33f14e
	  left_column = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case LINE_FORMAT_OPTION:
Packit 33f14e
	  specify_style (OUTPUT_IFDEF);
Packit 33f14e
	  for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
Packit 33f14e
	    specify_value (&line_format[i], optarg, "--line-format");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case NO_DEREFERENCE_OPTION:
Packit 33f14e
	  no_dereference_symlinks = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case NO_IGNORE_FILE_NAME_CASE_OPTION:
Packit 33f14e
	  ignore_file_name_case = false;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case NORMAL_OPTION:
Packit 33f14e
	  specify_style (OUTPUT_NORMAL);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case SDIFF_MERGE_ASSIST_OPTION:
Packit 33f14e
	  specify_style (OUTPUT_SDIFF);
Packit 33f14e
	  sdiff_merge_assist = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case STRIP_TRAILING_CR_OPTION:
Packit 33f14e
	  strip_trailing_cr = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case SUPPRESS_BLANK_EMPTY_OPTION:
Packit 33f14e
	  suppress_blank_empty = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case SUPPRESS_COMMON_LINES_OPTION:
Packit 33f14e
	  suppress_common_lines = true;
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case TABSIZE_OPTION:
Packit 33f14e
	  numval = strtoumax (optarg, &numend, 10);
Packit 33f14e
	  if (! (0 < numval && numval <= SIZE_MAX - GUTTER_WIDTH_MINIMUM)
Packit 33f14e
	      || *numend)
Packit 33f14e
	    try_help ("invalid tabsize '%s'", optarg);
Packit 33f14e
	  if (tabsize != numval)
Packit 33f14e
	    {
Packit 33f14e
	      if (tabsize)
Packit 33f14e
		fatal ("conflicting tabsize options");
Packit 33f14e
	      tabsize = numval;
Packit 33f14e
	    }
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case TO_FILE_OPTION:
Packit 33f14e
	  specify_value (&to_file, optarg, "--to-file");
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case UNCHANGED_LINE_FORMAT_OPTION:
Packit 33f14e
	case OLD_LINE_FORMAT_OPTION:
Packit 33f14e
	case NEW_LINE_FORMAT_OPTION:
Packit 33f14e
	  specify_style (OUTPUT_IFDEF);
Packit 33f14e
	  c -= UNCHANGED_LINE_FORMAT_OPTION;
Packit 33f14e
	  specify_value (&line_format[c], optarg, line_format_option[c]);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case UNCHANGED_GROUP_FORMAT_OPTION:
Packit 33f14e
	case OLD_GROUP_FORMAT_OPTION:
Packit 33f14e
	case NEW_GROUP_FORMAT_OPTION:
Packit 33f14e
	case CHANGED_GROUP_FORMAT_OPTION:
Packit 33f14e
	  specify_style (OUTPUT_IFDEF);
Packit 33f14e
	  c -= UNCHANGED_GROUP_FORMAT_OPTION;
Packit 33f14e
	  specify_value (&group_format[c], optarg, group_format_option[c]);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case COLOR_OPTION:
Packit 33f14e
	  specify_colors_style (optarg);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
	case COLOR_PALETTE_OPTION:
Packit 33f14e
	  set_color_palette (optarg);
Packit 33f14e
	  break;
Packit 33f14e
Packit 33f14e
        case PRESUME_OUTPUT_TTY_OPTION:
Packit 33f14e
          presume_output_tty = true;
Packit 33f14e
          break;
Packit 33f14e
Packit 33f14e
	default:
Packit 33f14e
	  try_help (NULL, NULL);
Packit 33f14e
	}
Packit 33f14e
      prev = c;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (colors_style == AUTO)
Packit 33f14e
    {
Packit 33f14e
      char const *t = getenv ("TERM");
Packit 33f14e
      if (t && STREQ (t, "dumb"))
Packit 33f14e
        colors_style = NEVER;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (output_style == OUTPUT_UNSPECIFIED)
Packit 33f14e
    {
Packit 33f14e
      if (show_c_function)
Packit 33f14e
	{
Packit 33f14e
	  specify_style (OUTPUT_CONTEXT);
Packit 33f14e
	  if (ocontext < 0)
Packit 33f14e
	    context = 3;
Packit 33f14e
	}
Packit 33f14e
      else
Packit 33f14e
	specify_style (OUTPUT_NORMAL);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
Packit 33f14e
    {
Packit 33f14e
#if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
Packit 33f14e
     || defined HAVE_STRUCT_STAT_ST_SPARE1)
Packit 33f14e
      time_format = "%Y-%m-%d %H:%M:%S.%N %z";
Packit 33f14e
#else
Packit 33f14e
      time_format = "%Y-%m-%d %H:%M:%S %z";
Packit 33f14e
#endif
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      /* See POSIX 1003.1-2001 for this format.  */
Packit 33f14e
      time_format = "%a %b %e %T %Y";
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (0 <= ocontext
Packit 33f14e
      && (output_style == OUTPUT_CONTEXT
Packit 33f14e
	  || output_style == OUTPUT_UNIFIED)
Packit 33f14e
      && (context < ocontext
Packit 33f14e
	  || (ocontext < context && ! explicit_context)))
Packit 33f14e
    context = ocontext;
Packit 33f14e
Packit 33f14e
  if (! tabsize)
Packit 33f14e
    tabsize = 8;
Packit 33f14e
  if (! width)
Packit 33f14e
    width = 130;
Packit 33f14e
Packit 33f14e
  {
Packit 33f14e
    /* Maximize first the half line width, and then the gutter width,
Packit 33f14e
       according to the following constraints:
Packit 33f14e
Packit 33f14e
	1.  Two half lines plus a gutter must fit in a line.
Packit 33f14e
	2.  If the half line width is nonzero:
Packit 33f14e
	    a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
Packit 33f14e
	    b.  If tabs are not expanded to spaces,
Packit 33f14e
		a half line plus a gutter is an integral number of tabs,
Packit 33f14e
		so that tabs in the right column line up.  */
Packit 33f14e
Packit 33f14e
    size_t t = expand_tabs ? 1 : tabsize;
Packit 33f14e
    size_t w = width;
Packit 33f14e
    size_t t_plus_g = t + GUTTER_WIDTH_MINIMUM;
Packit 33f14e
    size_t unaligned_off = (w >> 1) + (t_plus_g >> 1) + (w & t_plus_g & 1);
Packit 33f14e
    size_t off = unaligned_off - unaligned_off % t;
Packit 33f14e
    sdiff_half_width = (off <= GUTTER_WIDTH_MINIMUM || w <= off
Packit 33f14e
			? 0
Packit 33f14e
			: MIN (off - GUTTER_WIDTH_MINIMUM, w - off));
Packit 33f14e
    sdiff_column2_offset = sdiff_half_width ? off : w;
Packit 33f14e
  }
Packit 33f14e
Packit 33f14e
  /* Make the horizon at least as large as the context, so that
Packit 33f14e
     shift_boundaries has more freedom to shift the first and last hunks.  */
Packit 33f14e
  if (horizon_lines < context)
Packit 33f14e
    horizon_lines = context;
Packit 33f14e
Packit 33f14e
  summarize_regexp_list (&function_regexp_list);
Packit 33f14e
  summarize_regexp_list (&ignore_regexp_list);
Packit 33f14e
Packit 33f14e
  if (output_style == OUTPUT_IFDEF)
Packit 33f14e
    {
Packit 33f14e
      for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
Packit 33f14e
	if (!line_format[i])
Packit 33f14e
	  line_format[i] = "%l\n";
Packit 33f14e
      if (!group_format[OLD])
Packit 33f14e
	group_format[OLD]
Packit 33f14e
	  = group_format[CHANGED] ? group_format[CHANGED] : "%<";
Packit 33f14e
      if (!group_format[NEW])
Packit 33f14e
	group_format[NEW]
Packit 33f14e
	  = group_format[CHANGED] ? group_format[CHANGED] : "%>";
Packit 33f14e
      if (!group_format[UNCHANGED])
Packit 33f14e
	group_format[UNCHANGED] = "%=";
Packit 33f14e
      if (!group_format[CHANGED])
Packit 33f14e
	group_format[CHANGED] = concat (group_format[OLD],
Packit 33f14e
					group_format[NEW], "");
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  no_diff_means_no_output =
Packit 33f14e
    (output_style == OUTPUT_IFDEF ?
Packit 33f14e
      (!*group_format[UNCHANGED]
Packit 33f14e
       || (STREQ (group_format[UNCHANGED], "%=")
Packit 33f14e
	   && !*line_format[UNCHANGED]))
Packit 33f14e
     : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
Packit 33f14e
Packit 33f14e
  files_can_be_treated_as_binary =
Packit 33f14e
    (brief & binary
Packit 33f14e
     & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
Packit 33f14e
	  | (ignore_regexp_list.regexps || ignore_white_space)));
Packit 33f14e
Packit 33f14e
  switch_string = option_list (argv + 1, optind - 1);
Packit 33f14e
Packit 33f14e
  if (from_file)
Packit 33f14e
    {
Packit 33f14e
      if (to_file)
Packit 33f14e
	fatal ("--from-file and --to-file both specified");
Packit 33f14e
      else
Packit 33f14e
	for (; optind < argc; optind++)
Packit 33f14e
	  {
Packit 33f14e
	    int status = compare_files (NULL, from_file, argv[optind]);
Packit 33f14e
	    if (exit_status < status)
Packit 33f14e
	      exit_status = status;
Packit 33f14e
	  }
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      if (to_file)
Packit 33f14e
	for (; optind < argc; optind++)
Packit 33f14e
	  {
Packit 33f14e
	    int status = compare_files (NULL, argv[optind], to_file);
Packit 33f14e
	    if (exit_status < status)
Packit 33f14e
	      exit_status = status;
Packit 33f14e
	  }
Packit 33f14e
      else
Packit 33f14e
	{
Packit 33f14e
	  if (argc - optind != 2)
Packit 33f14e
	    {
Packit 33f14e
	      if (argc - optind < 2)
Packit 33f14e
		try_help ("missing operand after '%s'", argv[argc - 1]);
Packit 33f14e
	      else
Packit 33f14e
		try_help ("extra operand '%s'", argv[optind + 2]);
Packit 33f14e
	    }
Packit 33f14e
Packit 33f14e
	  exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Print any messages that were saved up for last.  */
Packit 33f14e
  print_message_queue ();
Packit 33f14e
Packit 33f14e
  check_stdout ();
Packit 33f14e
  exit (exit_status);
Packit 33f14e
  return exit_status;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Append to REGLIST the regexp PATTERN.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
add_regexp (struct regexp_list *reglist, char const *pattern)
Packit 33f14e
{
Packit 33f14e
  size_t patlen = strlen (pattern);
Packit 33f14e
  char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
Packit 33f14e
Packit 33f14e
  if (m != 0)
Packit 33f14e
    error (EXIT_TROUBLE, 0, "%s: %s", pattern, m);
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      char *regexps = reglist->regexps;
Packit 33f14e
      size_t len = reglist->len;
Packit 33f14e
      bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
Packit 33f14e
      size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
Packit 33f14e
      size_t size = reglist->size;
Packit 33f14e
Packit 33f14e
      if (size <= newlen)
Packit 33f14e
	{
Packit 33f14e
	  if (!size)
Packit 33f14e
	    size = 1;
Packit 33f14e
Packit 33f14e
	  do size *= 2;
Packit 33f14e
	  while (size <= newlen);
Packit 33f14e
Packit 33f14e
	  reglist->size = size;
Packit 33f14e
	  reglist->regexps = regexps = xrealloc (regexps, size);
Packit 33f14e
	}
Packit 33f14e
      if (multiple_regexps)
Packit 33f14e
	{
Packit 33f14e
	  regexps[len++] = '\\';
Packit 33f14e
	  regexps[len++] = '|';
Packit 33f14e
	}
Packit 33f14e
      memcpy (regexps + len, pattern, patlen + 1);
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Ensure that REGLIST represents the disjunction of its regexps.
Packit 33f14e
   This is done here, rather than earlier, to avoid O(N^2) behavior.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
summarize_regexp_list (struct regexp_list *reglist)
Packit 33f14e
{
Packit 33f14e
  if (reglist->regexps)
Packit 33f14e
    {
Packit 33f14e
      /* At least one regexp was specified.  Allocate a fastmap for it.  */
Packit 33f14e
      reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
Packit 33f14e
      if (reglist->multiple_regexps)
Packit 33f14e
	{
Packit 33f14e
	  /* Compile the disjunction of the regexps.
Packit 33f14e
	     (If just one regexp was specified, it is already compiled.)  */
Packit 33f14e
	  char const *m = re_compile_pattern (reglist->regexps, reglist->len,
Packit 33f14e
					      reglist->buf);
Packit 33f14e
	  if (m)
Packit 33f14e
	    die (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
try_help (char const *reason_msgid, char const *operand)
Packit 33f14e
{
Packit 33f14e
  if (reason_msgid)
Packit 33f14e
    error (0, 0, _(reason_msgid), operand);
Packit 33f14e
  die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
Packit 33f14e
	 program_name);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
check_stdout (void)
Packit 33f14e
{
Packit 33f14e
  if (ferror (stdout))
Packit 33f14e
    fatal ("write failed");
Packit 33f14e
  else if (fclose (stdout) != 0)
Packit 33f14e
    pfatal_with_name (_("standard output"));
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static char const * const option_help_msgid[] = {
Packit 33f14e
  N_("    --normal                  output a normal diff (the default)"),
Packit 33f14e
  N_("-q, --brief                   report only when files differ"),
Packit 33f14e
  N_("-s, --report-identical-files  report when two files are the same"),
Packit 33f14e
  N_("-c, -C NUM, --context[=NUM]   output NUM (default 3) lines of copied context"),
Packit 33f14e
  N_("-u, -U NUM, --unified[=NUM]   output NUM (default 3) lines of unified context"),
Packit 33f14e
  N_("-e, --ed                      output an ed script"),
Packit 33f14e
  N_("-n, --rcs                     output an RCS format diff"),
Packit 33f14e
  N_("-y, --side-by-side            output in two columns"),
Packit 33f14e
  N_("-W, --width=NUM               output at most NUM (default 130) print columns"),
Packit 33f14e
  N_("    --left-column             output only the left column of common lines"),
Packit 33f14e
  N_("    --suppress-common-lines   do not output common lines"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-p, --show-c-function         show which C function each change is in"),
Packit 33f14e
  N_("-F, --show-function-line=RE   show the most recent line matching RE"),
Packit 33f14e
  N_("    --label LABEL             use LABEL instead of file name and timestamp\n"
Packit 33f14e
     "                                (can be repeated)"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-t, --expand-tabs             expand tabs to spaces in output"),
Packit 33f14e
  N_("-T, --initial-tab             make tabs line up by prepending a tab"),
Packit 33f14e
  N_("    --tabsize=NUM             tab stops every NUM (default 8) print columns"),
Packit 33f14e
  N_("    --suppress-blank-empty    suppress space or tab before empty output lines"),
Packit 33f14e
  N_("-l, --paginate                pass output through 'pr' to paginate it"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-r, --recursive                 recursively compare any subdirectories found"),
Packit 33f14e
  N_("    --no-dereference            don't follow symbolic links"),
Packit 33f14e
  N_("-N, --new-file                  treat absent files as empty"),
Packit 33f14e
  N_("    --unidirectional-new-file   treat absent first files as empty"),
Packit 33f14e
  N_("    --ignore-file-name-case     ignore case when comparing file names"),
Packit 33f14e
  N_("    --no-ignore-file-name-case  consider case when comparing file names"),
Packit 33f14e
  N_("-x, --exclude=PAT               exclude files that match PAT"),
Packit 33f14e
  N_("-X, --exclude-from=FILE         exclude files that match any pattern in FILE"),
Packit 33f14e
  N_("-S, --starting-file=FILE        start with FILE when comparing directories"),
Packit 33f14e
  N_("    --from-file=FILE1           compare FILE1 to all operands;\n"
Packit 33f14e
     "                                  FILE1 can be a directory"),
Packit 33f14e
  N_("    --to-file=FILE2             compare all operands to FILE2;\n"
Packit 33f14e
     "                                  FILE2 can be a directory"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-i, --ignore-case               ignore case differences in file contents"),
Packit 33f14e
  N_("-E, --ignore-tab-expansion      ignore changes due to tab expansion"),
Packit 33f14e
  N_("-Z, --ignore-trailing-space     ignore white space at line end"),
Packit 33f14e
  N_("-b, --ignore-space-change       ignore changes in the amount of white space"),
Packit 33f14e
  N_("-w, --ignore-all-space          ignore all white space"),
Packit 33f14e
  N_("-B, --ignore-blank-lines        ignore changes where lines are all blank"),
Packit 33f14e
  N_("-I, --ignore-matching-lines=RE  ignore changes where all lines match RE"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-a, --text                      treat all files as text"),
Packit 33f14e
  N_("    --strip-trailing-cr         strip trailing carriage return on input"),
Packit 33f14e
#if O_BINARY
Packit 33f14e
  N_("    --binary                    read and write data in binary mode"),
Packit 33f14e
#endif
Packit 33f14e
  "",
Packit 33f14e
  N_("-D, --ifdef=NAME                output merged file with '#ifdef NAME' diffs"),
Packit 33f14e
  N_("    --GTYPE-group-format=GFMT   format GTYPE input groups with GFMT"),
Packit 33f14e
  N_("    --line-format=LFMT          format all input lines with LFMT"),
Packit 33f14e
  N_("    --LTYPE-line-format=LFMT    format LTYPE input lines with LFMT"),
Packit 33f14e
  N_("  These format options provide fine-grained control over the output\n"
Packit 33f14e
     "    of diff, generalizing -D/--ifdef."),
Packit 33f14e
  N_("  LTYPE is 'old', 'new', or 'unchanged'.  GTYPE is LTYPE or 'changed'."),
Packit 33f14e
  N_("  GFMT (only) may contain:\n\
Packit 33f14e
    %<  lines from FILE1\n\
Packit 33f14e
    %>  lines from FILE2\n\
Packit 33f14e
    %=  lines common to FILE1 and FILE2\n\
Packit 33f14e
    %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER\n\
Packit 33f14e
      LETTERs are as follows for new group, lower case for old group:\n\
Packit 33f14e
        F  first line number\n\
Packit 33f14e
        L  last line number\n\
Packit 33f14e
        N  number of lines = L-F+1\n\
Packit 33f14e
        E  F-1\n\
Packit 33f14e
        M  L+1\n\
Packit 33f14e
    %(A=B?T:E)  if A equals B then T else E"),
Packit 33f14e
  N_("  LFMT (only) may contain:\n\
Packit 33f14e
    %L  contents of line\n\
Packit 33f14e
    %l  contents of line, excluding any trailing newline\n\
Packit 33f14e
    %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number"),
Packit 33f14e
  N_("  Both GFMT and LFMT may contain:\n\
Packit 33f14e
    %%  %\n\
Packit 33f14e
    %c'C'  the single character C\n\
Packit 33f14e
    %c'\\OOO'  the character with octal code OOO\n\
Packit 33f14e
    C    the character C (other characters represent themselves)"),
Packit 33f14e
  "",
Packit 33f14e
  N_("-d, --minimal            try hard to find a smaller set of changes"),
Packit 33f14e
  N_("    --horizon-lines=NUM  keep NUM lines of the common prefix and suffix"),
Packit 33f14e
  N_("    --speed-large-files  assume large files and many scattered small changes"),
Packit 33f14e
  N_("    --color[=WHEN]       colorize the output; WHEN can be 'never', 'always',\n"
Packit 33f14e
     "                           or 'auto' (the default)"),
Packit 33f14e
  N_("    --palette=PALETTE    the colors to use when --color is active; PALETTE is\n"
Packit 33f14e
     "                           a colon-separated list of terminfo capabilities"),
Packit 33f14e
  "",
Packit 33f14e
  N_("    --help               display this help and exit"),
Packit 33f14e
  N_("-v, --version            output version information and exit"),
Packit 33f14e
  "",
Packit 33f14e
  N_("FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'."),
Packit 33f14e
  N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
Packit 33f14e
  N_("If a FILE is '-', read standard input."),
Packit 33f14e
  N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
Packit 33f14e
  0
Packit 33f14e
};
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
usage (void)
Packit 33f14e
{
Packit 33f14e
  char const * const *p;
Packit 33f14e
Packit 33f14e
  printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
Packit 33f14e
  printf ("%s\n\n", _("Compare FILES line by line."));
Packit 33f14e
Packit 33f14e
  fputs (_("\
Packit 33f14e
Mandatory arguments to long options are mandatory for short options too.\n\
Packit 33f14e
"), stdout);
Packit 33f14e
Packit 33f14e
  for (p = option_help_msgid;  *p;  p++)
Packit 33f14e
    {
Packit 33f14e
      if (!**p)
Packit 33f14e
	putchar ('\n');
Packit 33f14e
      else
Packit 33f14e
	{
Packit 33f14e
	  char const *msg = _(*p);
Packit 33f14e
	  char const *nl;
Packit 33f14e
	  while ((nl = strchr (msg, '\n')))
Packit 33f14e
	    {
Packit 33f14e
	      int msglen = nl + 1 - msg;
Packit 33f14e
	      printf ("  %.*s", msglen, msg);
Packit 33f14e
	      msg = nl + 1;
Packit 33f14e
	    }
Packit 33f14e
Packit 33f14e
	  printf ("  %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
  emit_bug_reporting_address ();
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Set VAR to VALUE, reporting an OPTION error if this is a
Packit 33f14e
   conflict.  */
Packit 33f14e
static void
Packit 33f14e
specify_value (char const **var, char const *value, char const *option)
Packit 33f14e
{
Packit 33f14e
  if (*var && ! STREQ (*var, value))
Packit 33f14e
    {
Packit 33f14e
      error (0, 0, _("conflicting %s option value '%s'"), option, value);
Packit 33f14e
      try_help (NULL, NULL);
Packit 33f14e
    }
Packit 33f14e
  *var = value;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Set the output style to STYLE, diagnosing conflicts.  */
Packit 33f14e
static void
Packit 33f14e
specify_style (enum output_style style)
Packit 33f14e
{
Packit 33f14e
  if (output_style != style)
Packit 33f14e
    {
Packit 33f14e
      if (output_style != OUTPUT_UNSPECIFIED)
Packit 33f14e
	try_help ("conflicting output style options", NULL);
Packit 33f14e
      output_style = style;
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Set the color mode.  */
Packit 33f14e
static void
Packit 33f14e
specify_colors_style (char const *value)
Packit 33f14e
{
Packit 33f14e
  if (value == NULL || STREQ (value, "auto"))
Packit 33f14e
    colors_style = AUTO;
Packit 33f14e
  else if (STREQ (value, "always"))
Packit 33f14e
    colors_style = ALWAYS;
Packit 33f14e
  else if (STREQ (value, "never"))
Packit 33f14e
    colors_style = NEVER;
Packit 33f14e
  else
Packit 33f14e
    try_help ("invalid color '%s'", value);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e

Packit 33f14e
/* Set the last-modified time of *ST to be the current time.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
set_mtime_to_now (struct stat *st)
Packit 33f14e
{
Packit 33f14e
#ifdef STAT_TIMESPEC
Packit 33f14e
  gettime (&STAT_TIMESPEC (st, st_mtim));
Packit 33f14e
#else
Packit 33f14e
  struct timespec t;
Packit 33f14e
  gettime (&t);
Packit 33f14e
  st->st_mtime = t.tv_sec;
Packit 33f14e
# if defined STAT_TIMESPEC_NS
Packit 33f14e
  STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
Packit 33f14e
# elif defined HAVE_STRUCT_STAT_ST_SPARE1
Packit 33f14e
  st->st_spare1 = t.tv_nsec / 1000;
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
}
Packit 33f14e

Packit 33f14e
/* Compare two files (or dirs) with parent comparison PARENT
Packit 33f14e
   and names NAME0 and NAME1.
Packit 33f14e
   (If PARENT is null, then the first name is just NAME0, etc.)
Packit 33f14e
   This is self-contained; it opens the files and closes them.
Packit 33f14e
Packit 33f14e
   Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
Packit 33f14e
   different, EXIT_TROUBLE if there is a problem opening them.  */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
compare_files (struct comparison const *parent,
Packit 33f14e
	       char const *name0,
Packit 33f14e
	       char const *name1)
Packit 33f14e
{
Packit 33f14e
  struct comparison cmp;
Packit 33f14e
#define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
Packit 33f14e
  register int f;
Packit 33f14e
  int status = EXIT_SUCCESS;
Packit 33f14e
  bool same_files;
Packit 33f14e
  char *free0;
Packit 33f14e
  char *free1;
Packit 33f14e
Packit 33f14e
  /* If this is directory comparison, perhaps we have a file
Packit 33f14e
     that exists only in one of the directories.
Packit 33f14e
     If so, just print a message to that effect.  */
Packit 33f14e
Packit 33f14e
  if (! ((name0 && name1)
Packit 33f14e
	 || (unidirectional_new_file && name1)
Packit 33f14e
	 || new_file))
Packit 33f14e
    {
Packit 33f14e
      char const *name = name0 ? name0 : name1;
Packit 33f14e
      char const *dir = parent->file[!name0].name;
Packit 33f14e
Packit 33f14e
      /* See POSIX 1003.1-2001 for this format.  */
Packit 33f14e
      message ("Only in %s: %s\n", dir, name);
Packit 33f14e
Packit 33f14e
      /* Return EXIT_FAILURE so that diff_dirs will return
Packit 33f14e
	 EXIT_FAILURE ("some files differ").  */
Packit 33f14e
      return EXIT_FAILURE;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  memset (cmp.file, 0, sizeof cmp.file);
Packit 33f14e
  cmp.parent = parent;
Packit 33f14e
Packit 33f14e
  /* cmp.file[f].desc markers */
Packit 33f14e
#define NONEXISTENT (-1) /* nonexistent file */
Packit 33f14e
#define UNOPENED (-2) /* unopened file (e.g. directory) */
Packit 33f14e
#define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
Packit 33f14e
Packit 33f14e
#define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
Packit 33f14e
Packit 33f14e
  cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
Packit 33f14e
  cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
Packit 33f14e
Packit 33f14e
  /* Now record the full name of each file, including nonexistent ones.  */
Packit 33f14e
Packit 33f14e
  if (!name0)
Packit 33f14e
    name0 = name1;
Packit 33f14e
  if (!name1)
Packit 33f14e
    name1 = name0;
Packit 33f14e
Packit 33f14e
  if (!parent)
Packit 33f14e
    {
Packit 33f14e
      free0 = NULL;
Packit 33f14e
      free1 = NULL;
Packit 33f14e
      cmp.file[0].name = name0;
Packit 33f14e
      cmp.file[1].name = name1;
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      cmp.file[0].name = free0
Packit 33f14e
	= file_name_concat (parent->file[0].name, name0, NULL);
Packit 33f14e
      cmp.file[1].name = free1
Packit 33f14e
	= file_name_concat (parent->file[1].name, name1, NULL);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Stat the files.  */
Packit 33f14e
Packit 33f14e
  for (f = 0; f < 2; f++)
Packit 33f14e
    {
Packit 33f14e
      if (cmp.file[f].desc != NONEXISTENT)
Packit 33f14e
	{
Packit 33f14e
	  if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
Packit 33f14e
	    {
Packit 33f14e
	      cmp.file[f].desc = cmp.file[0].desc;
Packit 33f14e
	      cmp.file[f].stat = cmp.file[0].stat;
Packit 33f14e
	    }
Packit 33f14e
	  else if (STREQ (cmp.file[f].name, "-"))
Packit 33f14e
	    {
Packit 33f14e
	      cmp.file[f].desc = STDIN_FILENO;
Packit 33f14e
	      if (binary && ! isatty (STDIN_FILENO))
Packit 33f14e
		set_binary_mode (STDIN_FILENO, O_BINARY);
Packit 33f14e
	      if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
Packit 33f14e
		cmp.file[f].desc = ERRNO_ENCODE (errno);
Packit 33f14e
	      else
Packit 33f14e
		{
Packit 33f14e
		  if (S_ISREG (cmp.file[f].stat.st_mode))
Packit 33f14e
		    {
Packit 33f14e
		      off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
Packit 33f14e
		      if (pos < 0)
Packit 33f14e
			cmp.file[f].desc = ERRNO_ENCODE (errno);
Packit 33f14e
		      else
Packit 33f14e
			cmp.file[f].stat.st_size =
Packit 33f14e
			  MAX (0, cmp.file[f].stat.st_size - pos);
Packit 33f14e
		    }
Packit 33f14e
Packit 33f14e
		  /* POSIX 1003.1-2001 requires current time for
Packit 33f14e
		     stdin.  */
Packit 33f14e
		  set_mtime_to_now (&cmp.file[f].stat);
Packit 33f14e
		}
Packit 33f14e
	    }
Packit 33f14e
	  else if ((no_dereference_symlinks
Packit 33f14e
		    ? lstat (cmp.file[f].name, &cmp.file[f].stat)
Packit 33f14e
		    : stat (cmp.file[f].name, &cmp.file[f].stat))
Packit 33f14e
		   != 0)
Packit 33f14e
	    cmp.file[f].desc = ERRNO_ENCODE (errno);
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Mark files as nonexistent as needed for -N and -P, if they are
Packit 33f14e
     inaccessible empty regular files (the kind of files that 'patch'
Packit 33f14e
     creates to indicate nonexistent backups), or if they are
Packit 33f14e
     top-level files that do not exist but their counterparts do
Packit 33f14e
     exist.  */
Packit 33f14e
  for (f = 0; f < 2; f++)
Packit 33f14e
    if ((new_file || (f == 0 && unidirectional_new_file))
Packit 33f14e
	&& (cmp.file[f].desc == UNOPENED
Packit 33f14e
	    ? (S_ISREG (cmp.file[f].stat.st_mode)
Packit 33f14e
	       && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
Packit 33f14e
	       && cmp.file[f].stat.st_size == 0)
Packit 33f14e
	    : ((cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
Packit 33f14e
		|| cmp.file[f].desc == ERRNO_ENCODE (EBADF))
Packit 33f14e
	       && ! parent
Packit 33f14e
	       && (cmp.file[1 - f].desc == UNOPENED
Packit 33f14e
		   || cmp.file[1 - f].desc == STDIN_FILENO))))
Packit 33f14e
      cmp.file[f].desc = NONEXISTENT;
Packit 33f14e
Packit 33f14e
  for (f = 0; f < 2; f++)
Packit 33f14e
    if (cmp.file[f].desc == NONEXISTENT)
Packit 33f14e
      {
Packit 33f14e
	memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
Packit 33f14e
	cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
Packit 33f14e
      }
Packit 33f14e
Packit 33f14e
  for (f = 0; f < 2; f++)
Packit 33f14e
    {
Packit 33f14e
      int e = ERRNO_DECODE (cmp.file[f].desc);
Packit 33f14e
      if (0 <= e)
Packit 33f14e
	{
Packit 33f14e
	  errno = e;
Packit 33f14e
	  perror_with_name (cmp.file[f].name);
Packit 33f14e
	  status = EXIT_TROUBLE;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
Packit 33f14e
    {
Packit 33f14e
      /* If one is a directory, and it was specified in the command line,
Packit 33f14e
	 use the file in that dir with the other file's basename.  */
Packit 33f14e
Packit 33f14e
      int fnm_arg = DIR_P (0);
Packit 33f14e
      int dir_arg = 1 - fnm_arg;
Packit 33f14e
      char const *fnm = cmp.file[fnm_arg].name;
Packit 33f14e
      char const *dir = cmp.file[dir_arg].name;
Packit 33f14e
      char const *filename = cmp.file[dir_arg].name = free0
Packit 33f14e
	= find_dir_file_pathname (dir, last_component (fnm));
Packit 33f14e
Packit 33f14e
      if (STREQ (fnm, "-"))
Packit 33f14e
	fatal ("cannot compare '-' to a directory");
Packit 33f14e
Packit 33f14e
      if ((no_dereference_symlinks
Packit 33f14e
	   ? lstat (filename, &cmp.file[dir_arg].stat)
Packit 33f14e
	   : stat (filename, &cmp.file[dir_arg].stat))
Packit 33f14e
	  != 0)
Packit 33f14e
	{
Packit 33f14e
	  perror_with_name (filename);
Packit 33f14e
	  status = EXIT_TROUBLE;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (status != EXIT_SUCCESS)
Packit 33f14e
    {
Packit 33f14e
      /* One of the files should exist but does not.  */
Packit 33f14e
    }
Packit 33f14e
  else if (cmp.file[0].desc == NONEXISTENT
Packit 33f14e
	   && cmp.file[1].desc == NONEXISTENT)
Packit 33f14e
    {
Packit 33f14e
      /* Neither file "exists", so there's nothing to compare.  */
Packit 33f14e
    }
Packit 33f14e
  else if ((same_files
Packit 33f14e
	    = (cmp.file[0].desc != NONEXISTENT
Packit 33f14e
	       && cmp.file[1].desc != NONEXISTENT
Packit 33f14e
	       && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
Packit 33f14e
	       && same_file_attributes (&cmp.file[0].stat,
Packit 33f14e
					&cmp.file[1].stat)))
Packit 33f14e
	   && no_diff_means_no_output)
Packit 33f14e
    {
Packit 33f14e
      /* The two named files are actually the same physical file.
Packit 33f14e
	 We know they are identical without actually reading them.  */
Packit 33f14e
    }
Packit 33f14e
  else if (DIR_P (0) & DIR_P (1))
Packit 33f14e
    {
Packit 33f14e
      if (output_style == OUTPUT_IFDEF)
Packit 33f14e
	fatal ("-D option not supported with directories");
Packit 33f14e
Packit 33f14e
      /* If both are directories, compare the files in them.  */
Packit 33f14e
Packit 33f14e
      if (parent && !recursive)
Packit 33f14e
	{
Packit 33f14e
	  /* But don't compare dir contents one level down
Packit 33f14e
	     unless -r was specified.
Packit 33f14e
	     See POSIX 1003.1-2001 for this format.  */
Packit 33f14e
	  message ("Common subdirectories: %s and %s\n",
Packit 33f14e
		   cmp.file[0].name, cmp.file[1].name);
Packit 33f14e
	}
Packit 33f14e
      else
Packit 33f14e
	status = diff_dirs (&cmp, compare_files);
Packit 33f14e
    }
Packit 33f14e
  else if ((DIR_P (0) | DIR_P (1))
Packit 33f14e
	   || (parent
Packit 33f14e
	       && !((S_ISREG (cmp.file[0].stat.st_mode)
Packit 33f14e
		     || S_ISLNK (cmp.file[0].stat.st_mode))
Packit 33f14e
		    && (S_ISREG (cmp.file[1].stat.st_mode)
Packit 33f14e
			|| S_ISLNK  (cmp.file[1].stat.st_mode)))))
Packit 33f14e
    {
Packit 33f14e
      if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
Packit 33f14e
	{
Packit 33f14e
	  /* We have a subdirectory that exists only in one directory.  */
Packit 33f14e
Packit 33f14e
	  if ((DIR_P (0) | DIR_P (1))
Packit 33f14e
	      && recursive
Packit 33f14e
	      && (new_file
Packit 33f14e
		  || (unidirectional_new_file
Packit 33f14e
		      && cmp.file[0].desc == NONEXISTENT)))
Packit 33f14e
	    status = diff_dirs (&cmp, compare_files);
Packit 33f14e
	  else
Packit 33f14e
	    {
Packit 33f14e
	      char const *dir;
Packit 33f14e
Packit 33f14e
	      /* PARENT must be non-NULL here.  */
Packit 33f14e
	      assert (parent);
Packit 33f14e
	      dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
Packit 33f14e
Packit 33f14e
	      /* See POSIX 1003.1-2001 for this format.  */
Packit 33f14e
	      message ("Only in %s: %s\n", dir, name0);
Packit 33f14e
Packit 33f14e
	      status = EXIT_FAILURE;
Packit 33f14e
	    }
Packit 33f14e
	}
Packit 33f14e
      else
Packit 33f14e
	{
Packit 33f14e
	  /* We have two files that are not to be compared.  */
Packit 33f14e
Packit 33f14e
	  /* See POSIX 1003.1-2001 for this format.  */
Packit 33f14e
	  message5 ("File %s is a %s while file %s is a %s\n",
Packit 33f14e
		    file_label[0] ? file_label[0] : cmp.file[0].name,
Packit 33f14e
		    file_type (&cmp.file[0].stat),
Packit 33f14e
		    file_label[1] ? file_label[1] : cmp.file[1].name,
Packit 33f14e
		    file_type (&cmp.file[1].stat));
Packit 33f14e
Packit 33f14e
	  /* This is a difference.  */
Packit 33f14e
	  status = EXIT_FAILURE;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
  else if (S_ISLNK (cmp.file[0].stat.st_mode)
Packit 33f14e
	   || S_ISLNK (cmp.file[1].stat.st_mode))
Packit 33f14e
    {
Packit 33f14e
      /* We get here only if we use lstat(), not stat().  */
Packit 33f14e
      assert (no_dereference_symlinks);
Packit 33f14e
Packit 33f14e
      if (S_ISLNK (cmp.file[0].stat.st_mode)
Packit 33f14e
	  && S_ISLNK (cmp.file[1].stat.st_mode))
Packit 33f14e
	{
Packit 33f14e
	  /* Compare the values of the symbolic links.  */
Packit 33f14e
	  char *link_value[2] = { NULL, NULL };
Packit 33f14e
Packit 33f14e
	  for (f = 0; f < 2; f++)
Packit 33f14e
	    {
Packit 33f14e
	      link_value[f] = xreadlink (cmp.file[f].name);
Packit 33f14e
	      if (link_value[f] == NULL)
Packit 33f14e
		{
Packit 33f14e
		  perror_with_name (cmp.file[f].name);
Packit 33f14e
		  status = EXIT_TROUBLE;
Packit 33f14e
		  break;
Packit 33f14e
		}
Packit 33f14e
	    }
Packit 33f14e
	  if (status == EXIT_SUCCESS)
Packit 33f14e
	    {
Packit 33f14e
	      if ( ! STREQ (link_value[0], link_value[1]))
Packit 33f14e
		{
Packit 33f14e
		  message ("Symbolic links %s and %s differ\n",
Packit 33f14e
			   cmp.file[0].name, cmp.file[1].name);
Packit 33f14e
		  /* This is a difference.  */
Packit 33f14e
		  status = EXIT_FAILURE;
Packit 33f14e
		}
Packit 33f14e
	    }
Packit 33f14e
	  for (f = 0; f < 2; f++)
Packit 33f14e
	    free (link_value[f]);
Packit 33f14e
	}
Packit 33f14e
      else
Packit 33f14e
	{
Packit 33f14e
	  /* We have two files that are not to be compared, because
Packit 33f14e
	     one of them is a symbolic link and the other one is not.  */
Packit 33f14e
Packit 33f14e
	  message5 ("File %s is a %s while file %s is a %s\n",
Packit 33f14e
		    file_label[0] ? file_label[0] : cmp.file[0].name,
Packit 33f14e
		    file_type (&cmp.file[0].stat),
Packit 33f14e
		    file_label[1] ? file_label[1] : cmp.file[1].name,
Packit 33f14e
		    file_type (&cmp.file[1].stat));
Packit 33f14e
Packit 33f14e
	  /* This is a difference.  */
Packit 33f14e
	  status = EXIT_FAILURE;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
  else if (files_can_be_treated_as_binary
Packit 33f14e
	   && S_ISREG (cmp.file[0].stat.st_mode)
Packit 33f14e
	   && S_ISREG (cmp.file[1].stat.st_mode)
Packit 33f14e
	   && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
Packit 33f14e
	   && 0 < cmp.file[0].stat.st_size
Packit 33f14e
	   && 0 < cmp.file[1].stat.st_size)
Packit 33f14e
    {
Packit 33f14e
      message ("Files %s and %s differ\n",
Packit 33f14e
	       file_label[0] ? file_label[0] : cmp.file[0].name,
Packit 33f14e
	       file_label[1] ? file_label[1] : cmp.file[1].name);
Packit 33f14e
      status = EXIT_FAILURE;
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      /* Both exist and neither is a directory.  */
Packit 33f14e
Packit 33f14e
      /* Open the files and record their descriptors.  */
Packit 33f14e
Packit 33f14e
      int oflags = O_RDONLY | (binary ? O_BINARY : 0);
Packit 33f14e
Packit 33f14e
      if (cmp.file[0].desc == UNOPENED)
Packit 33f14e
	if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
Packit 33f14e
	  {
Packit 33f14e
	    perror_with_name (cmp.file[0].name);
Packit 33f14e
	    status = EXIT_TROUBLE;
Packit 33f14e
	  }
Packit 33f14e
      if (cmp.file[1].desc == UNOPENED)
Packit 33f14e
	{
Packit 33f14e
	  if (same_files)
Packit 33f14e
	    cmp.file[1].desc = cmp.file[0].desc;
Packit 33f14e
	  else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
Packit 33f14e
	    {
Packit 33f14e
	      perror_with_name (cmp.file[1].name);
Packit 33f14e
	      status = EXIT_TROUBLE;
Packit 33f14e
	    }
Packit 33f14e
	}
Packit 33f14e
Packit 33f14e
      /* Compare the files, if no error was found.  */
Packit 33f14e
Packit 33f14e
      if (status == EXIT_SUCCESS)
Packit 33f14e
	status = diff_2_files (&cmp);
Packit 33f14e
Packit 33f14e
      /* Close the file descriptors.  */
Packit 33f14e
Packit 33f14e
      if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
Packit 33f14e
	{
Packit 33f14e
	  perror_with_name (cmp.file[0].name);
Packit 33f14e
	  status = EXIT_TROUBLE;
Packit 33f14e
	}
Packit 33f14e
      if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
Packit 33f14e
	  && close (cmp.file[1].desc) != 0)
Packit 33f14e
	{
Packit 33f14e
	  perror_with_name (cmp.file[1].name);
Packit 33f14e
	  status = EXIT_TROUBLE;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Now the comparison has been done, if no error prevented it,
Packit 33f14e
     and STATUS is the value this function will return.  */
Packit 33f14e
Packit 33f14e
  if (status == EXIT_SUCCESS)
Packit 33f14e
    {
Packit 33f14e
      if (report_identical_files && !DIR_P (0))
Packit 33f14e
	message ("Files %s and %s are identical\n",
Packit 33f14e
		 file_label[0] ? file_label[0] : cmp.file[0].name,
Packit 33f14e
		 file_label[1] ? file_label[1] : cmp.file[1].name);
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      /* Flush stdout so that the user sees differences immediately.
Packit 33f14e
	 This can hurt performance, unfortunately.  */
Packit 33f14e
      if (fflush (stdout) != 0)
Packit 33f14e
	pfatal_with_name (_("standard output"));
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  free (free0);
Packit 33f14e
  free (free1);
Packit 33f14e
Packit 33f14e
  return status;
Packit 33f14e
}