1170754Sdelphij/* cmp - compare two files byte by byte 2170754Sdelphij 3170754Sdelphij Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2001, 4170754Sdelphij 2002, 2004 Free Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This program is free software; you can redistribute it and/or modify 7170754Sdelphij it under the terms of the GNU General Public License as published by 8170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 9170754Sdelphij any later version. 10170754Sdelphij 11170754Sdelphij This program is distributed in the hope that it will be useful, 12170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 13170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14170754Sdelphij See the GNU General Public License for more details. 15170754Sdelphij 16170754Sdelphij You should have received a copy of the GNU General Public License 17170754Sdelphij along with this program; see the file COPYING. 18170754Sdelphij If not, write to the Free Software Foundation, 19170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20170754Sdelphij 21170754Sdelphij#include "system.h" 22170754Sdelphij#include "paths.h" 23170754Sdelphij 24170754Sdelphij#include <stdio.h> 25170754Sdelphij 26170754Sdelphij#include <c-stack.h> 27170754Sdelphij#include <cmpbuf.h> 28170754Sdelphij#include <error.h> 29170754Sdelphij#include <exit.h> 30170754Sdelphij#include <exitfail.h> 31170754Sdelphij#include <file-type.h> 32170754Sdelphij#include <getopt.h> 33170754Sdelphij#include <hard-locale.h> 34170754Sdelphij#include <inttostr.h> 35170754Sdelphij#include <setmode.h> 36170754Sdelphij#include <unlocked-io.h> 37170754Sdelphij#include <version-etc.h> 38170754Sdelphij#include <xalloc.h> 39170754Sdelphij#include <xstrtol.h> 40170754Sdelphij 41170754Sdelphij#if defined LC_MESSAGES && ENABLE_NLS 42170754Sdelphij# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES) 43170754Sdelphij#else 44170754Sdelphij# define hard_locale_LC_MESSAGES 0 45170754Sdelphij#endif 46170754Sdelphij 47170754Sdelphijstatic int cmp (void); 48170754Sdelphijstatic off_t file_position (int); 49170754Sdelphijstatic size_t block_compare (word const *, word const *); 50170754Sdelphijstatic size_t block_compare_and_count (word const *, word const *, off_t *); 51170754Sdelphijstatic void sprintc (char *, unsigned char); 52170754Sdelphij 53170754Sdelphij/* Name under which this program was invoked. */ 54170754Sdelphijchar *program_name; 55170754Sdelphij 56170754Sdelphij/* Filenames of the compared files. */ 57170754Sdelphijstatic char const *file[2]; 58170754Sdelphij 59170754Sdelphij/* File descriptors of the files. */ 60170754Sdelphijstatic int file_desc[2]; 61170754Sdelphij 62170754Sdelphij/* Status of the files. */ 63170754Sdelphijstatic struct stat stat_buf[2]; 64170754Sdelphij 65170754Sdelphij/* Read buffers for the files. */ 66170754Sdelphijstatic word *buffer[2]; 67170754Sdelphij 68170754Sdelphij/* Optimal block size for the files. */ 69170754Sdelphijstatic size_t buf_size; 70170754Sdelphij 71170754Sdelphij/* Initial prefix to ignore for each file. */ 72170754Sdelphijstatic off_t ignore_initial[2]; 73170754Sdelphij 74170754Sdelphij/* Number of bytes to compare. */ 75170754Sdelphijstatic uintmax_t bytes = UINTMAX_MAX; 76170754Sdelphij 77170754Sdelphij/* Output format. */ 78170754Sdelphijstatic enum comparison_type 79170754Sdelphij { 80170754Sdelphij type_first_diff, /* Print the first difference. */ 81170754Sdelphij type_all_diffs, /* Print all differences. */ 82170754Sdelphij type_status /* Exit status only. */ 83170754Sdelphij } comparison_type; 84170754Sdelphij 85170754Sdelphij/* If nonzero, print values of bytes quoted like cat -t does. */ 86170754Sdelphijstatic bool opt_print_bytes; 87170754Sdelphij 88170754Sdelphij/* Values for long options that do not have single-letter equivalents. */ 89170754Sdelphijenum 90170754Sdelphij{ 91170754Sdelphij HELP_OPTION = CHAR_MAX + 1 92170754Sdelphij}; 93170754Sdelphij 94170754Sdelphijstatic struct option const long_options[] = 95170754Sdelphij{ 96170754Sdelphij {"print-bytes", 0, 0, 'b'}, 97170754Sdelphij {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */ 98170754Sdelphij {"ignore-initial", 1, 0, 'i'}, 99170754Sdelphij {"verbose", 0, 0, 'l'}, 100170754Sdelphij {"bytes", 1, 0, 'n'}, 101170754Sdelphij {"silent", 0, 0, 's'}, 102170754Sdelphij {"quiet", 0, 0, 's'}, 103170754Sdelphij {"version", 0, 0, 'v'}, 104170754Sdelphij {"help", 0, 0, HELP_OPTION}, 105170754Sdelphij {0, 0, 0, 0} 106170754Sdelphij}; 107170754Sdelphij 108170754Sdelphijstatic void try_help (char const *, char const *) __attribute__((noreturn)); 109170754Sdelphijstatic void 110170754Sdelphijtry_help (char const *reason_msgid, char const *operand) 111170754Sdelphij{ 112170754Sdelphij if (reason_msgid) 113170754Sdelphij error (0, 0, _(reason_msgid), operand); 114170754Sdelphij error (EXIT_TROUBLE, 0, 115170754Sdelphij _("Try `%s --help' for more information."), program_name); 116170754Sdelphij abort (); 117170754Sdelphij} 118170754Sdelphij 119170754Sdelphijstatic char const valid_suffixes[] = "kKMGTPEZY0"; 120170754Sdelphij 121170754Sdelphij/* Update ignore_initial[F] according to the result of parsing an 122170754Sdelphij *operand ARGPTR of --ignore-initial, updating *ARGPTR to point 123170754Sdelphij *after the operand. If DELIMITER is nonzero, the operand may be 124170754Sdelphij *followed by DELIMITER; otherwise it must be null-terminated. */ 125170754Sdelphijstatic void 126170754Sdelphijspecify_ignore_initial (int f, char **argptr, char delimiter) 127170754Sdelphij{ 128170754Sdelphij uintmax_t val; 129170754Sdelphij off_t o; 130170754Sdelphij char const *arg = *argptr; 131170754Sdelphij strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes); 132170754Sdelphij if (! (e == LONGINT_OK 133170754Sdelphij || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter)) 134170754Sdelphij || (o = val) < 0 || o != val || val == UINTMAX_MAX) 135170754Sdelphij try_help ("invalid --ignore-initial value `%s'", arg); 136170754Sdelphij if (ignore_initial[f] < o) 137170754Sdelphij ignore_initial[f] = o; 138170754Sdelphij} 139170754Sdelphij 140170754Sdelphij/* Specify the output format. */ 141170754Sdelphijstatic void 142170754Sdelphijspecify_comparison_type (enum comparison_type t) 143170754Sdelphij{ 144170754Sdelphij if (comparison_type && comparison_type != t) 145170754Sdelphij try_help ("options -l and -s are incompatible", 0); 146170754Sdelphij comparison_type = t; 147170754Sdelphij} 148170754Sdelphij 149170754Sdelphijstatic void 150170754Sdelphijcheck_stdout (void) 151170754Sdelphij{ 152170754Sdelphij if (ferror (stdout)) 153170754Sdelphij error (EXIT_TROUBLE, 0, "%s", _("write failed")); 154170754Sdelphij else if (fclose (stdout) != 0) 155170754Sdelphij error (EXIT_TROUBLE, errno, "%s", _("standard output")); 156170754Sdelphij} 157170754Sdelphij 158170754Sdelphijstatic char const * const option_help_msgid[] = { 159170754Sdelphij N_("-b --print-bytes Print differing bytes."), 160170754Sdelphij N_("-i SKIP --ignore-initial=SKIP Skip the first SKIP bytes of input."), 161170754Sdelphij N_("-i SKIP1:SKIP2 --ignore-initial=SKIP1:SKIP2"), 162170754Sdelphij N_(" Skip the first SKIP1 bytes of FILE1 and the first SKIP2 bytes of FILE2."), 163170754Sdelphij N_("-l --verbose Output byte numbers and values of all differing bytes."), 164170754Sdelphij N_("-n LIMIT --bytes=LIMIT Compare at most LIMIT bytes."), 165170754Sdelphij N_("-s --quiet --silent Output nothing; yield exit status only."), 166170754Sdelphij N_("-v --version Output version info."), 167170754Sdelphij N_("--help Output this help."), 168170754Sdelphij 0 169170754Sdelphij}; 170170754Sdelphij 171170754Sdelphijstatic void 172170754Sdelphijusage (void) 173170754Sdelphij{ 174170754Sdelphij char const * const *p; 175170754Sdelphij 176170754Sdelphij printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"), 177170754Sdelphij program_name); 178170754Sdelphij printf ("%s\n\n", _("Compare two files byte by byte.")); 179170754Sdelphij for (p = option_help_msgid; *p; p++) 180170754Sdelphij printf (" %s\n", _(*p)); 181170754Sdelphij printf ("\n%s\n%s\n\n%s\n%s\n\n%s\n", 182170754Sdelphij _("SKIP1 and SKIP2 are the number of bytes to skip in each file."), 183170754Sdelphij _("SKIP values may be followed by the following multiplicative suffixes:\n\ 184170754SdelphijkB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\ 185170754SdelphijGB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."), 186170754Sdelphij _("If a FILE is `-' or missing, read standard input."), 187170754Sdelphij _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."), 188170754Sdelphij _("Report bugs to <bug-gnu-utils@gnu.org>.")); 189170754Sdelphij} 190170754Sdelphij 191170754Sdelphijint 192170754Sdelphijmain (int argc, char **argv) 193170754Sdelphij{ 194170754Sdelphij int c, f, exit_status; 195170754Sdelphij size_t words_per_buffer; 196170754Sdelphij 197170754Sdelphij exit_failure = EXIT_TROUBLE; 198170754Sdelphij initialize_main (&argc, &argv); 199170754Sdelphij program_name = argv[0]; 200170754Sdelphij setlocale (LC_ALL, ""); 201170754Sdelphij bindtextdomain (PACKAGE, LOCALEDIR); 202170754Sdelphij textdomain (PACKAGE); 203170754Sdelphij c_stack_action (0); 204170754Sdelphij 205170754Sdelphij /* Parse command line options. */ 206170754Sdelphij 207170754Sdelphij while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0)) 208170754Sdelphij != -1) 209170754Sdelphij switch (c) 210170754Sdelphij { 211170754Sdelphij case 'b': 212170754Sdelphij case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */ 213170754Sdelphij opt_print_bytes = true; 214170754Sdelphij break; 215170754Sdelphij 216170754Sdelphij case 'i': 217170754Sdelphij specify_ignore_initial (0, &optarg, ':'); 218170754Sdelphij if (*optarg++ == ':') 219170754Sdelphij specify_ignore_initial (1, &optarg, 0); 220170754Sdelphij else if (ignore_initial[1] < ignore_initial[0]) 221170754Sdelphij ignore_initial[1] = ignore_initial[0]; 222170754Sdelphij break; 223170754Sdelphij 224170754Sdelphij case 'l': 225170754Sdelphij specify_comparison_type (type_all_diffs); 226170754Sdelphij break; 227170754Sdelphij 228170754Sdelphij case 'n': 229170754Sdelphij { 230170754Sdelphij uintmax_t n; 231170754Sdelphij if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK) 232170754Sdelphij try_help ("invalid --bytes value `%s'", optarg); 233170754Sdelphij if (n < bytes) 234170754Sdelphij bytes = n; 235170754Sdelphij } 236170754Sdelphij break; 237170754Sdelphij 238170754Sdelphij case 's': 239170754Sdelphij specify_comparison_type (type_status); 240170754Sdelphij break; 241170754Sdelphij 242170754Sdelphij case 'v': 243170754Sdelphij /* TRANSLATORS: Please translate the second "o" in "Torbjorn 244170754Sdelphij Granlund" to an o-with-umlaut (U+00F6, LATIN SMALL LETTER O 245170754Sdelphij WITH DIAERESIS) if possible. */ 246170754Sdelphij version_etc (stdout, "cmp", PACKAGE_NAME, PACKAGE_VERSION, 247170754Sdelphij _("Torbjorn Granlund"), "David MacKenzie", (char *) 0); 248170754Sdelphij check_stdout (); 249170754Sdelphij return EXIT_SUCCESS; 250170754Sdelphij 251170754Sdelphij case HELP_OPTION: 252170754Sdelphij usage (); 253170754Sdelphij check_stdout (); 254170754Sdelphij return EXIT_SUCCESS; 255170754Sdelphij 256170754Sdelphij default: 257170754Sdelphij try_help (0, 0); 258170754Sdelphij } 259170754Sdelphij 260170754Sdelphij if (optind == argc) 261170754Sdelphij try_help ("missing operand after `%s'", argv[argc - 1]); 262170754Sdelphij 263170754Sdelphij file[0] = argv[optind++]; 264170754Sdelphij file[1] = optind < argc ? argv[optind++] : "-"; 265170754Sdelphij 266170754Sdelphij for (f = 0; f < 2 && optind < argc; f++) 267170754Sdelphij { 268170754Sdelphij char *arg = argv[optind++]; 269170754Sdelphij specify_ignore_initial (f, &arg, 0); 270170754Sdelphij } 271170754Sdelphij 272170754Sdelphij if (optind < argc) 273170754Sdelphij try_help ("extra operand `%s'", argv[optind]); 274170754Sdelphij 275170754Sdelphij for (f = 0; f < 2; f++) 276170754Sdelphij { 277170754Sdelphij /* If file[1] is "-", treat it first; this avoids a misdiagnostic if 278170754Sdelphij stdin is closed and opening file[0] yields file descriptor 0. */ 279170754Sdelphij int f1 = f ^ (strcmp (file[1], "-") == 0); 280170754Sdelphij 281170754Sdelphij /* Two files with the same name and offset are identical. 282170754Sdelphij But wait until we open the file once, for proper diagnostics. */ 283170754Sdelphij if (f && ignore_initial[0] == ignore_initial[1] 284170754Sdelphij && file_name_cmp (file[0], file[1]) == 0) 285170754Sdelphij return EXIT_SUCCESS; 286170754Sdelphij 287170754Sdelphij file_desc[f1] = (strcmp (file[f1], "-") == 0 288170754Sdelphij ? STDIN_FILENO 289170754Sdelphij : open (file[f1], O_RDONLY, 0)); 290170754Sdelphij if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0) 291170754Sdelphij { 292170754Sdelphij if (file_desc[f1] < 0 && comparison_type == type_status) 293170754Sdelphij exit (EXIT_TROUBLE); 294170754Sdelphij else 295170754Sdelphij error (EXIT_TROUBLE, errno, "%s", file[f1]); 296170754Sdelphij } 297170754Sdelphij 298170754Sdelphij set_binary_mode (file_desc[f1], true); 299170754Sdelphij } 300170754Sdelphij 301170754Sdelphij /* If the files are links to the same inode and have the same file position, 302170754Sdelphij they are identical. */ 303170754Sdelphij 304170754Sdelphij if (0 < same_file (&stat_buf[0], &stat_buf[1]) 305170754Sdelphij && same_file_attributes (&stat_buf[0], &stat_buf[1]) 306170754Sdelphij && file_position (0) == file_position (1)) 307170754Sdelphij return EXIT_SUCCESS; 308170754Sdelphij 309170754Sdelphij /* If output is redirected to the null device, we may assume `-s'. */ 310170754Sdelphij 311170754Sdelphij if (comparison_type != type_status) 312170754Sdelphij { 313170754Sdelphij struct stat outstat, nullstat; 314170754Sdelphij 315170754Sdelphij if (fstat (STDOUT_FILENO, &outstat) == 0 316170754Sdelphij && stat (NULL_DEVICE, &nullstat) == 0 317170754Sdelphij && 0 < same_file (&outstat, &nullstat)) 318170754Sdelphij comparison_type = type_status; 319170754Sdelphij } 320170754Sdelphij 321170754Sdelphij /* If only a return code is needed, 322170754Sdelphij and if both input descriptors are associated with plain files, 323170754Sdelphij conclude that the files differ if they have different sizes 324170754Sdelphij and if more bytes will be compared than are in the smaller file. */ 325170754Sdelphij 326170754Sdelphij if (comparison_type == type_status 327170754Sdelphij && S_ISREG (stat_buf[0].st_mode) 328170754Sdelphij && S_ISREG (stat_buf[1].st_mode)) 329170754Sdelphij { 330170754Sdelphij off_t s0 = stat_buf[0].st_size - file_position (0); 331170754Sdelphij off_t s1 = stat_buf[1].st_size - file_position (1); 332170754Sdelphij if (s0 < 0) 333170754Sdelphij s0 = 0; 334170754Sdelphij if (s1 < 0) 335170754Sdelphij s1 = 0; 336170754Sdelphij if (s0 != s1 && MIN (s0, s1) < bytes) 337170754Sdelphij exit (EXIT_FAILURE); 338170754Sdelphij } 339170754Sdelphij 340170754Sdelphij /* Get the optimal block size of the files. */ 341170754Sdelphij 342170754Sdelphij buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]), 343170754Sdelphij STAT_BLOCKSIZE (stat_buf[1]), 344170754Sdelphij PTRDIFF_MAX - sizeof (word)); 345170754Sdelphij 346170754Sdelphij /* Allocate word-aligned buffers, with space for sentinels at the end. */ 347170754Sdelphij 348170754Sdelphij words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word); 349170754Sdelphij buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer); 350170754Sdelphij buffer[1] = buffer[0] + words_per_buffer; 351170754Sdelphij 352170754Sdelphij exit_status = cmp (); 353170754Sdelphij 354170754Sdelphij for (f = 0; f < 2; f++) 355170754Sdelphij if (close (file_desc[f]) != 0) 356170754Sdelphij error (EXIT_TROUBLE, errno, "%s", file[f]); 357170754Sdelphij if (exit_status != 0 && comparison_type != type_status) 358170754Sdelphij check_stdout (); 359170754Sdelphij exit (exit_status); 360170754Sdelphij return exit_status; 361170754Sdelphij} 362170754Sdelphij 363170754Sdelphij/* Compare the two files already open on `file_desc[0]' and `file_desc[1]', 364170754Sdelphij using `buffer[0]' and `buffer[1]'. 365170754Sdelphij Return EXIT_SUCCESS if identical, EXIT_FAILURE if different, 366170754Sdelphij >1 if error. */ 367170754Sdelphij 368170754Sdelphijstatic int 369170754Sdelphijcmp (void) 370170754Sdelphij{ 371170754Sdelphij off_t line_number = 1; /* Line number (1...) of difference. */ 372170754Sdelphij off_t byte_number = 1; /* Byte number (1...) of difference. */ 373170754Sdelphij uintmax_t remaining = bytes; /* Remaining number of bytes to compare. */ 374170754Sdelphij size_t read0, read1; /* Number of bytes read from each file. */ 375170754Sdelphij size_t first_diff; /* Offset (0...) in buffers of 1st diff. */ 376170754Sdelphij size_t smaller; /* The lesser of `read0' and `read1'. */ 377170754Sdelphij word *buffer0 = buffer[0]; 378170754Sdelphij word *buffer1 = buffer[1]; 379170754Sdelphij char *buf0 = (char *) buffer0; 380170754Sdelphij char *buf1 = (char *) buffer1; 381170754Sdelphij int ret = EXIT_SUCCESS; 382170754Sdelphij int f; 383170754Sdelphij int offset_width; 384170754Sdelphij 385170754Sdelphij if (comparison_type == type_all_diffs) 386170754Sdelphij { 387170754Sdelphij off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t)); 388170754Sdelphij 389170754Sdelphij for (f = 0; f < 2; f++) 390170754Sdelphij if (S_ISREG (stat_buf[f].st_mode)) 391170754Sdelphij { 392170754Sdelphij off_t file_bytes = stat_buf[f].st_size - file_position (f); 393170754Sdelphij if (file_bytes < byte_number_max) 394170754Sdelphij byte_number_max = file_bytes; 395170754Sdelphij } 396170754Sdelphij 397170754Sdelphij for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++) 398170754Sdelphij continue; 399170754Sdelphij } 400170754Sdelphij 401170754Sdelphij for (f = 0; f < 2; f++) 402170754Sdelphij { 403170754Sdelphij off_t ig = ignore_initial[f]; 404170754Sdelphij if (ig && file_position (f) == -1) 405170754Sdelphij { 406170754Sdelphij /* lseek failed; read and discard the ignored initial prefix. */ 407170754Sdelphij do 408170754Sdelphij { 409170754Sdelphij size_t bytes_to_read = MIN (ig, buf_size); 410170754Sdelphij size_t r = block_read (file_desc[f], buf0, bytes_to_read); 411170754Sdelphij if (r != bytes_to_read) 412170754Sdelphij { 413170754Sdelphij if (r == SIZE_MAX) 414170754Sdelphij error (EXIT_TROUBLE, errno, "%s", file[f]); 415170754Sdelphij break; 416170754Sdelphij } 417170754Sdelphij ig -= r; 418170754Sdelphij } 419170754Sdelphij while (ig); 420170754Sdelphij } 421170754Sdelphij } 422170754Sdelphij 423170754Sdelphij do 424170754Sdelphij { 425170754Sdelphij size_t bytes_to_read = buf_size; 426170754Sdelphij 427170754Sdelphij if (remaining != UINTMAX_MAX) 428170754Sdelphij { 429170754Sdelphij if (remaining < bytes_to_read) 430170754Sdelphij bytes_to_read = remaining; 431170754Sdelphij remaining -= bytes_to_read; 432170754Sdelphij } 433170754Sdelphij 434170754Sdelphij read0 = block_read (file_desc[0], buf0, bytes_to_read); 435170754Sdelphij if (read0 == SIZE_MAX) 436170754Sdelphij error (EXIT_TROUBLE, errno, "%s", file[0]); 437170754Sdelphij read1 = block_read (file_desc[1], buf1, bytes_to_read); 438170754Sdelphij if (read1 == SIZE_MAX) 439170754Sdelphij error (EXIT_TROUBLE, errno, "%s", file[1]); 440170754Sdelphij 441170754Sdelphij /* Insert sentinels for the block compare. */ 442170754Sdelphij 443170754Sdelphij buf0[read0] = ~buf1[read0]; 444170754Sdelphij buf1[read1] = ~buf0[read1]; 445170754Sdelphij 446170754Sdelphij /* If the line number should be written for differing files, 447170754Sdelphij compare the blocks and count the number of newlines 448170754Sdelphij simultaneously. */ 449170754Sdelphij first_diff = (comparison_type == type_first_diff 450170754Sdelphij ? block_compare_and_count (buffer0, buffer1, &line_number) 451170754Sdelphij : block_compare (buffer0, buffer1)); 452170754Sdelphij 453170754Sdelphij byte_number += first_diff; 454170754Sdelphij smaller = MIN (read0, read1); 455170754Sdelphij 456170754Sdelphij if (first_diff < smaller) 457170754Sdelphij { 458170754Sdelphij switch (comparison_type) 459170754Sdelphij { 460170754Sdelphij case type_first_diff: 461170754Sdelphij { 462170754Sdelphij char byte_buf[INT_BUFSIZE_BOUND (off_t)]; 463170754Sdelphij char line_buf[INT_BUFSIZE_BOUND (off_t)]; 464170754Sdelphij char const *byte_num = offtostr (byte_number, byte_buf); 465170754Sdelphij char const *line_num = offtostr (line_number, line_buf); 466170754Sdelphij if (!opt_print_bytes) 467170754Sdelphij { 468170754Sdelphij /* See POSIX 1003.1-2001 for this format. This 469170754Sdelphij message is used only in the POSIX locale, so it 470170754Sdelphij need not be translated. */ 471170754Sdelphij static char const char_message[] = 472170754Sdelphij "%s %s differ: char %s, line %s\n"; 473170754Sdelphij 474170754Sdelphij /* The POSIX rationale recommends using the word 475170754Sdelphij "byte" outside the POSIX locale. Some gettext 476170754Sdelphij implementations translate even in the POSIX 477170754Sdelphij locale if certain other environment variables 478170754Sdelphij are set, so use "byte" if a translation is 479170754Sdelphij available, or if outside the POSIX locale. */ 480170754Sdelphij static char const byte_msgid[] = 481170754Sdelphij N_("%s %s differ: byte %s, line %s\n"); 482170754Sdelphij char const *byte_message = _(byte_msgid); 483170754Sdelphij bool use_byte_message = (byte_message != byte_msgid 484170754Sdelphij || hard_locale_LC_MESSAGES); 485170754Sdelphij 486170754Sdelphij printf (use_byte_message ? byte_message : char_message, 487170754Sdelphij file[0], file[1], byte_num, line_num); 488170754Sdelphij } 489170754Sdelphij else 490170754Sdelphij { 491170754Sdelphij unsigned char c0 = buf0[first_diff]; 492170754Sdelphij unsigned char c1 = buf1[first_diff]; 493170754Sdelphij char s0[5]; 494170754Sdelphij char s1[5]; 495170754Sdelphij sprintc (s0, c0); 496170754Sdelphij sprintc (s1, c1); 497170754Sdelphij printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"), 498170754Sdelphij file[0], file[1], byte_num, line_num, 499170754Sdelphij c0, s0, c1, s1); 500170754Sdelphij } 501170754Sdelphij } 502170754Sdelphij /* Fall through. */ 503170754Sdelphij case type_status: 504170754Sdelphij return EXIT_FAILURE; 505170754Sdelphij 506170754Sdelphij case type_all_diffs: 507170754Sdelphij do 508170754Sdelphij { 509170754Sdelphij unsigned char c0 = buf0[first_diff]; 510170754Sdelphij unsigned char c1 = buf1[first_diff]; 511170754Sdelphij if (c0 != c1) 512170754Sdelphij { 513170754Sdelphij char byte_buf[INT_BUFSIZE_BOUND (off_t)]; 514170754Sdelphij char const *byte_num = offtostr (byte_number, byte_buf); 515170754Sdelphij if (!opt_print_bytes) 516170754Sdelphij { 517170754Sdelphij /* See POSIX 1003.1-2001 for this format. */ 518170754Sdelphij printf ("%*s %3o %3o\n", 519170754Sdelphij offset_width, byte_num, c0, c1); 520170754Sdelphij } 521170754Sdelphij else 522170754Sdelphij { 523170754Sdelphij char s0[5]; 524170754Sdelphij char s1[5]; 525170754Sdelphij sprintc (s0, c0); 526170754Sdelphij sprintc (s1, c1); 527170754Sdelphij printf ("%*s %3o %-4s %3o %s\n", 528170754Sdelphij offset_width, byte_num, c0, s0, c1, s1); 529170754Sdelphij } 530170754Sdelphij } 531170754Sdelphij byte_number++; 532170754Sdelphij first_diff++; 533170754Sdelphij } 534170754Sdelphij while (first_diff < smaller); 535170754Sdelphij ret = EXIT_FAILURE; 536170754Sdelphij break; 537170754Sdelphij } 538170754Sdelphij } 539170754Sdelphij 540170754Sdelphij if (read0 != read1) 541170754Sdelphij { 542170754Sdelphij if (comparison_type != type_status) 543170754Sdelphij { 544170754Sdelphij /* See POSIX 1003.1-2001 for this format. */ 545170754Sdelphij fprintf (stderr, _("cmp: EOF on %s\n"), file[read1 < read0]); 546170754Sdelphij } 547170754Sdelphij 548170754Sdelphij return EXIT_FAILURE; 549170754Sdelphij } 550170754Sdelphij } 551170754Sdelphij while (read0 == buf_size); 552170754Sdelphij 553170754Sdelphij return ret; 554170754Sdelphij} 555170754Sdelphij 556170754Sdelphij/* Compare two blocks of memory P0 and P1 until they differ, 557170754Sdelphij and count the number of '\n' occurrences in the common 558170754Sdelphij part of P0 and P1. 559170754Sdelphij If the blocks are not guaranteed to be different, put sentinels at the ends 560170754Sdelphij of the blocks before calling this function. 561170754Sdelphij 562170754Sdelphij Return the offset of the first byte that differs. 563170754Sdelphij Increment *COUNT by the count of '\n' occurrences. */ 564170754Sdelphij 565170754Sdelphijstatic size_t 566170754Sdelphijblock_compare_and_count (word const *p0, word const *p1, off_t *count) 567170754Sdelphij{ 568170754Sdelphij word l; /* One word from first buffer. */ 569170754Sdelphij word const *l0, *l1; /* Pointers into each buffer. */ 570170754Sdelphij char const *c0, *c1; /* Pointers for finding exact address. */ 571170754Sdelphij size_t cnt = 0; /* Number of '\n' occurrences. */ 572170754Sdelphij word nnnn; /* Newline, sizeof (word) times. */ 573170754Sdelphij int i; 574170754Sdelphij 575170754Sdelphij nnnn = 0; 576170754Sdelphij for (i = 0; i < sizeof nnnn; i++) 577170754Sdelphij nnnn = (nnnn << CHAR_BIT) | '\n'; 578170754Sdelphij 579170754Sdelphij /* Find the rough position of the first difference by reading words, 580170754Sdelphij not bytes. */ 581170754Sdelphij 582170754Sdelphij for (l0 = p0, l1 = p1; (l = *l0) == *l1; l0++, l1++) 583170754Sdelphij { 584170754Sdelphij l ^= nnnn; 585170754Sdelphij for (i = 0; i < sizeof l; i++) 586170754Sdelphij { 587170754Sdelphij unsigned char uc = l; 588170754Sdelphij cnt += ! uc; 589170754Sdelphij l >>= CHAR_BIT; 590170754Sdelphij } 591170754Sdelphij } 592170754Sdelphij 593170754Sdelphij /* Find the exact differing position (endianness independent). */ 594170754Sdelphij 595170754Sdelphij for (c0 = (char const *) l0, c1 = (char const *) l1; 596170754Sdelphij *c0 == *c1; 597170754Sdelphij c0++, c1++) 598170754Sdelphij cnt += *c0 == '\n'; 599170754Sdelphij 600170754Sdelphij *count += cnt; 601170754Sdelphij return c0 - (char const *) p0; 602170754Sdelphij} 603170754Sdelphij 604170754Sdelphij/* Compare two blocks of memory P0 and P1 until they differ. 605170754Sdelphij If the blocks are not guaranteed to be different, put sentinels at the ends 606170754Sdelphij of the blocks before calling this function. 607170754Sdelphij 608170754Sdelphij Return the offset of the first byte that differs. */ 609170754Sdelphij 610170754Sdelphijstatic size_t 611170754Sdelphijblock_compare (word const *p0, word const *p1) 612170754Sdelphij{ 613170754Sdelphij word const *l0, *l1; 614170754Sdelphij char const *c0, *c1; 615170754Sdelphij 616170754Sdelphij /* Find the rough position of the first difference by reading words, 617170754Sdelphij not bytes. */ 618170754Sdelphij 619170754Sdelphij for (l0 = p0, l1 = p1; *l0 == *l1; l0++, l1++) 620170754Sdelphij continue; 621170754Sdelphij 622170754Sdelphij /* Find the exact differing position (endianness independent). */ 623170754Sdelphij 624170754Sdelphij for (c0 = (char const *) l0, c1 = (char const *) l1; 625170754Sdelphij *c0 == *c1; 626170754Sdelphij c0++, c1++) 627170754Sdelphij continue; 628170754Sdelphij 629170754Sdelphij return c0 - (char const *) p0; 630170754Sdelphij} 631170754Sdelphij 632170754Sdelphij/* Put into BUF the unsigned char C, making unprintable bytes 633170754Sdelphij visible by quoting like cat -t does. */ 634170754Sdelphij 635170754Sdelphijstatic void 636170754Sdelphijsprintc (char *buf, unsigned char c) 637170754Sdelphij{ 638170754Sdelphij if (! isprint (c)) 639170754Sdelphij { 640170754Sdelphij if (c >= 128) 641170754Sdelphij { 642170754Sdelphij *buf++ = 'M'; 643170754Sdelphij *buf++ = '-'; 644170754Sdelphij c -= 128; 645170754Sdelphij } 646170754Sdelphij if (c < 32) 647170754Sdelphij { 648170754Sdelphij *buf++ = '^'; 649170754Sdelphij c += 64; 650170754Sdelphij } 651170754Sdelphij else if (c == 127) 652170754Sdelphij { 653170754Sdelphij *buf++ = '^'; 654170754Sdelphij c = '?'; 655170754Sdelphij } 656170754Sdelphij } 657170754Sdelphij 658170754Sdelphij *buf++ = c; 659170754Sdelphij *buf = 0; 660170754Sdelphij} 661170754Sdelphij 662170754Sdelphij/* Position file F to ignore_initial[F] bytes from its initial position, 663170754Sdelphij and yield its new position. Don't try more than once. */ 664170754Sdelphij 665170754Sdelphijstatic off_t 666170754Sdelphijfile_position (int f) 667170754Sdelphij{ 668170754Sdelphij static bool positioned[2]; 669170754Sdelphij static off_t position[2]; 670170754Sdelphij 671170754Sdelphij if (! positioned[f]) 672170754Sdelphij { 673170754Sdelphij positioned[f] = true; 674170754Sdelphij position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR); 675170754Sdelphij } 676170754Sdelphij return position[f]; 677170754Sdelphij} 678