ok,
<< What do you mean by "logical" file name resolution? >>
"logical" is the boolean variable define in the file we talk about :
"lib/canonicalize.c"
line 187 : bool logical = ...;
<< "NOLINKS" flag - what do you mean by that? >>
"CAN_NOLINKS" is a flag that modifs "logical" boolean variable in the
file we talk about : "lib/canonicalize.c"
line 187 : bool logical = (can_mode & CAN_NOLINKS) != 0;
<< "current-directory is a symbolic-link" - what do you mean by that? >>
suppose "/tmp/logical_dir" is a symbolic-link to directory
"/tmp/phisical_dir".
if you change directory to "/tmp/logical_dir", then "current-directory
is a symbolic-link"
example script :
{
mkdir -p /tmp/phisical_dir
ln -sf phisical_dir /tmp/logical_dir
cd -L /tmp/logical_dir
pwd
}
I have attache a new version of my commit-proposal including a section
"How to reproduce".
about unit-test :
- here is the use-case I wanted to add in "tests/test-canonicalize.c"
around line 430 :
/* Check that "." and "$PWD" result the same way */
{
ASSERT (0 == chdir(BASE "/s"));
char *wd = get_current_dir_name();
char *result1 = canonicalize_filename_mode ( ".", 0);
char *result2 = canonicalize_filename_mode ( ".", CAN_NOLINKS);
char *result3 = canonicalize_filename_mode ( wd, 0);
char *result4 = canonicalize_filename_mode ( wd, CAN_NOLINKS);
ASSERT (0 == chdir("../.."));
ASSERT (result1 != NULL);
ASSERT (result2 != NULL);
ASSERT (result3 != NULL);
ASSERT (result4 != NULL);
ASSERT (str_endswith (wd, "/" BASE "/s"));
ASSERT (str_startswith (result1, wd));
ASSERT (str_startswith (result2, wd));
ASSERT (str_startswith (result3, wd));
ASSERT (str_startswith (result4, wd));
ASSERT (str_endswith (result1, "/" BASE "/d"));
ASSERT (0 == strcmp (result1, result2));
ASSERT (0 == strcmp (result1, result3));
ASSERT (str_endswith (result4, "/" BASE "/s"));
free (wd);
free (result1);
free (result2);
free (result3);
free (result4);
}
- but this test does not work due to chdir(BASE "/s") that behave as
chdir(BASE "/d") in the test-context.
- feel free to have a look but I did not found any C langage workaround.
- I did a working unit-test on coreutil realpath executable. I attach the
corresponding commit to this post.
Patrick
On 01/11/2025 12:58, Bruno Haible wrote:
Hi,
Patrick GARCIA wrote:
I did not manage to use "vc-dwim" properly.
vc-dwim is used to create a ChangeLog entry. When you provide a patch
with a reasonable git commit message, we can easily turn that into
a ChangeLog entry. Therefore, no real need to use 'vc-dwim'.
When you propose a patch, you should provide an explanation regarding
the situation where it provides an improvement.
- Which OS? I guess GNU/Linux, but you should better state that
explicitly.
- What are the "How to reproduce" instructions?
Your git commit message gives only partial answers:
- What do you mean by "logical" file name resolution?
- "NOLINKS" flag - what do you mean by that?
- "current-directory is a sybolic-link" - what do you mean by that?
When a process enters a directory via chdir(), is follows symbolic
links. Therefore the current directory is _never_ a symbolic link,
it is _always_ a real directory.
PS : I will, then, propose a shell unit-test to the coreutils team.
Ideally, since Gnulib is a C functions library, the unit test should
be a small self-contained C program. That test program lives in
gnulib/tests/.
Bruno
From 878f29266f73a15d9a7592cb0b0df33fe8bc3754 Mon Sep 17 00:00:00 2001
From: Patrick GARCIA <[email protected]>
Date: Fri, 31 Oct 2025 18:20:34 +0100
Subject: [PATCH] Fix canonicalize_filename_mode_stk() when current-directory
is a symlink
* lib/canonicalize.c: modify canonicalize_filename_mode_stk()
when logical resolution (NOLINKS mode is set) and current-directory is a
symlink. in this case current-directory should be resolved as "${PWD}".
How to reproduce :
- on rockylinux 9.4
- start the bash script :
{
echo "--- prepare ---"
mkdir -p /tmp/phisical_dir
ln -sf phisical_dir /tmp/logical_dir
cd -L /tmp/logical_dir
echo "--- check ---"
ls -ld /tmp/*_dir
[ "/tmp/logical_dir" == "$(realpath -sm "${PWD}")" ] && echo "ok as expected" || echo "bug!"
[ "/tmp/logical_dir" == "$(realpath -sm ".")" ] && echo "ok as expected" || echo "bug!"
realpath -sm "${PWD}" "."
}
- directory "${PWD}" and "." should return the same value "/tmp/logical_dir" while using : realpath -sm
no unit test can compile in C because chdir() refuse tu switch to a
sybolic-link-directory. a unit test is provided in coreutils project, for
realpath executable as this unit test is a shell script.
---
lib/canonicalize.c | 36 ++++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/lib/canonicalize.c b/lib/canonicalize.c
index 10d85ef..c437b1d 100644
--- a/lib/canonicalize.c
+++ b/lib/canonicalize.c
@@ -216,22 +216,30 @@ canonicalize_filename_mode_stk (const char *name, canonicalize_mode_t can_mode,
if (!IS_ABSOLUTE_FILE_NAME (name))
{
- while (!getcwd (bufs->rname.data, bufs->rname.length))
- {
- switch (errno)
+ if (logical) {
+ /* use "naiv" PWD env var */
+ char *cwd = get_current_dir_name();
+ strncpy (bufs->rname.data, cwd, bufs->rname.length);
+ free(cwd);
+ } else {
+ /* resolve phisical working dir */
+ while (!getcwd (bufs->rname.data, bufs->rname.length))
{
- case ERANGE:
- if (scratch_buffer_grow (&bufs->rname))
- break;
- FALLTHROUGH;
- case ENOMEM:
- xalloc_die ();
-
- default:
- dest = rname;
- goto error;
+ switch (errno)
+ {
+ case ERANGE:
+ if (scratch_buffer_grow (&bufs->rname))
+ break;
+ FALLTHROUGH;
+ case ENOMEM:
+ xalloc_die ();
+
+ default:
+ dest = rname;
+ goto error;
+ }
+ rname = bufs->rname.data;
}
- rname = bufs->rname.data;
}
dest = rawmemchr (rname, '\0');
start = name;
--
2.30.2
From dd08cabf47efe13247046738e00822d7da8e3ee3 Mon Sep 17 00:00:00 2001
From: Patrick GARCIA <[email protected]>
Date: Fri, 31 Oct 2025 18:17:23 +0100
Subject: [PATCH] Test current-directory resolution bug in realpath
* tests/misc/realpath.sh: add use case in realpath unit-test to check that
<< realpath -s "." >> result is "$PWD" event when current-directory is a
symlink
---
tests/misc/realpath.sh | 81 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh
index 3ba9bd3..43f8a3f 100755
--- a/tests/misc/realpath.sh
+++ b/tests/misc/realpath.sh
@@ -111,4 +111,85 @@ else
test "$out" = ".$nl." || fail=1
fi
+
+
+
+
+# when call "realpath . $(pwd)", the two results should be equals what ever
+# additional options.
+
+rm -fr "${PWD}/one_link" "${PWD}/one_directory"
+mkdir -pv "${PWD}/one_directory"
+ln -sv "${PWD}/one_directory" "${PWD}/one_link"
+
+actual="
+$(
+for wd in "${PWD}/one_directory" "${PWD}/one_link" ; do
+for opt in "" -s -m -sm ; do
+ echo ""
+ cmd="cd '${wd}' ; realpath ${opt} '${wd}' . a a/b 2>&1"
+ echo "${cmd}"
+ LANG=C bash -c "${cmd}"
+done
+done
+)
+"
+
+expected="
+
+cd '${PWD}/one_directory' ; realpath '${PWD}/one_directory' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+realpath: a/b: No such file or directory
+
+cd '${PWD}/one_directory' ; realpath -s '${PWD}/one_directory' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+${PWD}/one_directory/a/b
+
+cd '${PWD}/one_directory' ; realpath -m '${PWD}/one_directory' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+${PWD}/one_directory/a/b
+
+cd '${PWD}/one_directory' ; realpath -sm '${PWD}/one_directory' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+${PWD}/one_directory/a/b
+
+cd '${PWD}/one_link' ; realpath '${PWD}/one_link' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+realpath: a/b: No such file or directory
+
+cd '${PWD}/one_link' ; realpath -s '${PWD}/one_link' . a a/b 2>&1
+${PWD}/one_link
+${PWD}/one_link
+${PWD}/one_link/a
+${PWD}/one_link/a/b
+
+cd '${PWD}/one_link' ; realpath -m '${PWD}/one_link' . a a/b 2>&1
+${PWD}/one_directory
+${PWD}/one_directory
+${PWD}/one_directory/a
+${PWD}/one_directory/a/b
+
+cd '${PWD}/one_link' ; realpath -sm '${PWD}/one_link' . a a/b 2>&1
+${PWD}/one_link
+${PWD}/one_link
+${PWD}/one_link/a
+${PWD}/one_link/a/b
+"
+
+#echo "${actual}"
+diff <(echo "${actual}") <(echo "${expected}") -C 2 || {
+ fail=1
+ sdiff <(echo "${actual}") <(echo "${expected}") --width=150
+}
+
Exit $fail
--
2.30.2