Blame src/cmp.c.shows_incorrect_data

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