Blame src/cmp.c.cmp-s-empty

Packit f38335
/* cmp - compare two files byte by byte
Packit f38335
Packit f38335
   Copyright (C) 1990-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
Packit f38335
   2015-2017 Free Software Foundation, Inc.
Packit f38335
Packit f38335
   This program is free software: you can redistribute it and/or modify
Packit f38335
   it under the terms of the GNU General Public License as published by
Packit f38335
   the Free Software Foundation, either version 3 of the License, or
Packit f38335
   (at your option) any later version.
Packit f38335
Packit f38335
   This program is distributed in the hope that it will be useful,
Packit f38335
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit f38335
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit f38335
   GNU General Public License for more details.
Packit f38335
Packit f38335
   You should have received a copy of the GNU General Public License
Packit f38335
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit f38335

Packit f38335
#include "system.h"
Packit f38335
#include "paths.h"
Packit f38335
Packit f38335
#include <stdio.h>
Packit f38335
Packit f38335
#include <c-stack.h>
Packit f38335
#include <cmpbuf.h>
Packit f38335
#include "die.h"
Packit f38335
#include <error.h>
Packit f38335
#include <exitfail.h>
Packit f38335
#include <file-type.h>
Packit f38335
#include <getopt.h>
Packit f38335
#include <hard-locale.h>
Packit f38335
#include <inttostr.h>
Packit f38335
#include <progname.h>
Packit f38335
#include <unlocked-io.h>
Packit f38335
#include <version-etc.h>
Packit f38335
#include <xalloc.h>
Packit f38335
#include <binary-io.h>
Packit f38335
#include <xstrtol.h>
Packit f38335
Packit f38335
/* The official name of this program (e.g., no 'g' prefix).  */
Packit f38335
#define PROGRAM_NAME "cmp"
Packit f38335
Packit f38335
#define AUTHORS \
Packit f38335
  proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
Packit f38335
  proper_name ("David MacKenzie")
Packit f38335
Packit f38335
#if defined LC_MESSAGES && ENABLE_NLS
Packit f38335
# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES)
Packit f38335
#else
Packit f38335
# define hard_locale_LC_MESSAGES 0
Packit f38335
#endif
Packit f38335
Packit f38335
static int cmp (void);
Packit f38335
static off_t file_position (int);
Packit f38335
static size_t block_compare (word const *, word const *) _GL_ATTRIBUTE_PURE;
Packit f38335
static size_t count_newlines (char *, size_t);
Packit f38335
static void sprintc (char *, unsigned char);
Packit f38335
Packit f38335
/* Filenames of the compared files.  */
Packit f38335
static char const *file[2];
Packit f38335
Packit f38335
/* File descriptors of the files.  */
Packit f38335
static int file_desc[2];
Packit f38335
Packit f38335
/* Status of the files.  */
Packit f38335
static struct stat stat_buf[2];
Packit f38335
Packit f38335
/* Read buffers for the files.  */
Packit f38335
static word *buffer[2];
Packit f38335
Packit f38335
/* Optimal block size for the files.  */
Packit f38335
static size_t buf_size;
Packit f38335
Packit f38335
/* Initial prefix to ignore for each file.  */
Packit f38335
static off_t ignore_initial[2];
Packit f38335
Packit f38335
/* Number of bytes to compare.  */
Packit f38335
static uintmax_t bytes = UINTMAX_MAX;
Packit f38335
Packit f38335
/* Output format.  */
Packit f38335
static enum comparison_type
Packit f38335
  {
Packit f38335
    type_first_diff,	/* Print the first difference.  */
Packit f38335
    type_all_diffs,	/* Print all differences.  */
Packit f38335
    type_no_stdout,	/* Do not output to stdout; only stderr.  */
Packit f38335
    type_status		/* Exit status only.  */
Packit f38335
  } comparison_type;
Packit f38335
Packit f38335
/* If nonzero, print values of bytes quoted like cat -t does. */
Packit f38335
static bool opt_print_bytes;
Packit f38335
Packit f38335
/* Values for long options that do not have single-letter equivalents.  */
Packit f38335
enum
Packit f38335
{
Packit f38335
  HELP_OPTION = CHAR_MAX + 1
Packit f38335
};
Packit f38335
Packit f38335
static struct option const long_options[] =
Packit f38335
{
Packit f38335
  {"print-bytes", 0, 0, 'b'},
Packit f38335
  {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */
Packit f38335
  {"ignore-initial", 1, 0, 'i'},
Packit f38335
  {"verbose", 0, 0, 'l'},
Packit f38335
  {"bytes", 1, 0, 'n'},
Packit f38335
  {"silent", 0, 0, 's'},
Packit f38335
  {"quiet", 0, 0, 's'},
Packit f38335
  {"version", 0, 0, 'v'},
Packit f38335
  {"help", 0, 0, HELP_OPTION},
Packit f38335
  {0, 0, 0, 0}
Packit f38335
};
Packit f38335

Packit f38335
static void try_help (char const *, char const *) __attribute__((noreturn));
Packit f38335
static void
Packit f38335
try_help (char const *reason_msgid, char const *operand)
Packit f38335
{
Packit f38335
  if (reason_msgid)
Packit f38335
    error (0, 0, _(reason_msgid), operand);
Packit f38335
  die (EXIT_TROUBLE, 0,
Packit f38335
	 _("Try '%s --help' for more information."), program_name);
Packit f38335
}
Packit f38335
Packit f38335
static char const valid_suffixes[] = "kKMGTPEZY0";
Packit f38335
Packit f38335
/* Update ignore_initial[F] according to the result of parsing an
Packit f38335
   *operand ARGPTR of --ignore-initial, updating *ARGPTR to point
Packit f38335
   *after the operand.  If DELIMITER is nonzero, the operand may be
Packit f38335
   *followed by DELIMITER; otherwise it must be null-terminated.  */
Packit f38335
static void
Packit f38335
specify_ignore_initial (int f, char **argptr, char delimiter)
Packit f38335
{
Packit f38335
  uintmax_t val;
Packit f38335
  char const *arg = *argptr;
Packit f38335
  strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes);
Packit f38335
  if (! (e == LONGINT_OK
Packit f38335
	 || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter))
Packit f38335
      || TYPE_MAXIMUM (off_t) < val)
Packit f38335
    try_help ("invalid --ignore-initial value '%s'", arg);
Packit f38335
  if (ignore_initial[f] < val)
Packit f38335
    ignore_initial[f] = val;
Packit f38335
}
Packit f38335
Packit f38335
/* Specify the output format.  */
Packit f38335
static void
Packit f38335
specify_comparison_type (enum comparison_type t)
Packit f38335
{
Packit f38335
  if (comparison_type && comparison_type != t)
Packit f38335
    try_help ("options -l and -s are incompatible", 0);
Packit f38335
  comparison_type = t;
Packit f38335
}
Packit f38335
Packit f38335
static void
Packit f38335
check_stdout (void)
Packit f38335
{
Packit f38335
  if (ferror (stdout))
Packit f38335
    die (EXIT_TROUBLE, 0, "%s", _("write failed"));
Packit f38335
  else if (fclose (stdout) != 0)
Packit f38335
    die (EXIT_TROUBLE, errno, "%s", _("standard output"));
Packit f38335
}
Packit f38335
Packit f38335
static char const * const option_help_msgid[] = {
Packit f38335
  N_("-b, --print-bytes          print differing bytes"),
Packit f38335
  N_("-i, --ignore-initial=SKIP         skip first SKIP bytes of both inputs"),
Packit f38335
  N_("-i, --ignore-initial=SKIP1:SKIP2  skip first SKIP1 bytes of FILE1 and\n"
Packit f38335
     "                                      first SKIP2 bytes of FILE2"),
Packit f38335
  N_("-l, --verbose              output byte numbers and differing byte values"),
Packit f38335
  N_("-n, --bytes=LIMIT          compare at most LIMIT bytes"),
Packit f38335
  N_("-s, --quiet, --silent      suppress all normal output"),
Packit f38335
  N_("    --help                 display this help and exit"),
Packit f38335
  N_("-v, --version              output version information and exit"),
Packit f38335
  0
Packit f38335
};
Packit f38335
Packit f38335
static void
Packit f38335
usage (void)
Packit f38335
{
Packit f38335
  char const * const *p;
Packit f38335
Packit f38335
  printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"),
Packit f38335
	  program_name);
Packit f38335
  printf ("%s\n", _("Compare two files byte by byte."));
Packit f38335
  printf ("\n%s\n\n",
Packit f38335
_("The optional SKIP1 and SKIP2 specify the number of bytes to skip\n"
Packit f38335
  "at the beginning of each file (zero by default)."));
Packit f38335
Packit f38335
  fputs (_("\
Packit f38335
Mandatory arguments to long options are mandatory for short options too.\n\
Packit f38335
"), stdout);
Packit f38335
  for (p = option_help_msgid;  *p;  p++)
Packit f38335
    printf ("  %s\n", _(*p));
Packit f38335
  printf ("\n%s\n\n%s\n%s\n",
Packit f38335
	  _("SKIP values may be followed by the following multiplicative suffixes:\n\
Packit f38335
kB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\
Packit f38335
GB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."),
Packit f38335
	  _("If a FILE is '-' or missing, read standard input."),
Packit f38335
	  _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
Packit f38335
  emit_bug_reporting_address ();
Packit f38335
}
Packit f38335

Packit f38335
int
Packit f38335
main (int argc, char **argv)
Packit f38335
{
Packit f38335
  int c, f, exit_status;
Packit f38335
  size_t words_per_buffer;
Packit f38335
Packit f38335
  exit_failure = EXIT_TROUBLE;
Packit f38335
  initialize_main (&argc, &argv);
Packit f38335
  set_program_name (argv[0]);
Packit f38335
  setlocale (LC_ALL, "");
Packit f38335
  bindtextdomain (PACKAGE, LOCALEDIR);
Packit f38335
  textdomain (PACKAGE);
Packit f38335
  c_stack_action (0);
Packit f38335
Packit f38335
  /* Parse command line options.  */
Packit f38335
Packit f38335
  while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0))
Packit f38335
	 != -1)
Packit f38335
    switch (c)
Packit f38335
      {
Packit f38335
      case 'b':
Packit f38335
      case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */
Packit f38335
	opt_print_bytes = true;
Packit f38335
	break;
Packit f38335
Packit f38335
      case 'i':
Packit f38335
	specify_ignore_initial (0, &optarg, ':');
Packit f38335
	if (*optarg++ == ':')
Packit f38335
	  specify_ignore_initial (1, &optarg, 0);
Packit f38335
	else if (ignore_initial[1] < ignore_initial[0])
Packit f38335
	  ignore_initial[1] = ignore_initial[0];
Packit f38335
	break;
Packit f38335
Packit f38335
      case 'l':
Packit f38335
	specify_comparison_type (type_all_diffs);
Packit f38335
	break;
Packit f38335
Packit f38335
      case 'n':
Packit f38335
	{
Packit f38335
	  uintmax_t n;
Packit f38335
	  if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK)
Packit f38335
	    try_help ("invalid --bytes value '%s'", optarg);
Packit f38335
	  if (n < bytes)
Packit f38335
	    bytes = n;
Packit f38335
	}
Packit f38335
	break;
Packit f38335
Packit f38335
      case 's':
Packit f38335
	specify_comparison_type (type_status);
Packit f38335
	break;
Packit f38335
Packit f38335
      case 'v':
Packit f38335
	version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
Packit f38335
		     AUTHORS, (char *) NULL);
Packit f38335
	check_stdout ();
Packit f38335
	return EXIT_SUCCESS;
Packit f38335
Packit f38335
      case HELP_OPTION:
Packit f38335
	usage ();
Packit f38335
	check_stdout ();
Packit f38335
	return EXIT_SUCCESS;
Packit f38335
Packit f38335
      default:
Packit f38335
	try_help (0, 0);
Packit f38335
      }
Packit f38335
Packit f38335
  if (optind == argc)
Packit f38335
    try_help ("missing operand after '%s'", argv[argc - 1]);
Packit f38335
Packit f38335
  file[0] = argv[optind++];
Packit f38335
  file[1] = optind < argc ? argv[optind++] : "-";
Packit f38335
Packit f38335
  for (f = 0; f < 2 && optind < argc; f++)
Packit f38335
    {
Packit f38335
      char *arg = argv[optind++];
Packit f38335
      specify_ignore_initial (f, &arg, 0);
Packit f38335
    }
Packit f38335
Packit f38335
  if (optind < argc)
Packit f38335
    try_help ("extra operand '%s'", argv[optind]);
Packit f38335
Packit f38335
  for (f = 0; f < 2; f++)
Packit f38335
    {
Packit f38335
      /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
Packit f38335
	 stdin is closed and opening file[0] yields file descriptor 0.  */
Packit f38335
      int f1 = f ^ (STREQ (file[1], "-"));
Packit f38335
Packit f38335
      /* Two files with the same name and offset are identical.
Packit f38335
	 But wait until we open the file once, for proper diagnostics.  */
Packit f38335
      if (f && ignore_initial[0] == ignore_initial[1]
Packit f38335
	  && file_name_cmp (file[0], file[1]) == 0)
Packit f38335
	return EXIT_SUCCESS;
Packit f38335
Packit f38335
      if (STREQ (file[f1], "-"))
Packit f38335
	{
Packit f38335
	  file_desc[f1] = STDIN_FILENO;
Packit f38335
	  if (O_BINARY && ! isatty (STDIN_FILENO))
Packit f38335
	    set_binary_mode (STDIN_FILENO, O_BINARY);
Packit f38335
	}
Packit f38335
      else
Packit f38335
	file_desc[f1] = open (file[f1], O_RDONLY | O_BINARY, 0);
Packit f38335
Packit f38335
      if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0)
Packit f38335
	{
Packit f38335
	  if (file_desc[f1] < 0 && comparison_type == type_status)
Packit f38335
	    exit (EXIT_TROUBLE);
Packit f38335
	  else
Packit f38335
	    die (EXIT_TROUBLE, errno, "%s", file[f1]);
Packit f38335
	}
Packit f38335
    }
Packit f38335
Packit f38335
  /* If the files are links to the same inode and have the same file position,
Packit f38335
     they are identical.  */
Packit f38335
Packit f38335
  if (0 < same_file (&stat_buf[0], &stat_buf[1])
Packit f38335
      && same_file_attributes (&stat_buf[0], &stat_buf[1])
Packit f38335
      && file_position (0) == file_position (1))
Packit f38335
    return EXIT_SUCCESS;
Packit f38335
Packit f38335
  /* If output is redirected to the null device, we can avoid some of
Packit f38335
     the work.  */
Packit f38335
Packit f38335
  if (comparison_type != type_status)
Packit f38335
    {
Packit f38335
      struct stat outstat, nullstat;
Packit f38335
Packit f38335
      if (fstat (STDOUT_FILENO, &outstat) == 0
Packit f38335
	  && stat (NULL_DEVICE, &nullstat) == 0
Packit f38335
	  && 0 < same_file (&outstat, &nullstat))
Packit f38335
	comparison_type = type_no_stdout;
Packit f38335
    }
Packit f38335
Packit f38335
  /* If only a return code is needed,
Packit f38335
     and if both input descriptors are associated with plain files,
Packit f38335
     conclude that the files differ if they have different sizes
Packit f38335
     and if more bytes will be compared than are in the smaller file.  */
Packit f38335
Packit f38335
  if (comparison_type == type_status
Packit f38335
      && S_ISREG (stat_buf[0].st_mode)
Packit f38335
      && S_ISREG (stat_buf[1].st_mode))
Packit f38335
    {
Packit f38335
      off_t s0 = stat_buf[0].st_size - file_position (0);
Packit f38335
      off_t s1 = stat_buf[1].st_size - file_position (1);
Packit f38335
      if (s0 < 0)
Packit f38335
	s0 = 0;
Packit f38335
      if (s1 < 0)
Packit f38335
	s1 = 0;
Packit f38335
      if (s0 != s1 && MIN (s0, s1) < bytes)
Packit f38335
	exit (EXIT_FAILURE);
Packit f38335
    }
Packit f38335
Packit f38335
  /* Get the optimal block size of the files.  */
Packit f38335
Packit f38335
  buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
Packit f38335
			 STAT_BLOCKSIZE (stat_buf[1]),
Packit f38335
			 PTRDIFF_MAX - sizeof (word));
Packit f38335
Packit f38335
  /* Allocate word-aligned buffers, with space for sentinels at the end.  */
Packit f38335
Packit f38335
  words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);
Packit f38335
  buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer);
Packit f38335
  buffer[1] = buffer[0] + words_per_buffer;
Packit f38335
Packit f38335
  exit_status = cmp ();
Packit f38335
Packit f38335
  for (f = 0; f < 2; f++)
Packit f38335
    if (close (file_desc[f]) != 0)
Packit f38335
      die (EXIT_TROUBLE, errno, "%s", file[f]);
Packit f38335
  if (exit_status != EXIT_SUCCESS && comparison_type < type_no_stdout)
Packit f38335
    check_stdout ();
Packit f38335
  exit (exit_status);
Packit f38335
  return exit_status;
Packit f38335
}
Packit f38335

Packit f38335
/* Compare the two files already open on 'file_desc[0]' and 'file_desc[1]',
Packit f38335
   using 'buffer[0]' and 'buffer[1]'.
Packit f38335
   Return EXIT_SUCCESS if identical, EXIT_FAILURE if different,
Packit f38335
   >1 if error.  */
Packit f38335
Packit f38335
static int
Packit f38335
cmp (void)
Packit f38335
{
Packit f38335
  bool at_line_start = true;
Packit f38335
  off_t line_number = 1;	/* Line number (1...) of difference. */
Packit f38335
  off_t byte_number = 1;	/* Byte number (1...) of difference. */
Packit f38335
  uintmax_t remaining = bytes;	/* Remaining number of bytes to compare.  */
Packit f38335
  size_t read0, read1;		/* Number of bytes read from each file. */
Packit f38335
  size_t first_diff;		/* Offset (0...) in buffers of 1st diff. */
Packit f38335
  size_t smaller;		/* The lesser of 'read0' and 'read1'. */
Packit f38335
  word *buffer0 = buffer[0];
Packit f38335
  word *buffer1 = buffer[1];
Packit f38335
  char *buf0 = (char *) buffer0;
Packit f38335
  char *buf1 = (char *) buffer1;
Packit f38335
  int differing = 0;
Packit f38335
  int f;
Packit f38335
  int offset_width IF_LINT (= 0);
Packit f38335
Packit f38335
  if (comparison_type == type_all_diffs)
Packit f38335
    {
Packit f38335
      off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t));
Packit f38335
Packit f38335
      for (f = 0; f < 2; f++)
Packit f38335
	if (S_ISREG (stat_buf[f].st_mode))
Packit f38335
	  {
Packit f38335
	    off_t file_bytes = stat_buf[f].st_size - file_position (f);
Packit f38335
	    if (file_bytes < byte_number_max)
Packit f38335
	      byte_number_max = file_bytes;
Packit f38335
	  }
Packit f38335
Packit f38335
      for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++)
Packit f38335
	continue;
Packit f38335
    }
Packit f38335
Packit f38335
  for (f = 0; f < 2; f++)
Packit f38335
    {
Packit f38335
      off_t ig = ignore_initial[f];
Packit f38335
      if (ig && file_position (f) == -1)
Packit f38335
	{
Packit f38335
	  /* lseek failed; read and discard the ignored initial prefix.  */
Packit f38335
	  do
Packit f38335
	    {
Packit f38335
	      size_t bytes_to_read = MIN (ig, buf_size);
Packit f38335
	      size_t r = block_read (file_desc[f], buf0, bytes_to_read);
Packit f38335
	      if (r != bytes_to_read)
Packit f38335
		{
Packit f38335
		  if (r == SIZE_MAX)
Packit f38335
		    die (EXIT_TROUBLE, errno, "%s", file[f]);
Packit f38335
		  break;
Packit f38335
		}
Packit f38335
	      ig -= r;
Packit f38335
	    }
Packit f38335
	  while (ig);
Packit f38335
	}
Packit f38335
    }
Packit f38335
Packit f38335
  do
Packit f38335
    {
Packit f38335
      size_t bytes_to_read = buf_size;
Packit f38335
Packit f38335
      if (remaining != UINTMAX_MAX)
Packit f38335
	{
Packit f38335
	  if (remaining < bytes_to_read)
Packit f38335
	    bytes_to_read = remaining;
Packit f38335
	  remaining -= bytes_to_read;
Packit f38335
	}
Packit f38335
Packit f38335
      read0 = block_read (file_desc[0], buf0, bytes_to_read);
Packit f38335
      if (read0 == SIZE_MAX)
Packit f38335
	die (EXIT_TROUBLE, errno, "%s", file[0]);
Packit f38335
      read1 = block_read (file_desc[1], buf1, bytes_to_read);
Packit f38335
      if (read1 == SIZE_MAX)
Packit f38335
	die (EXIT_TROUBLE, errno, "%s", file[1]);
Packit f38335
Packit f38335
      smaller = MIN (read0, read1);
Packit f38335
Packit f38335
      /* Optimize the common case where the buffers are the same.  */
Packit f38335
      if (memcmp (buf0, buf1, smaller) == 0)
Packit f38335
	first_diff = smaller;
Packit f38335
      else
Packit f38335
	{
Packit f38335
	  /* Insert sentinels for the block compare.  */
Packit f38335
	  buf0[read0] = ~buf1[read0];
Packit f38335
	  buf1[read1] = ~buf0[read1];
Packit f38335
Packit f38335
	  first_diff = block_compare (buffer0, buffer1);
Packit f38335
	}
Packit f38335
Packit f38335
      byte_number += first_diff;
Packit f38335
      if (comparison_type == type_first_diff && first_diff != 0)
Packit f38335
	{
Packit f38335
	  line_number += count_newlines (buf0, first_diff);
Packit f38335
	  at_line_start = buf0[first_diff - 1] == '\n';
Packit f38335
	}
Packit f38335
Packit f38335
      if (first_diff < smaller)
Packit f38335
	{
Packit f38335
	  switch (comparison_type)
Packit f38335
	    {
Packit f38335
	    case type_first_diff:
Packit f38335
	      {
Packit f38335
		char byte_buf[INT_BUFSIZE_BOUND (off_t)];
Packit f38335
		char line_buf[INT_BUFSIZE_BOUND (off_t)];
Packit f38335
		char const *byte_num = offtostr (byte_number, byte_buf);
Packit f38335
		char const *line_num = offtostr (line_number, line_buf);
Packit f38335
		if (!opt_print_bytes)
Packit f38335
		  {
Packit f38335
		    /* See POSIX for this format.  This message is
Packit f38335
		       used only in the POSIX locale, so it need not
Packit f38335
		       be translated.  */
Packit f38335
		    static char const char_message[] =
Packit f38335
		      "%s %s differ: char %s, line %s\n";
Packit f38335
Packit f38335
		    /* The POSIX rationale recommends using the word
Packit f38335
		       "byte" outside the POSIX locale.  Some gettext
Packit f38335
		       implementations translate even in the POSIX
Packit f38335
		       locale if certain other environment variables
Packit f38335
		       are set, so use "byte" if a translation is
Packit f38335
		       available, or if outside the POSIX locale.  */
Packit f38335
		    static char const byte_msgid[] =
Packit f38335
		      N_("%s %s differ: byte %s, line %s\n");
Packit f38335
		    char const *byte_message = _(byte_msgid);
Packit f38335
		    bool use_byte_message = (byte_message != byte_msgid
Packit f38335
					     || hard_locale_LC_MESSAGES);
Packit f38335
Packit f38335
		    printf (use_byte_message ? byte_message : char_message,
Packit f38335
			    file[0], file[1], byte_num, line_num);
Packit f38335
		  }
Packit f38335
		else
Packit f38335
		  {
Packit f38335
		    unsigned char c0 = buf0[first_diff];
Packit f38335
		    unsigned char c1 = buf1[first_diff];
Packit f38335
		    char s0[5];
Packit f38335
		    char s1[5];
Packit f38335
		    sprintc (s0, c0);
Packit f38335
		    sprintc (s1, c1);
Packit f38335
		    printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
Packit f38335
			    file[0], file[1], byte_num, line_num,
Packit f38335
			    c0, s0, c1, s1);
Packit f38335
		  }
Packit f38335
	      }
Packit f38335
	      FALLTHROUGH;
Packit f38335
	    case type_status:
Packit f38335
	      return EXIT_FAILURE;
Packit f38335
Packit f38335
	    case type_all_diffs:
Packit f38335
	      do
Packit f38335
		{
Packit f38335
		  unsigned char c0 = buf0[first_diff];
Packit f38335
		  unsigned char c1 = buf1[first_diff];
Packit f38335
		  if (c0 != c1)
Packit f38335
		    {
Packit f38335
		      char byte_buf[INT_BUFSIZE_BOUND (off_t)];
Packit f38335
		      char const *byte_num = offtostr (byte_number, byte_buf);
Packit f38335
		      if (!opt_print_bytes)
Packit f38335
			{
Packit f38335
			  /* See POSIX for this format.  */
Packit f38335
			  printf ("%*s %3o %3o\n",
Packit f38335
				  offset_width, byte_num, c0, c1);
Packit f38335
			}
Packit f38335
		      else
Packit f38335
			{
Packit f38335
			  char s0[5];
Packit f38335
			  char s1[5];
Packit f38335
			  sprintc (s0, c0);
Packit f38335
			  sprintc (s1, c1);
Packit f38335
			  printf ("%*s %3o %-4s %3o %s\n",
Packit f38335
				  offset_width, byte_num, c0, s0, c1, s1);
Packit f38335
			}
Packit f38335
		    }
Packit f38335
		  byte_number++;
Packit f38335
		  first_diff++;
Packit f38335
		}
Packit f38335
	      while (first_diff < smaller);
Packit f38335
	      differing = -1;
Packit f38335
	      break;
Packit f38335
Packit f38335
	    case type_no_stdout:
Packit f38335
	      differing = 1;
Packit f38335
	      break;
Packit f38335
	    }
Packit f38335
	}
Packit f38335
Packit f38335
      if (read0 != read1)
Packit f38335
	{
Packit f38335
	  if (differing <= 0 && comparison_type != type_status)
Packit f38335
	    {
Packit f38335
	      char const *shorter_file = file[read1 < read0];
Packit f38335
Packit f38335
	      /* POSIX says that each of these format strings must be
Packit f38335
		 "cmp: EOF on %s", optionally followed by a blank and
Packit f38335
		 extra text sans newline, then terminated by "\n".  */
Packit f38335
	      if (byte_number == 1)
Packit f38335
		fprintf (stderr, _("cmp: EOF on %s which is empty\n"),
Packit f38335
			 shorter_file);
Packit f38335
	      else
Packit f38335
		{
Packit f38335
		  char byte_buf[INT_BUFSIZE_BOUND (off_t)];
Packit f38335
		  char const *byte_num = offtostr (byte_number - 1, byte_buf);
Packit f38335
Packit f38335
		  if (comparison_type == type_first_diff)
Packit f38335
		    {
Packit f38335
		      char line_buf[INT_BUFSIZE_BOUND (off_t)];
Packit f38335
		      char const *line_num
Packit f38335
			= offtostr (line_number - at_line_start, line_buf);
Packit f38335
		      fprintf (stderr,
Packit f38335
			       (at_line_start
Packit f38335
				? _("cmp: EOF on %s after byte %s, line %s\n")
Packit f38335
				: _("cmp: EOF on %s after byte %s,"
Packit f38335
				    " in line %s\n")),
Packit f38335
			       shorter_file, byte_num, line_num);
Packit f38335
		    }
Packit f38335
		  else
Packit f38335
		    fprintf (stderr,
Packit f38335
			     _("cmp: EOF on %s after byte %s\n"),
Packit f38335
			     shorter_file, byte_num);
Packit f38335
		}
Packit f38335
	    }
Packit f38335
Packit f38335
	  return EXIT_FAILURE;
Packit f38335
	}
Packit f38335
    }
Packit f38335
  while (differing <= 0 && read0 == buf_size);
Packit f38335
Packit f38335
  return differing == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
Packit f38335
}
Packit f38335

Packit f38335
/* Compare two blocks of memory P0 and P1 until they differ.
Packit f38335
   If the blocks are not guaranteed to be different, put sentinels at the ends
Packit f38335
   of the blocks before calling this function.
Packit f38335
Packit f38335
   Return the offset of the first byte that differs.  */
Packit f38335
Packit f38335
static size_t
Packit f38335
block_compare (word const *p0, word const *p1)
Packit f38335
{
Packit f38335
  word const *l0, *l1;
Packit f38335
  char const *c0, *c1;
Packit f38335
Packit f38335
  /* Find the rough position of the first difference by reading words,
Packit f38335
     not bytes.  */
Packit f38335
Packit f38335
  for (l0 = p0, l1 = p1;  *l0 == *l1;  l0++, l1++)
Packit f38335
    continue;
Packit f38335
Packit f38335
  /* Find the exact differing position (endianness independent).  */
Packit f38335
Packit f38335
  for (c0 = (char const *) l0, c1 = (char const *) l1;
Packit f38335
       *c0 == *c1;
Packit f38335
       c0++, c1++)
Packit f38335
    continue;
Packit f38335
Packit f38335
  return c0 - (char const *) p0;
Packit f38335
}
Packit f38335
Packit f38335
/* Return the number of newlines in BUF, of size BUFSIZE,
Packit f38335
   where BUF[NBYTES] is available for use as a sentinel.  */
Packit f38335
Packit f38335
static size_t
Packit f38335
count_newlines (char *buf, size_t bufsize)
Packit f38335
{
Packit f38335
  size_t count = 0;
Packit f38335
  char *p;
Packit f38335
  char *lim = buf + bufsize;
Packit f38335
  *lim = '\n';
Packit f38335
  for (p = buf; (p = rawmemchr (p, '\n')) != lim; p++)
Packit f38335
    count++;
Packit f38335
  return count;
Packit f38335
}
Packit f38335
Packit f38335
/* Put into BUF the unsigned char C, making unprintable bytes
Packit f38335
   visible by quoting like cat -t does.  */
Packit f38335
Packit f38335
static void
Packit f38335
sprintc (char *buf, unsigned char c)
Packit f38335
{
Packit f38335
  if (! isprint (c))
Packit f38335
    {
Packit f38335
      if (c >= 128)
Packit f38335
	{
Packit f38335
	  *buf++ = 'M';
Packit f38335
	  *buf++ = '-';
Packit f38335
	  c -= 128;
Packit f38335
	}
Packit f38335
      if (c < 32)
Packit f38335
	{
Packit f38335
	  *buf++ = '^';
Packit f38335
	  c += 64;
Packit f38335
	}
Packit f38335
      else if (c == 127)
Packit f38335
	{
Packit f38335
	  *buf++ = '^';
Packit f38335
	  c = '?';
Packit f38335
	}
Packit f38335
    }
Packit f38335
Packit f38335
  *buf++ = c;
Packit f38335
  *buf = 0;
Packit f38335
}
Packit f38335

Packit f38335
/* Position file F to ignore_initial[F] bytes from its initial position,
Packit f38335
   and yield its new position.  Don't try more than once.  */
Packit f38335
Packit f38335
static off_t
Packit f38335
file_position (int f)
Packit f38335
{
Packit f38335
  static bool positioned[2];
Packit f38335
  static off_t position[2];
Packit f38335
Packit f38335
  if (! positioned[f])
Packit f38335
    {
Packit f38335
      positioned[f] = true;
Packit f38335
      position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR);
Packit f38335
    }
Packit f38335
  return position[f];
Packit f38335
}