diff --git a/NEWS b/NEWS index 3ecd111..423c0ee 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,9 @@ GNU diffutils NEWS -*- outline -*- option-arguments as if they were large positive numbers. [bug#35256 introduced in 2.8] +** Improvements + + New cmp option: -x or --verbose-hex. * Noteworthy changes in release 3.7 (2018-12-31) [stable] diff --git a/doc/diffutils.texi b/doc/diffutils.texi index 3a780db..c5ef984 100644 --- a/doc/diffutils.texi +++ b/doc/diffutils.texi @@ -3581,6 +3581,11 @@ the files differ. @item -v @itemx --version Output version information and then exit. + +@item -x +@itemx --verbose-hex +Like @option{--verbose}, but with hexadecimal numbers, and byte numbers +starting at 0. @end table In the above table, operands that are byte counts are normally diff --git a/src/cmp.c b/src/cmp.c index 5152ca0..555665c 100644 --- a/src/cmp.c +++ b/src/cmp.c @@ -90,6 +90,9 @@ static enum comparison_type /* If nonzero, print values of bytes quoted like cat -t does. */ static bool opt_print_bytes; +/* Show verbose output using hexadecimal numbers. */ +static bool opt_verbose_hex; + /* Values for long options that do not have single-letter equivalents. */ enum { @@ -106,6 +109,7 @@ static struct option const long_options[] = {"silent", 0, 0, 's'}, {"quiet", 0, 0, 's'}, {"version", 0, 0, 'v'}, + {"verbose-hex", 0, 0, 'x'}, {"help", 0, 0, HELP_OPTION}, {0, 0, 0, 0} }; @@ -168,6 +172,8 @@ static char const * const option_help_msgid[] = { N_("-s, --quiet, --silent suppress all normal output"), N_(" --help display this help and exit"), N_("-v, --version output version information and exit"), + N_("-x, --verbose-hex output byte numbers and differing byte values\n" + " using hexadecimal numbers"), 0 }; @@ -214,7 +220,7 @@ main (int argc, char **argv) /* Parse command line options. */ - while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0)) + while ((c = getopt_long (argc, argv, "bci:ln:svx", long_options, 0)) != -1) switch (c) { @@ -256,6 +262,11 @@ main (int argc, char **argv) check_stdout (); return EXIT_SUCCESS; + case 'x': + specify_comparison_type (type_all_diffs); + opt_verbose_hex = true; + break; + case HELP_OPTION: usage (); check_stdout (); @@ -378,8 +389,10 @@ static int cmp (void) { bool at_line_start = true; + off_t start_offset = opt_verbose_hex ? 0 : 1; off_t line_number = 1; /* Line number (1...) of difference. */ - off_t byte_number = 1; /* Byte number (1...) of difference. */ + off_t byte_number = start_offset; + /* Byte number (0/1...) of difference. */ intmax_t remaining = bytes; /* Remaining bytes to compare, or -1. */ size_t read0, read1; /* Number of bytes read from each file. */ size_t first_diff; /* Offset (0...) in buffers of 1st diff. */ @@ -396,6 +409,7 @@ cmp (void) { off_t byte_number_max = (0 <= bytes && bytes <= TYPE_MAXIMUM (off_t) ? bytes : TYPE_MAXIMUM (off_t)); + int base = opt_verbose_hex ? 16 : 10; for (f = 0; f < 2; f++) if (S_ISREG (stat_buf[f].st_mode)) @@ -405,8 +419,13 @@ cmp (void) byte_number_max = file_bytes; } - for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++) + byte_number_max = byte_number_max + start_offset - 1; + + for (offset_width = 1; (byte_number_max /= base) != 0; offset_width++) continue; + + if (opt_verbose_hex && offset_width < 8) + offset_width = 8; } for (f = 0; f < 2; f++) @@ -527,13 +546,14 @@ cmp (void) unsigned char c1 = buf1[first_diff]; if (c0 != c1) { - char byte_buf[INT_BUFSIZE_BOUND (off_t)]; - char const *byte_num = offtostr (byte_number, byte_buf); if (!opt_print_bytes) { /* See POSIX for this format. */ - printf ("%*s %3o %3o\n", - offset_width, byte_num, c0, c1); + printf (opt_verbose_hex ? + "%0*"PRIxMAX" %.2x %.2x\n" : + "%*"PRIuMAX" %3o %3o\n", + offset_width, (uintmax_t) byte_number, + c0, c1); } else { @@ -541,8 +561,11 @@ cmp (void) char s1[5]; sprintc (s0, c0); sprintc (s1, c1); - printf ("%*s %3o %-4s %3o %s\n", - offset_width, byte_num, c0, s0, c1, s1); + printf (opt_verbose_hex ? + "%0*"PRIxMAX" %.2x %-4s %.2x %s\n" : + "%*"PRIuMAX" %3o %-4s %3o %s\n", + offset_width, (uintmax_t) byte_number, + c0, s0, c1, s1); } } byte_number++; @@ -567,30 +590,30 @@ cmp (void) /* POSIX says that each of these format strings must be "cmp: EOF on %s", optionally followed by a blank and extra text sans newline, then terminated by "\n". */ - if (byte_number == 1) + if (byte_number == start_offset) fprintf (stderr, _("cmp: EOF on %s which is empty\n"), shorter_file); else { - char byte_buf[INT_BUFSIZE_BOUND (off_t)]; - char const *byte_num = offtostr (byte_number - 1, byte_buf); - if (comparison_type == type_first_diff) { - char line_buf[INT_BUFSIZE_BOUND (off_t)]; - char const *line_num - = offtostr (line_number - at_line_start, line_buf); fprintf (stderr, (at_line_start - ? _("cmp: EOF on %s after byte %s, line %s\n") - : _("cmp: EOF on %s after byte %s," - " in line %s\n")), - shorter_file, byte_num, line_num); + ? _("cmp: EOF on %s after byte %"PRIuMAX"," + " line %"PRIuMAX"\n") + : _("cmp: EOF on %s after byte %"PRIuMAX"," + " in line %"PRIuMAX"\n")), + shorter_file, (uintmax_t) (byte_number - 1), + (uintmax_t) (line_number - at_line_start)); } + else if (opt_verbose_hex) + fprintf (stderr, + _("cmp: EOF on %s at byte %#"PRIxMAX"\n"), + shorter_file, (uintmax_t) byte_number); else fprintf (stderr, - _("cmp: EOF on %s after byte %s\n"), - shorter_file, byte_num); + _("cmp: EOF on %s after byte %"PRIuMAX"\n"), + shorter_file, (uintmax_t) (byte_number - 1)); } } diff --git a/tests/cmp b/tests/cmp index ff49388..5c96dd2 100755 --- a/tests/cmp +++ b/tests/cmp @@ -143,6 +143,51 @@ cmp -s d c 2 cmp -s d d 2 +cmp -x a a +0 +cmp -x a b +00000000 61 62 +1 +cmp -x a c +cmp: EOF on c which is empty +1 +cmp -x a d +cmp: d: No such file or directory +2 +cmp -x b a +00000000 62 61 +1 +cmp -x b b +0 +cmp -x b c +cmp: EOF on c which is empty +1 +cmp -x b d +cmp: d: No such file or directory +2 +cmp -x c a +cmp: EOF on c which is empty +1 +cmp -x c b +cmp: EOF on c which is empty +1 +cmp -x c c +0 +cmp -x c d +cmp: d: No such file or directory +2 +cmp -x d a +cmp: d: No such file or directory +2 +cmp -x d b +cmp: d: No such file or directory +2 +cmp -x d c +cmp: d: No such file or directory +2 +cmp -x d d +cmp: d: No such file or directory +2 EOF echo a >a @@ -150,7 +195,7 @@ echo b >b : >c rm -f d -for option in '' -l -s; do +for option in '' -l -s -x; do for i in a b c d; do for j in a b c d; do echo cmp $option $i $j @@ -189,6 +234,15 @@ cmp -s a1 a2 1 cmp -s a2 a3 1 +cmp -x a0 a1 +cmp: EOF on a0 which is empty +1 +cmp -x a1 a2 +cmp: EOF on a1 at byte 0x2 +1 +cmp -x a2 a3 +cmp: EOF on a2 at byte 0x5 +1 EOF printf '' >a0 @@ -196,7 +250,7 @@ printf '1\n' >a1 printf '1\nfoo' >a2 printf '1\nfoolery\n' >a3 -for option in '' -l -s; do +for option in '' -l -s -x; do for files in 'a0 a1' 'a1 a2' 'a2 a3'; do echo cmp $option $files cmp $option $files >stdout 2>stderr