Blame src/cmp.c

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