xref: /netbsd-src/external/gpl2/diffutils/dist/src/cmp.c (revision 75f6d617e282811cb173c2ccfbf5df0dd71f7045)
1*75f6d617Schristos /*	$NetBSD: cmp.c,v 1.1.1.1 2016/01/13 03:15:30 christos Exp $	*/
2*75f6d617Schristos 
3*75f6d617Schristos /* cmp - compare two files byte by byte
4*75f6d617Schristos 
5*75f6d617Schristos    Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
6*75f6d617Schristos    2002 Free Software Foundation, Inc.
7*75f6d617Schristos 
8*75f6d617Schristos    This program is free software; you can redistribute it and/or modify
9*75f6d617Schristos    it under the terms of the GNU General Public License as published by
10*75f6d617Schristos    the Free Software Foundation; either version 2, or (at your option)
11*75f6d617Schristos    any later version.
12*75f6d617Schristos 
13*75f6d617Schristos    This program is distributed in the hope that it will be useful,
14*75f6d617Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
15*75f6d617Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16*75f6d617Schristos    See the GNU General Public License for more details.
17*75f6d617Schristos 
18*75f6d617Schristos    You should have received a copy of the GNU General Public License
19*75f6d617Schristos    along with this program; see the file COPYING.
20*75f6d617Schristos    If not, write to the Free Software Foundation,
21*75f6d617Schristos    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22*75f6d617Schristos 
23*75f6d617Schristos #include "system.h"
24*75f6d617Schristos 
25*75f6d617Schristos #include <stdio.h>
26*75f6d617Schristos #include <cmpbuf.h>
27*75f6d617Schristos #include <c-stack.h>
28*75f6d617Schristos #include <error.h>
29*75f6d617Schristos #include <exitfail.h>
30*75f6d617Schristos #include <freesoft.h>
31*75f6d617Schristos #include <getopt.h>
32*75f6d617Schristos #include <hard-locale.h>
33*75f6d617Schristos #include <inttostr.h>
34*75f6d617Schristos #include <setmode.h>
35*75f6d617Schristos #include <xalloc.h>
36*75f6d617Schristos #include <xstrtol.h>
37*75f6d617Schristos 
38*75f6d617Schristos #if defined LC_MESSAGES && ENABLE_NLS
39*75f6d617Schristos # define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES)
40*75f6d617Schristos #else
41*75f6d617Schristos # define hard_locale_LC_MESSAGES 0
42*75f6d617Schristos #endif
43*75f6d617Schristos 
44*75f6d617Schristos static char const authorship_msgid[] =
45*75f6d617Schristos   N_("Written by Torbjorn Granlund and David MacKenzie.");
46*75f6d617Schristos 
47*75f6d617Schristos static char const copyright_string[] =
48*75f6d617Schristos   "Copyright (C) 2002 Free Software Foundation, Inc.";
49*75f6d617Schristos 
50*75f6d617Schristos extern char const version_string[];
51*75f6d617Schristos 
52*75f6d617Schristos static int cmp (void);
53*75f6d617Schristos static off_t file_position (int);
54*75f6d617Schristos static size_t block_compare (word const *, word const *);
55*75f6d617Schristos static size_t block_compare_and_count (word const *, word const *, off_t *);
56*75f6d617Schristos static void sprintc (char *, unsigned char);
57*75f6d617Schristos 
58*75f6d617Schristos /* Name under which this program was invoked.  */
59*75f6d617Schristos char *program_name;
60*75f6d617Schristos 
61*75f6d617Schristos /* Filenames of the compared files.  */
62*75f6d617Schristos static char const *file[2];
63*75f6d617Schristos 
64*75f6d617Schristos /* File descriptors of the files.  */
65*75f6d617Schristos static int file_desc[2];
66*75f6d617Schristos 
67*75f6d617Schristos /* Status of the files.  */
68*75f6d617Schristos static struct stat stat_buf[2];
69*75f6d617Schristos 
70*75f6d617Schristos /* Read buffers for the files.  */
71*75f6d617Schristos static word *buffer[2];
72*75f6d617Schristos 
73*75f6d617Schristos /* Optimal block size for the files.  */
74*75f6d617Schristos static size_t buf_size;
75*75f6d617Schristos 
76*75f6d617Schristos /* Initial prefix to ignore for each file.  */
77*75f6d617Schristos static off_t ignore_initial[2];
78*75f6d617Schristos 
79*75f6d617Schristos /* Number of bytes to compare.  */
80*75f6d617Schristos static uintmax_t bytes = UINTMAX_MAX;
81*75f6d617Schristos 
82*75f6d617Schristos /* Output format.  */
83*75f6d617Schristos static enum comparison_type
84*75f6d617Schristos   {
85*75f6d617Schristos     type_first_diff,	/* Print the first difference.  */
86*75f6d617Schristos     type_all_diffs,	/* Print all differences.  */
87*75f6d617Schristos     type_status		/* Exit status only.  */
88*75f6d617Schristos   } comparison_type;
89*75f6d617Schristos 
90*75f6d617Schristos /* If nonzero, print values of bytes quoted like cat -t does. */
91*75f6d617Schristos static bool opt_print_bytes;
92*75f6d617Schristos 
93*75f6d617Schristos /* Values for long options that do not have single-letter equivalents.  */
94*75f6d617Schristos enum
95*75f6d617Schristos {
96*75f6d617Schristos   HELP_OPTION = CHAR_MAX + 1
97*75f6d617Schristos };
98*75f6d617Schristos 
99*75f6d617Schristos static struct option const long_options[] =
100*75f6d617Schristos {
101*75f6d617Schristos   {"print-bytes", 0, 0, 'b'},
102*75f6d617Schristos   {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */
103*75f6d617Schristos   {"ignore-initial", 1, 0, 'i'},
104*75f6d617Schristos   {"verbose", 0, 0, 'l'},
105*75f6d617Schristos   {"bytes", 1, 0, 'n'},
106*75f6d617Schristos   {"silent", 0, 0, 's'},
107*75f6d617Schristos   {"quiet", 0, 0, 's'},
108*75f6d617Schristos   {"version", 0, 0, 'v'},
109*75f6d617Schristos   {"help", 0, 0, HELP_OPTION},
110*75f6d617Schristos   {0, 0, 0, 0}
111*75f6d617Schristos };
112*75f6d617Schristos 
113*75f6d617Schristos static void try_help (char const *, char const *) __attribute__((noreturn));
114*75f6d617Schristos static void
try_help(char const * reason_msgid,char const * operand)115*75f6d617Schristos try_help (char const *reason_msgid, char const *operand)
116*75f6d617Schristos {
117*75f6d617Schristos   if (reason_msgid)
118*75f6d617Schristos     error (0, 0, _(reason_msgid), operand);
119*75f6d617Schristos   error (EXIT_TROUBLE, 0,
120*75f6d617Schristos 	 _("Try `%s --help' for more information."), program_name);
121*75f6d617Schristos   abort ();
122*75f6d617Schristos }
123*75f6d617Schristos 
124*75f6d617Schristos static char const valid_suffixes[] = "kKMGTPEZY0";
125*75f6d617Schristos 
126*75f6d617Schristos /* Parse an operand *ARGPTR of --ignore-initial, updating *ARGPTR to
127*75f6d617Schristos    point after the operand.  If DELIMITER is nonzero, the operand may
128*75f6d617Schristos    be followed by DELIMITER; otherwise it must be null-terminated.  */
129*75f6d617Schristos static off_t
parse_ignore_initial(char ** argptr,char delimiter)130*75f6d617Schristos parse_ignore_initial (char **argptr, char delimiter)
131*75f6d617Schristos {
132*75f6d617Schristos   uintmax_t val;
133*75f6d617Schristos   off_t o;
134*75f6d617Schristos   char const *arg = *argptr;
135*75f6d617Schristos   strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes);
136*75f6d617Schristos   if (! (e == LONGINT_OK
137*75f6d617Schristos 	 || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter))
138*75f6d617Schristos       || (o = val) < 0 || o != val || val == UINTMAX_MAX)
139*75f6d617Schristos     try_help ("invalid --ignore-initial value `%s'", arg);
140*75f6d617Schristos   return o;
141*75f6d617Schristos }
142*75f6d617Schristos 
143*75f6d617Schristos /* Specify the output format.  */
144*75f6d617Schristos static void
specify_comparison_type(enum comparison_type t)145*75f6d617Schristos specify_comparison_type (enum comparison_type t)
146*75f6d617Schristos {
147*75f6d617Schristos   if (comparison_type)
148*75f6d617Schristos     try_help ("options -l and -s are incompatible", 0);
149*75f6d617Schristos   comparison_type = t;
150*75f6d617Schristos }
151*75f6d617Schristos 
152*75f6d617Schristos static void
check_stdout(void)153*75f6d617Schristos check_stdout (void)
154*75f6d617Schristos {
155*75f6d617Schristos   if (ferror (stdout))
156*75f6d617Schristos     error (EXIT_TROUBLE, 0, "%s", _("write failed"));
157*75f6d617Schristos   else if (fclose (stdout) != 0)
158*75f6d617Schristos     error (EXIT_TROUBLE, errno, "%s", _("standard output"));
159*75f6d617Schristos }
160*75f6d617Schristos 
161*75f6d617Schristos static char const * const option_help_msgid[] = {
162*75f6d617Schristos   N_("-b  --print-bytes  Print differing bytes."),
163*75f6d617Schristos   N_("-i SKIP  --ignore-initial=SKIP  Skip the first SKIP bytes of input."),
164*75f6d617Schristos   N_("-i SKIP1:SKIP2  --ignore-initial=SKIP1:SKIP2"),
165*75f6d617Schristos   N_("  Skip the first SKIP1 bytes of FILE1 and the first SKIP2 bytes of FILE2."),
166*75f6d617Schristos   N_("-l  --verbose  Output byte numbers and values of all differing bytes."),
167*75f6d617Schristos   N_("-n LIMIT  --bytes=LIMIT  Compare at most LIMIT bytes."),
168*75f6d617Schristos   N_("-s  --quiet  --silent  Output nothing; yield exit status only."),
169*75f6d617Schristos   N_("-v  --version  Output version info."),
170*75f6d617Schristos   N_("--help  Output this help."),
171*75f6d617Schristos   0
172*75f6d617Schristos };
173*75f6d617Schristos 
174*75f6d617Schristos static void
usage(void)175*75f6d617Schristos usage (void)
176*75f6d617Schristos {
177*75f6d617Schristos   char const * const *p;
178*75f6d617Schristos 
179*75f6d617Schristos   printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"),
180*75f6d617Schristos 	  program_name);
181*75f6d617Schristos   printf ("%s\n\n", _("Compare two files byte by byte."));
182*75f6d617Schristos   for (p = option_help_msgid;  *p;  p++)
183*75f6d617Schristos     printf ("  %s\n", _(*p));
184*75f6d617Schristos   printf ("\n%s\n%s\n\n%s\n\n%s\n",
185*75f6d617Schristos 	  _("SKIP1 and SKIP2 are the number of bytes to skip in each file."),
186*75f6d617Schristos 	  _("SKIP values may be followed by the following multiplicative suffixes:\n\
187*75f6d617Schristos kB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\
188*75f6d617Schristos GB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."),
189*75f6d617Schristos 	  _("If a FILE is `-' or missing, read standard input."),
190*75f6d617Schristos 	  _("Report bugs to <bug-gnu-utils@gnu.org>."));
191*75f6d617Schristos }
192*75f6d617Schristos 
193*75f6d617Schristos int
main(int argc,char ** argv)194*75f6d617Schristos main (int argc, char **argv)
195*75f6d617Schristos {
196*75f6d617Schristos   int c, f, exit_status;
197*75f6d617Schristos   size_t words_per_buffer;
198*75f6d617Schristos 
199*75f6d617Schristos   exit_failure = EXIT_TROUBLE;
200*75f6d617Schristos   initialize_main (&argc, &argv);
201*75f6d617Schristos   program_name = argv[0];
202*75f6d617Schristos   setlocale (LC_ALL, "");
203*75f6d617Schristos   bindtextdomain (PACKAGE, LOCALEDIR);
204*75f6d617Schristos   textdomain (PACKAGE);
205*75f6d617Schristos   c_stack_action (c_stack_die);
206*75f6d617Schristos 
207*75f6d617Schristos   /* Parse command line options.  */
208*75f6d617Schristos 
209*75f6d617Schristos   while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0))
210*75f6d617Schristos 	 != -1)
211*75f6d617Schristos     switch (c)
212*75f6d617Schristos       {
213*75f6d617Schristos       case 'b':
214*75f6d617Schristos       case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */
215*75f6d617Schristos 	opt_print_bytes = 1;
216*75f6d617Schristos 	break;
217*75f6d617Schristos 
218*75f6d617Schristos       case 'i':
219*75f6d617Schristos 	ignore_initial[0] = parse_ignore_initial (&optarg, ':');
220*75f6d617Schristos 	ignore_initial[1] = (*optarg++ == ':'
221*75f6d617Schristos 			     ? parse_ignore_initial (&optarg, 0)
222*75f6d617Schristos 			     : ignore_initial[0]);
223*75f6d617Schristos 	break;
224*75f6d617Schristos 
225*75f6d617Schristos       case 'l':
226*75f6d617Schristos 	specify_comparison_type (type_all_diffs);
227*75f6d617Schristos 	break;
228*75f6d617Schristos 
229*75f6d617Schristos       case 'n':
230*75f6d617Schristos 	{
231*75f6d617Schristos 	  uintmax_t n;
232*75f6d617Schristos 	  if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK)
233*75f6d617Schristos 	    try_help ("invalid --bytes value `%s'", optarg);
234*75f6d617Schristos 	  if (n < bytes)
235*75f6d617Schristos 	    bytes = n;
236*75f6d617Schristos 	}
237*75f6d617Schristos 	break;
238*75f6d617Schristos 
239*75f6d617Schristos       case 's':
240*75f6d617Schristos 	specify_comparison_type (type_status);
241*75f6d617Schristos 	break;
242*75f6d617Schristos 
243*75f6d617Schristos       case 'v':
244*75f6d617Schristos 	printf ("cmp %s\n%s\n\n%s\n\n%s\n",
245*75f6d617Schristos 		version_string, copyright_string,
246*75f6d617Schristos 		_(free_software_msgid), _(authorship_msgid));
247*75f6d617Schristos 	check_stdout ();
248*75f6d617Schristos 	return EXIT_SUCCESS;
249*75f6d617Schristos 
250*75f6d617Schristos       case HELP_OPTION:
251*75f6d617Schristos 	usage ();
252*75f6d617Schristos 	check_stdout ();
253*75f6d617Schristos 	return EXIT_SUCCESS;
254*75f6d617Schristos 
255*75f6d617Schristos       default:
256*75f6d617Schristos 	try_help (0, 0);
257*75f6d617Schristos       }
258*75f6d617Schristos 
259*75f6d617Schristos   if (optind == argc)
260*75f6d617Schristos     try_help ("missing operand after `%s'", argv[argc - 1]);
261*75f6d617Schristos 
262*75f6d617Schristos   file[0] = argv[optind++];
263*75f6d617Schristos   file[1] = optind < argc ? argv[optind++] : "-";
264*75f6d617Schristos 
265*75f6d617Schristos   for (f = 0; f < 2 && optind < argc; f++)
266*75f6d617Schristos     {
267*75f6d617Schristos       char *arg = argv[optind++];
268*75f6d617Schristos       ignore_initial[f] = parse_ignore_initial (&arg, 0);
269*75f6d617Schristos     }
270*75f6d617Schristos 
271*75f6d617Schristos   if (optind < argc)
272*75f6d617Schristos     try_help ("extra operand `%s'", argv[optind]);
273*75f6d617Schristos 
274*75f6d617Schristos   for (f = 0; f < 2; f++)
275*75f6d617Schristos     {
276*75f6d617Schristos       /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
277*75f6d617Schristos 	 stdin is closed and opening file[0] yields file descriptor 0.  */
278*75f6d617Schristos       int f1 = f ^ (strcmp (file[1], "-") == 0);
279*75f6d617Schristos 
280*75f6d617Schristos       /* Two files with the same name are identical.
281*75f6d617Schristos 	 But wait until we open the file once, for proper diagnostics.  */
282*75f6d617Schristos       if (f && file_name_cmp (file[0], file[1]) == 0)
283*75f6d617Schristos 	return EXIT_SUCCESS;
284*75f6d617Schristos 
285*75f6d617Schristos       file_desc[f1] = (strcmp (file[f1], "-") == 0
286*75f6d617Schristos 		       ? STDIN_FILENO
287*75f6d617Schristos 		       : open (file[f1], O_RDONLY, 0));
288*75f6d617Schristos       if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0)
289*75f6d617Schristos 	{
290*75f6d617Schristos 	  if (file_desc[f1] < 0 && comparison_type == type_status)
291*75f6d617Schristos 	    exit (EXIT_TROUBLE);
292*75f6d617Schristos 	  else
293*75f6d617Schristos 	    error (EXIT_TROUBLE, errno, "%s", file[f1]);
294*75f6d617Schristos 	}
295*75f6d617Schristos 
296*75f6d617Schristos       set_binary_mode (file_desc[f1], 1);
297*75f6d617Schristos     }
298*75f6d617Schristos 
299*75f6d617Schristos   /* If the files are links to the same inode and have the same file position,
300*75f6d617Schristos      they are identical.  */
301*75f6d617Schristos 
302*75f6d617Schristos   if (0 < same_file (&stat_buf[0], &stat_buf[1])
303*75f6d617Schristos       && same_file_attributes (&stat_buf[0], &stat_buf[1])
304*75f6d617Schristos       && file_position (0) == file_position (1))
305*75f6d617Schristos     return EXIT_SUCCESS;
306*75f6d617Schristos 
307*75f6d617Schristos   /* If output is redirected to the null device, we may assume `-s'.  */
308*75f6d617Schristos 
309*75f6d617Schristos   if (comparison_type != type_status)
310*75f6d617Schristos     {
311*75f6d617Schristos       struct stat outstat, nullstat;
312*75f6d617Schristos 
313*75f6d617Schristos       if (fstat (STDOUT_FILENO, &outstat) == 0
314*75f6d617Schristos 	  && stat (NULL_DEVICE, &nullstat) == 0
315*75f6d617Schristos 	  && 0 < same_file (&outstat, &nullstat))
316*75f6d617Schristos 	comparison_type = type_status;
317*75f6d617Schristos     }
318*75f6d617Schristos 
319*75f6d617Schristos   /* If only a return code is needed,
320*75f6d617Schristos      and if both input descriptors are associated with plain files,
321*75f6d617Schristos      conclude that the files differ if they have different sizes
322*75f6d617Schristos      and if more bytes will be compared than are in the smaller file.  */
323*75f6d617Schristos 
324*75f6d617Schristos   if (comparison_type == type_status
325*75f6d617Schristos       && S_ISREG (stat_buf[0].st_mode)
326*75f6d617Schristos       && S_ISREG (stat_buf[1].st_mode))
327*75f6d617Schristos     {
328*75f6d617Schristos       off_t s0 = stat_buf[0].st_size - file_position (0);
329*75f6d617Schristos       off_t s1 = stat_buf[1].st_size - file_position (1);
330*75f6d617Schristos       if (s0 < 0)
331*75f6d617Schristos 	s0 = 0;
332*75f6d617Schristos       if (s1 < 0)
333*75f6d617Schristos 	s1 = 0;
334*75f6d617Schristos       if (s0 != s1 && MIN (s0, s1) < bytes)
335*75f6d617Schristos 	exit (EXIT_FAILURE);
336*75f6d617Schristos     }
337*75f6d617Schristos 
338*75f6d617Schristos   /* Get the optimal block size of the files.  */
339*75f6d617Schristos 
340*75f6d617Schristos   buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
341*75f6d617Schristos 			 STAT_BLOCKSIZE (stat_buf[1]),
342*75f6d617Schristos 			 PTRDIFF_MAX - sizeof (word));
343*75f6d617Schristos 
344*75f6d617Schristos   /* Allocate word-aligned buffers, with space for sentinels at the end.  */
345*75f6d617Schristos 
346*75f6d617Schristos   words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);
347*75f6d617Schristos   buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer);
348*75f6d617Schristos   buffer[1] = buffer[0] + words_per_buffer;
349*75f6d617Schristos 
350*75f6d617Schristos   exit_status = cmp ();
351*75f6d617Schristos 
352*75f6d617Schristos   for (f = 0; f < 2; f++)
353*75f6d617Schristos     if (close (file_desc[f]) != 0)
354*75f6d617Schristos       error (EXIT_TROUBLE, errno, "%s", file[f]);
355*75f6d617Schristos   if (exit_status != 0  &&  comparison_type != type_status)
356*75f6d617Schristos     check_stdout ();
357*75f6d617Schristos   exit (exit_status);
358*75f6d617Schristos   return exit_status;
359*75f6d617Schristos }
360*75f6d617Schristos 
361*75f6d617Schristos /* Compare the two files already open on `file_desc[0]' and `file_desc[1]',
362*75f6d617Schristos    using `buffer[0]' and `buffer[1]'.
363*75f6d617Schristos    Return EXIT_SUCCESS if identical, EXIT_FAILURE if different,
364*75f6d617Schristos    >1 if error.  */
365*75f6d617Schristos 
366*75f6d617Schristos static int
cmp(void)367*75f6d617Schristos cmp (void)
368*75f6d617Schristos {
369*75f6d617Schristos   off_t line_number = 1;	/* Line number (1...) of difference. */
370*75f6d617Schristos   off_t byte_number = 1;	/* Byte number (1...) of difference. */
371*75f6d617Schristos   uintmax_t remaining = bytes;	/* Remaining number of bytes to compare.  */
372*75f6d617Schristos   size_t read0, read1;		/* Number of bytes read from each file. */
373*75f6d617Schristos   size_t first_diff;		/* Offset (0...) in buffers of 1st diff. */
374*75f6d617Schristos   size_t smaller;		/* The lesser of `read0' and `read1'. */
375*75f6d617Schristos   word *buffer0 = buffer[0];
376*75f6d617Schristos   word *buffer1 = buffer[1];
377*75f6d617Schristos   char *buf0 = (char *) buffer0;
378*75f6d617Schristos   char *buf1 = (char *) buffer1;
379*75f6d617Schristos   int ret = EXIT_SUCCESS;
380*75f6d617Schristos   int f;
381*75f6d617Schristos   int offset_width;
382*75f6d617Schristos 
383*75f6d617Schristos   if (comparison_type == type_all_diffs)
384*75f6d617Schristos     {
385*75f6d617Schristos       off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t));
386*75f6d617Schristos 
387*75f6d617Schristos       for (f = 0; f < 2; f++)
388*75f6d617Schristos 	if (S_ISREG (stat_buf[f].st_mode))
389*75f6d617Schristos 	  {
390*75f6d617Schristos 	    off_t file_bytes = stat_buf[f].st_size - file_position (f);
391*75f6d617Schristos 	    if (file_bytes < byte_number_max)
392*75f6d617Schristos 	      byte_number_max = file_bytes;
393*75f6d617Schristos 	  }
394*75f6d617Schristos 
395*75f6d617Schristos       for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++)
396*75f6d617Schristos 	continue;
397*75f6d617Schristos     }
398*75f6d617Schristos 
399*75f6d617Schristos   for (f = 0; f < 2; f++)
400*75f6d617Schristos     {
401*75f6d617Schristos       off_t ig = ignore_initial[f];
402*75f6d617Schristos       if (ig && file_position (f) == -1)
403*75f6d617Schristos 	{
404*75f6d617Schristos 	  /* lseek failed; read and discard the ignored initial prefix.  */
405*75f6d617Schristos 	  do
406*75f6d617Schristos 	    {
407*75f6d617Schristos 	      size_t bytes_to_read = MIN (ig, buf_size);
408*75f6d617Schristos 	      size_t r = block_read (file_desc[f], buf0, bytes_to_read);
409*75f6d617Schristos 	      if (r != bytes_to_read)
410*75f6d617Schristos 		{
411*75f6d617Schristos 		  if (r == SIZE_MAX)
412*75f6d617Schristos 		    error (EXIT_TROUBLE, errno, "%s", file[f]);
413*75f6d617Schristos 		  break;
414*75f6d617Schristos 		}
415*75f6d617Schristos 	      ig -= r;
416*75f6d617Schristos 	    }
417*75f6d617Schristos 	  while (ig);
418*75f6d617Schristos 	}
419*75f6d617Schristos     }
420*75f6d617Schristos 
421*75f6d617Schristos   do
422*75f6d617Schristos     {
423*75f6d617Schristos       size_t bytes_to_read = buf_size;
424*75f6d617Schristos 
425*75f6d617Schristos       if (remaining != UINTMAX_MAX)
426*75f6d617Schristos 	{
427*75f6d617Schristos 	  if (remaining < bytes_to_read)
428*75f6d617Schristos 	    bytes_to_read = remaining;
429*75f6d617Schristos 	  remaining -= bytes_to_read;
430*75f6d617Schristos 	}
431*75f6d617Schristos 
432*75f6d617Schristos       read0 = block_read (file_desc[0], buf0, bytes_to_read);
433*75f6d617Schristos       if (read0 == SIZE_MAX)
434*75f6d617Schristos 	error (EXIT_TROUBLE, errno, "%s", file[0]);
435*75f6d617Schristos       read1 = block_read (file_desc[1], buf1, bytes_to_read);
436*75f6d617Schristos       if (read1 == SIZE_MAX)
437*75f6d617Schristos 	error (EXIT_TROUBLE, errno, "%s", file[1]);
438*75f6d617Schristos 
439*75f6d617Schristos       /* Insert sentinels for the block compare.  */
440*75f6d617Schristos 
441*75f6d617Schristos       buf0[read0] = ~buf1[read0];
442*75f6d617Schristos       buf1[read1] = ~buf0[read1];
443*75f6d617Schristos 
444*75f6d617Schristos       /* If the line number should be written for differing files,
445*75f6d617Schristos 	 compare the blocks and count the number of newlines
446*75f6d617Schristos 	 simultaneously.  */
447*75f6d617Schristos       first_diff = (comparison_type == type_first_diff
448*75f6d617Schristos 		    ? block_compare_and_count (buffer0, buffer1, &line_number)
449*75f6d617Schristos 		    : block_compare (buffer0, buffer1));
450*75f6d617Schristos 
451*75f6d617Schristos       byte_number += first_diff;
452*75f6d617Schristos       smaller = MIN (read0, read1);
453*75f6d617Schristos 
454*75f6d617Schristos       if (first_diff < smaller)
455*75f6d617Schristos 	{
456*75f6d617Schristos 	  switch (comparison_type)
457*75f6d617Schristos 	    {
458*75f6d617Schristos 	    case type_first_diff:
459*75f6d617Schristos 	      {
460*75f6d617Schristos 		char byte_buf[INT_BUFSIZE_BOUND (off_t)];
461*75f6d617Schristos 		char line_buf[INT_BUFSIZE_BOUND (off_t)];
462*75f6d617Schristos 		char const *byte_num = offtostr (byte_number, byte_buf);
463*75f6d617Schristos 		char const *line_num = offtostr (line_number, line_buf);
464*75f6d617Schristos 		if (!opt_print_bytes)
465*75f6d617Schristos 		  {
466*75f6d617Schristos 		    /* See POSIX 1003.1-2001 for this format.  This
467*75f6d617Schristos 		       message is used only in the POSIX locale, so it
468*75f6d617Schristos 		       need not be translated.  */
469*75f6d617Schristos 		    static char const char_message[] =
470*75f6d617Schristos 		      "%s %s differ: char %s, line %s\n";
471*75f6d617Schristos 
472*75f6d617Schristos 		    /* The POSIX rationale recommends using the word
473*75f6d617Schristos 		       "byte" outside the POSIX locale.  Some gettext
474*75f6d617Schristos 		       implementations translate even in the POSIX
475*75f6d617Schristos 		       locale if certain other environment variables
476*75f6d617Schristos 		       are set, so use "byte" if a translation is
477*75f6d617Schristos 		       available, or if outside the POSIX locale.  */
478*75f6d617Schristos 		    static char const byte_msgid[] =
479*75f6d617Schristos 		      N_("%s %s differ: byte %s, line %s\n");
480*75f6d617Schristos 		    char const *byte_message = _(byte_msgid);
481*75f6d617Schristos 		    bool use_byte_message = (byte_message != byte_msgid
482*75f6d617Schristos 					     || hard_locale_LC_MESSAGES);
483*75f6d617Schristos 
484*75f6d617Schristos 		    printf ((use_byte_message
485*75f6d617Schristos 			     ? byte_message
486*75f6d617Schristos 			     : "%s %s differ: char %s, line %s\n"),
487*75f6d617Schristos 			    file[0], file[1], byte_num, line_num);
488*75f6d617Schristos 		  }
489*75f6d617Schristos 		else
490*75f6d617Schristos 		  {
491*75f6d617Schristos 		    unsigned char c0 = buf0[first_diff];
492*75f6d617Schristos 		    unsigned char c1 = buf1[first_diff];
493*75f6d617Schristos 		    char s0[5];
494*75f6d617Schristos 		    char s1[5];
495*75f6d617Schristos 		    sprintc (s0, c0);
496*75f6d617Schristos 		    sprintc (s1, c1);
497*75f6d617Schristos 		    printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
498*75f6d617Schristos 			    file[0], file[1], byte_num, line_num,
499*75f6d617Schristos 			    c0, s0, c1, s1);
500*75f6d617Schristos 		}
501*75f6d617Schristos 	      }
502*75f6d617Schristos 	      /* Fall through.  */
503*75f6d617Schristos 	    case type_status:
504*75f6d617Schristos 	      return EXIT_FAILURE;
505*75f6d617Schristos 
506*75f6d617Schristos 	    case type_all_diffs:
507*75f6d617Schristos 	      do
508*75f6d617Schristos 		{
509*75f6d617Schristos 		  unsigned char c0 = buf0[first_diff];
510*75f6d617Schristos 		  unsigned char c1 = buf1[first_diff];
511*75f6d617Schristos 		  if (c0 != c1)
512*75f6d617Schristos 		    {
513*75f6d617Schristos 		      char byte_buf[INT_BUFSIZE_BOUND (off_t)];
514*75f6d617Schristos 		      char const *byte_num = offtostr (byte_number, byte_buf);
515*75f6d617Schristos 		      if (!opt_print_bytes)
516*75f6d617Schristos 			{
517*75f6d617Schristos 			  /* See POSIX 1003.1-2001 for this format.  */
518*75f6d617Schristos 			  printf ("%*s %3o %3o\n",
519*75f6d617Schristos 				  offset_width, byte_num, c0, c1);
520*75f6d617Schristos 			}
521*75f6d617Schristos 		      else
522*75f6d617Schristos 			{
523*75f6d617Schristos 			  char s0[5];
524*75f6d617Schristos 			  char s1[5];
525*75f6d617Schristos 			  sprintc (s0, c0);
526*75f6d617Schristos 			  sprintc (s1, c1);
527*75f6d617Schristos 			  printf ("%*s %3o %-4s %3o %s\n",
528*75f6d617Schristos 				  offset_width, byte_num, c0, s0, c1, s1);
529*75f6d617Schristos 			}
530*75f6d617Schristos 		    }
531*75f6d617Schristos 		  byte_number++;
532*75f6d617Schristos 		  first_diff++;
533*75f6d617Schristos 		}
534*75f6d617Schristos 	      while (first_diff < smaller);
535*75f6d617Schristos 	      ret = EXIT_FAILURE;
536*75f6d617Schristos 	      break;
537*75f6d617Schristos 	    }
538*75f6d617Schristos 	}
539*75f6d617Schristos 
540*75f6d617Schristos       if (read0 != read1)
541*75f6d617Schristos 	{
542*75f6d617Schristos 	  if (comparison_type != type_status)
543*75f6d617Schristos 	    {
544*75f6d617Schristos 	      /* See POSIX 1003.1-2001 for this format.  */
545*75f6d617Schristos 	      fprintf (stderr, _("cmp: EOF on %s\n"), file[read1 < read0]);
546*75f6d617Schristos 	    }
547*75f6d617Schristos 
548*75f6d617Schristos 	  return EXIT_FAILURE;
549*75f6d617Schristos 	}
550*75f6d617Schristos     }
551*75f6d617Schristos   while (read0 == buf_size);
552*75f6d617Schristos 
553*75f6d617Schristos   return ret;
554*75f6d617Schristos }
555*75f6d617Schristos 
556*75f6d617Schristos /* Compare two blocks of memory P0 and P1 until they differ,
557*75f6d617Schristos    and count the number of '\n' occurrences in the common
558*75f6d617Schristos    part of P0 and P1.
559*75f6d617Schristos    If the blocks are not guaranteed to be different, put sentinels at the ends
560*75f6d617Schristos    of the blocks before calling this function.
561*75f6d617Schristos 
562*75f6d617Schristos    Return the offset of the first byte that differs.
563*75f6d617Schristos    Increment *COUNT by the count of '\n' occurrences.  */
564*75f6d617Schristos 
565*75f6d617Schristos static size_t
block_compare_and_count(word const * p0,word const * p1,off_t * count)566*75f6d617Schristos block_compare_and_count (word const *p0, word const *p1, off_t *count)
567*75f6d617Schristos {
568*75f6d617Schristos   word l;		/* One word from first buffer. */
569*75f6d617Schristos   word const *l0, *l1;	/* Pointers into each buffer. */
570*75f6d617Schristos   char const *c0, *c1;	/* Pointers for finding exact address. */
571*75f6d617Schristos   size_t cnt = 0;	/* Number of '\n' occurrences. */
572*75f6d617Schristos   word nnnn;		/* Newline, sizeof (word) times.  */
573*75f6d617Schristos   int i;
574*75f6d617Schristos 
575*75f6d617Schristos   nnnn = 0;
576*75f6d617Schristos   for (i = 0; i < sizeof nnnn; i++)
577*75f6d617Schristos     nnnn = (nnnn << CHAR_BIT) | '\n';
578*75f6d617Schristos 
579*75f6d617Schristos   /* Find the rough position of the first difference by reading words,
580*75f6d617Schristos      not bytes.  */
581*75f6d617Schristos 
582*75f6d617Schristos   for (l0 = p0, l1 = p1;  (l = *l0) == *l1;  l0++, l1++)
583*75f6d617Schristos     {
584*75f6d617Schristos       l ^= nnnn;
585*75f6d617Schristos       for (i = 0; i < sizeof l; i++)
586*75f6d617Schristos 	{
587*75f6d617Schristos 	  cnt += ! (unsigned char) l;
588*75f6d617Schristos 	  l >>= CHAR_BIT;
589*75f6d617Schristos 	}
590*75f6d617Schristos     }
591*75f6d617Schristos 
592*75f6d617Schristos   /* Find the exact differing position (endianness independent).  */
593*75f6d617Schristos 
594*75f6d617Schristos   for (c0 = (char const *) l0, c1 = (char const *) l1;
595*75f6d617Schristos        *c0 == *c1;
596*75f6d617Schristos        c0++, c1++)
597*75f6d617Schristos     cnt += *c0 == '\n';
598*75f6d617Schristos 
599*75f6d617Schristos   *count += cnt;
600*75f6d617Schristos   return c0 - (char const *) p0;
601*75f6d617Schristos }
602*75f6d617Schristos 
603*75f6d617Schristos /* Compare two blocks of memory P0 and P1 until they differ.
604*75f6d617Schristos    If the blocks are not guaranteed to be different, put sentinels at the ends
605*75f6d617Schristos    of the blocks before calling this function.
606*75f6d617Schristos 
607*75f6d617Schristos    Return the offset of the first byte that differs.  */
608*75f6d617Schristos 
609*75f6d617Schristos static size_t
block_compare(word const * p0,word const * p1)610*75f6d617Schristos block_compare (word const *p0, word const *p1)
611*75f6d617Schristos {
612*75f6d617Schristos   word const *l0, *l1;
613*75f6d617Schristos   char const *c0, *c1;
614*75f6d617Schristos 
615*75f6d617Schristos   /* Find the rough position of the first difference by reading words,
616*75f6d617Schristos      not bytes.  */
617*75f6d617Schristos 
618*75f6d617Schristos   for (l0 = p0, l1 = p1;  *l0 == *l1;  l0++, l1++)
619*75f6d617Schristos     continue;
620*75f6d617Schristos 
621*75f6d617Schristos   /* Find the exact differing position (endianness independent).  */
622*75f6d617Schristos 
623*75f6d617Schristos   for (c0 = (char const *) l0, c1 = (char const *) l1;
624*75f6d617Schristos        *c0 == *c1;
625*75f6d617Schristos        c0++, c1++)
626*75f6d617Schristos     continue;
627*75f6d617Schristos 
628*75f6d617Schristos   return c0 - (char const *) p0;
629*75f6d617Schristos }
630*75f6d617Schristos 
631*75f6d617Schristos /* Put into BUF the unsigned char C, making unprintable bytes
632*75f6d617Schristos    visible by quoting like cat -t does.  */
633*75f6d617Schristos 
634*75f6d617Schristos static void
sprintc(char * buf,unsigned char c)635*75f6d617Schristos sprintc (char *buf, unsigned char c)
636*75f6d617Schristos {
637*75f6d617Schristos   if (! ISPRINT (c))
638*75f6d617Schristos     {
639*75f6d617Schristos       if (c >= 128)
640*75f6d617Schristos 	{
641*75f6d617Schristos 	  *buf++ = 'M';
642*75f6d617Schristos 	  *buf++ = '-';
643*75f6d617Schristos 	  c -= 128;
644*75f6d617Schristos 	}
645*75f6d617Schristos       if (c < 32)
646*75f6d617Schristos 	{
647*75f6d617Schristos 	  *buf++ = '^';
648*75f6d617Schristos 	  c += 64;
649*75f6d617Schristos 	}
650*75f6d617Schristos       else if (c == 127)
651*75f6d617Schristos 	{
652*75f6d617Schristos 	  *buf++ = '^';
653*75f6d617Schristos 	  c = '?';
654*75f6d617Schristos 	}
655*75f6d617Schristos     }
656*75f6d617Schristos 
657*75f6d617Schristos   *buf++ = c;
658*75f6d617Schristos   *buf = 0;
659*75f6d617Schristos }
660*75f6d617Schristos 
661*75f6d617Schristos /* Position file F to ignore_initial[F] bytes from its initial position,
662*75f6d617Schristos    and yield its new position.  Don't try more than once.  */
663*75f6d617Schristos 
664*75f6d617Schristos static off_t
file_position(int f)665*75f6d617Schristos file_position (int f)
666*75f6d617Schristos {
667*75f6d617Schristos   static bool positioned[2];
668*75f6d617Schristos   static off_t position[2];
669*75f6d617Schristos 
670*75f6d617Schristos   if (! positioned[f])
671*75f6d617Schristos     {
672*75f6d617Schristos       positioned[f] = 1;
673*75f6d617Schristos       position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR);
674*75f6d617Schristos     }
675*75f6d617Schristos   return position[f];
676*75f6d617Schristos }
677