diff: output symlink contents when they differ

* bootstrap.conf (gnulib_modules): Add quote.
* src/diff.c: Include quote.h.
(compare_files): Print contents of symlinks that differ,
and quote their names and contents.
* src/system.h (symlink_size_ok): Remove.
(stat_size): Don’t worry about symlink sizes.
* tests/no-dereference: Adjust tests to match new behavior.
This commit is contained in:
Paul Eggert 2023-08-07 15:22:13 -07:00
parent 6f357cfa9f
commit b684567824
5 changed files with 46 additions and 44 deletions

5
NEWS
View File

@ -4,6 +4,11 @@ GNU diffutils NEWS -*- outline -*-
** Improvements
diff now outputs more information when symbolic links differ, and
quotes their names to avoid ambiguity. For example, instead of
"Symbolic links d/x do and e/x do differ", diff now outputs
"Symbolic links 'd/x do' -> 'a' and 'e/x do' -> 'b' differ".
diff's --ignore-case (-i) and --ignore-file-name-case options now
support multi-byte characters. For example, they treat Greek
capital Δ like small δ when input uses UTF-8.

View File

@ -86,6 +86,7 @@ perl
popen
progname
propername-lite
quote
raise
rawmemchr
readdir

View File

@ -34,6 +34,7 @@
#include <getopt.h>
#include <hard-locale.h>
#include <progname.h>
#include <quote.h>
#include <sh-quote.h>
#include <stat-time.h>
#include <version-etc.h>
@ -1493,45 +1494,39 @@ compare_files (struct comparison const *parent,
dassert (no_dereference_symlinks);
/* Compare the values of the symbolic links. */
if (cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
&& 0 <= cmp.file[0].stat.st_size
&& 0 <= cmp.file[1].stat.st_size)
status = EXIT_FAILURE;
else
char *link_value[2]; link_value[1] = nullptr;
char linkbuf[2][128];
for (bool f = false; ; f = true)
{
char *link_value[2]; link_value[1] = nullptr;
char linkbuf[2][128];
for (bool f = false; ; f = true)
int dirfd = parent->file[f].desc;
char const *name = cmp.file[f].name;
char const *nm = dirfd < 0 ? name : last_component (name);
link_value[f] = careadlinkat (dirfd, nm,
linkbuf[f], sizeof linkbuf[f],
nullptr, readlinkat);
if (!link_value[f])
{
int dirfd = parent->file[f].desc;
char const *name = cmp.file[f].name;
char const *nm = dirfd < 0 ? name : last_component (name);
link_value[f] = careadlinkat (dirfd, nm,
linkbuf[f], sizeof linkbuf[f],
nullptr, readlinkat);
if (!link_value[f])
{
perror_with_name (cmp.file[f].name);
status = EXIT_TROUBLE;
break;
}
if (f)
{
status = (STREQ (link_value[0], link_value[f])
? EXIT_SUCCESS : EXIT_FAILURE);
break;
}
perror_with_name (cmp.file[f].name);
status = EXIT_TROUBLE;
break;
}
if (f)
{
status = (STREQ (link_value[0], link_value[f])
? EXIT_SUCCESS : EXIT_FAILURE);
break;
}
for (int f = 0; f < 2; f++)
if (link_value[f] != linkbuf[f])
free (link_value[f]);
}
if (status == EXIT_FAILURE)
message ("Symbolic links %s and %s differ\n",
cmp.file[0].name, cmp.file[1].name);
message ("Symbolic links %s -> %s and %s -> %s differ\n",
quote_n (0, cmp.file[0].name), quote_n (1, link_value[0]),
quote_n (2, cmp.file[1].name), quote_n (3, link_value[1]));
for (int f = 0; f < 2; f++)
if (link_value[f] != linkbuf[f])
free (link_value[f]);
}
else if (files_can_be_treated_as_binary
&& cmp.file[0].detype == DE_REG

View File

@ -114,7 +114,7 @@
SYSTEM_EXTERN dev_t proc_dev;
#endif
#if defined __linux__ || defined __ANDROID__
#if false && (defined __linux__ || defined __ANDROID__)
# include <sys/utsname.h>
/* 1 if symlink st_size is OK, -1 if not, 0 if unknown yet. */
SYSTEM_EXTERN signed char symlink_size_ok;
@ -197,9 +197,10 @@ SYSTEM_INLINE off_t stat_size (struct stat *s)
return -1;
}
#endif
#if defined __linux__ || defined __ANDROID__
#if false && (defined __linux__ || defined __ANDROID__)
/* Symlinks have suspect sizes on Linux kernels before 5.15,
due to bugs in fscrypt. */
due to bugs in fscrypt. However, diffutils never looks
at symlink sizes so this code is not needed. */
if (S_ISLNK (mode))
{
if (! symlink_size_ok)

View File

@ -43,17 +43,17 @@ compare /dev/null out || fail=1
# Test case 7: Compare symbolic links with different value and different target
# contents.
returns_ 1 diff --no-dereference symlink1 symlink2 > out || fail=1
LC_ALL=C returns_ 1 diff --no-dereference symlink1 symlink2 > out || fail=1
cat <<EOF > expected || framework_failure_
Symbolic links symlink1 and symlink2 differ
Symbolic links 'symlink1' -> 'regular1' and 'symlink2' -> 'regular2' differ
EOF
compare expected out || fail=1
# Test case 8: Compare symbolic links with different value and same target
# contents.
returns_ 1 diff --no-dereference symlink2 symlink3 > out || fail=1
LC_ALL=C returns_ 1 diff --no-dereference symlink2 symlink3 > out || fail=1
cat <<EOF > expected || framework_failure_
Symbolic links symlink2 and symlink3 differ
Symbolic links 'symlink2' -> 'regular2' and 'symlink3' -> 'regular3' differ
EOF
compare expected out || fail=1
@ -134,9 +134,9 @@ mkdir subdir7a
mkdir subdir7b
ln -s ../regular1 subdir7a/foo
ln -s ../regular2 subdir7b/foo
returns_ 1 diff -r --no-dereference subdir7a subdir7b > out || fail=1
LC_ALL=C returns_ 1 diff -r --no-dereference subdir7a subdir7b > out || fail=1
cat <<EOF > expected || framework_failure_
Symbolic links subdir7a/foo and subdir7b/foo differ
Symbolic links 'subdir7a/foo' -> '../regular1' and 'subdir7b/foo' -> '../regular2' differ
EOF
compare expected out || fail=1
@ -146,9 +146,9 @@ mkdir subdir8a
mkdir subdir8b
ln -s ../regular2 subdir8a/foo
ln -s ../regular3 subdir8b/foo
returns_ 1 diff -r --no-dereference subdir8a subdir8b > out || fail=1
LC_ALL=C returns_ 1 diff -r --no-dereference subdir8a subdir8b > out || fail=1
cat <<EOF > expected || framework_failure_
Symbolic links subdir8a/foo and subdir8b/foo differ
Symbolic links 'subdir8a/foo' -> '../regular2' and 'subdir8b/foo' -> '../regular3' differ
EOF
compare expected out || fail=1