Blame src/cmp.c

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