cmp: improve EOF diagnostic

This improves on yesterday's change, following up on a
remark by Jim Meyering (Bug#22816#21).
* doc/diffutils.texi (Invoking cmp, cmp Options): Follow POSIX more
closely in the documentation of the information appended to the EOF
diagnostic.
* src/cmp.c (cmp): Be more specific about the shorter file's length
and fix some off-by-1 issues in reporting line counts.
* tests/cmp: Adjust to match new behavior.
Don't assume internal details about stdio buffering.
This commit is contained in:
Paul Eggert 2017-05-04 17:17:23 -07:00
parent 98b6fc2f32
commit ac05c6d549
3 changed files with 100 additions and 38 deletions

View File

@ -3511,15 +3511,16 @@ reports the location of the first difference to standard output:
@noindent
If one file is a prefix of the other, @command{cmp} reports the
shorter file's length to standard error (@acronym{POSIX} allows but
does not require the shorter file's name to be followed by a blank and
additional information):
shorter file's name to standard error, followed by a blank and extra
information about the shorter file:
@example
cmp: EOF on @var{shorter-file} after byte @var{byte-number}, line @var{line-number}
cmp: EOF on @var{shorter-file} @var{extra-info}
@end example
The message formats can differ outside the @acronym{POSIX} locale.
@acronym{POSIX} allows but does not require the EOF diagnostic's file
name to be followed by a blank and additional information.
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.
@ -3566,8 +3567,7 @@ instead of the default standard output.
Each output line contains a differing byte's number relative to the
start of the input, followed by the differing byte values.
Byte numbers start at 1.
Also, if one file is shorter than the other, output the @acronym{EOF}
message with just a byte number.
Also, output the @acronym{EOF} message if one file is shorter than the other.
@item -n @var{count}
@itemx --bytes=@var{count}

View File

@ -378,6 +378,7 @@ main (int argc, char **argv)
static int
cmp (void)
{
bool at_line_start = true;
off_t line_number = 1; /* Line number (1...) of difference. */
off_t byte_number = 1; /* Byte number (1...) of difference. */
uintmax_t remaining = bytes; /* Remaining number of bytes to compare. */
@ -463,8 +464,11 @@ cmp (void)
}
byte_number += first_diff;
if (comparison_type == type_first_diff)
line_number += count_newlines (buf0, first_diff);
if (comparison_type == type_first_diff && first_diff != 0)
{
line_number += count_newlines (buf0, first_diff);
at_line_start = buf0[first_diff - 1] == '\n';
}
if (first_diff < smaller)
{
@ -478,9 +482,9 @@ cmp (void)
char const *line_num = offtostr (line_number, line_buf);
if (!opt_print_bytes)
{
/* See POSIX 1003.1-2001 for this format. This
message is used only in the POSIX locale, so it
need not be translated. */
/* See POSIX for this format. This message is
used only in the POSIX locale, so it need not
be translated. */
static char const char_message[] =
"%s %s differ: char %s, line %s\n";
@ -510,7 +514,7 @@ cmp (void)
printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
file[0], file[1], byte_num, line_num,
c0, s0, c1, s1);
}
}
}
/* Fall through. */
case type_status:
@ -527,7 +531,7 @@ cmp (void)
char const *byte_num = offtostr (byte_number, byte_buf);
if (!opt_print_bytes)
{
/* See POSIX 1003.1-2001 for this format. */
/* See POSIX for this format. */
printf ("%*s %3o %3o\n",
offset_width, byte_num, c0, c1);
}
@ -559,23 +563,35 @@ cmp (void)
if (differing <= 0 && comparison_type != type_status)
{
char const *shorter_file = file[read1 < read0];
char byte_buf[INT_BUFSIZE_BOUND (off_t)];
char const *byte_num = offtostr (byte_number - 1, byte_buf);
/* See POSIX 1003.1-2001 for the constraints on these
format strings. */
if (comparison_type == type_first_diff)
{
char line_buf[INT_BUFSIZE_BOUND (off_t)];
char const *line_num = offtostr (line_number - 1, line_buf);
fprintf (stderr,
_("cmp: EOF on %s after byte %s, line %s\n"),
shorter_file, byte_num, line_num);
}
/* 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)
fprintf (stderr, _("cmp: EOF on %s which is empty\n"),
shorter_file);
else
fprintf (stderr,
_("cmp: EOF on %s after byte %s\n"),
shorter_file, byte_num);
{
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);
}
else
fprintf (stderr,
_("cmp: EOF on %s after byte %s\n"),
shorter_file, byte_num);
}
}
return EXIT_FAILURE;

View File

@ -27,7 +27,7 @@ cmp a b
a b differ: char 1, line 1
1
cmp a c
cmp: EOF on c after byte 0, line 0
cmp: EOF on c which is empty
1
cmp a d
cmp: d: No such file or directory
@ -38,16 +38,16 @@ b a differ: char 1, line 1
cmp b b
0
cmp b c
cmp: EOF on c after byte 0, line 0
cmp: EOF on c which is empty
1
cmp b d
cmp: d: No such file or directory
2
cmp c a
cmp: EOF on c after byte 0, line 0
cmp: EOF on c which is empty
1
cmp c b
cmp: EOF on c after byte 0, line 0
cmp: EOF on c which is empty
1
cmp c c
0
@ -72,7 +72,7 @@ cmp -l a b
1 141 142
1
cmp -l a c
cmp: EOF on c after byte 0
cmp: EOF on c which is empty
1
cmp -l a d
cmp: d: No such file or directory
@ -83,16 +83,16 @@ cmp -l b a
cmp -l b b
0
cmp -l b c
cmp: EOF on c after byte 0
cmp: EOF on c which is empty
1
cmp -l b d
cmp: d: No such file or directory
2
cmp -l c a
cmp: EOF on c after byte 0
cmp: EOF on c which is empty
1
cmp -l c b
cmp: EOF on c after byte 0
cmp: EOF on c which is empty
1
cmp -l c c
0
@ -154,12 +154,58 @@ for option in '' -l -s; do
for i in a b c d; do
for j in a b c d; do
echo cmp $option $i $j
cmp $option $i $j 2>&1
echo $?
cmp $option $i $j >stdout 2>stderr
status=$?
cat stderr stdout
echo $status
done
done
done >out
compare exp out || fail=1
cat <<'EOF' > exp1 || fail=1
cmp a0 a1
cmp: EOF on a0 which is empty
1
cmp a1 a2
cmp: EOF on a1 after byte 2, line 1
1
cmp a2 a3
cmp: EOF on a2 after byte 5, in line 2
1
cmp -l a0 a1
cmp: EOF on a0 which is empty
1
cmp -l a1 a2
cmp: EOF on a1 after byte 2
1
cmp -l a2 a3
cmp: EOF on a2 after byte 5
1
cmp -s a0 a1
1
cmp -s a1 a2
1
cmp -s a2 a3
1
EOF
printf '' >a0
printf '1\n' >a1
printf '1\nfoo' >a2
printf '1\nfoolery\n' >a3
for option in '' -l -s; do
for files in 'a0 a1' 'a1 a2' 'a2 a3'; do
echo cmp $option $files
cmp $option $files >stdout 2>stderr
status=$?
cat stderr stdout
echo $status
done
done >out1
compare exp1 out1 || fail=1
Exit $fail