Collin Funk <collin.fu...@gmail.com> writes: > Pádraig Brady <p...@draigbrady.com> writes: > >>> Done, v2 attached. >> >> Looks good. > > Pushed, thanks to you and Dmitry for the review. > > Leaving this bug open for realpath. I'll have a look at that as well, > just requires some more reading than readlink did. :)
It looks like 'realpath -E' is the same as Coreutils 'readlink -f', and default behavior of Coreutils 'realpath'. POSIX us to keep our default behavior [1]: If no options are specified, realpath shall canonicalize the specified pathname in an unspecified manner such that the resulting absolute pathname does not contain any components that refer to files of type symbolic link and does not contain any components that are dot or dot-dot. Here is a proposed patch. I don't love having 'realpath -E' and 'readlink -f' with the same long option name, but having the same functionality with a different long option name seems worse. Collin [1] https://pubs.opengroup.org/onlinepubs/9799919799/utilities/realpath.html
>From 9e8803c4552ef03aa25c48df90efba59670782c8 Mon Sep 17 00:00:00 2001 Message-ID: <9e8803c4552ef03aa25c48df90efba59670782c8.1754787534.git.collin.fu...@gmail.com> From: Collin Funk <collin.fu...@gmail.com> Date: Sat, 9 Aug 2025 17:53:29 -0700 Subject: [PATCH] realpath: support the -E option required by POSIX * src/realpath.c (longopts): Add the option. (main): Likewise. (usage): Add the option to the --help message. * tests/misc/realpath.sh: Add a simple test. * doc/coreutils.texi (realpath invocation): Mention the new option. * NEWS: Likewise. --- NEWS | 4 ++++ doc/coreutils.texi | 14 ++++++++++++-- src/realpath.c | 16 ++++++++++++---- tests/misc/realpath.sh | 3 +++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index f80d0150e..e4575d0db 100644 --- a/NEWS +++ b/NEWS @@ -80,6 +80,10 @@ GNU coreutils NEWS -*- outline -*- basenc supports the --base58 option to encode and decode the visually unambiguous Base58 encoding. + realpath supports the -E option as required by POSIX. The behavior is + the same as realpath with no options. The corresponding long option + is --canonicalize. + ** Improvements 'factor' is now much faster at identifying large prime numbers, diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 19de577e5..766c27033 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -15002,8 +15002,7 @@ @node realpath invocation @findex realpath @command{realpath} expands all symbolic links and resolves references to -@samp{/./}, @samp{/../} and extra @samp{/} characters. By default, -all but the last component of the specified files must exist. Synopsis: +@samp{/./}, @samp{/../} and extra @samp{/} characters. Synopsis: @example realpath [@var{option}]@dots{} @var{file}@dots{} @@ -15018,6 +15017,17 @@ @node realpath invocation @table @samp +@item -E +@itemx --canonicalize +@opindex -E +@opindex --canonicalize + +Ensure all but the last component of the specified file name exist. +Otherwise, @command{realpath} will output a diagnostic unless the +@option{-q} option is specified, and exit with a nonzero exit code. A +trailing slash is ignored. This option is the default behavior, but is +included for POSIX compatibility. + @item -e @itemx --canonicalize-existing @opindex -e diff --git a/src/realpath.c b/src/realpath.c index 4bc00edc2..b06e5e845 100644 --- a/src/realpath.c +++ b/src/realpath.c @@ -44,6 +44,7 @@ static char const *can_relative_base; static struct option const longopts[] = { + {"canonicalize", no_argument, nullptr, 'E'}, {"canonicalize-existing", no_argument, nullptr, 'e'}, {"canonicalize-missing", no_argument, nullptr, 'm'}, {"relative-to", required_argument, nullptr, RELATIVE_TO_OPTION}, @@ -68,11 +69,14 @@ usage (int status) { printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); fputs (_("\ -Print the resolved absolute file name;\n\ -all but the last component must exist\n\ -\n\ +Print the resolved absolute file name.\n\ "), stdout); fputs (_("\ + -E, --canonicalize canonicalize by following every symlink in\n\ + every component of the given name recursively;\ +\n\ + all but the last component must exist\n\ + (this is the default)\n\ -e, --canonicalize-existing all components of the path must exist\n\ -m, --canonicalize-missing no path components need exist or be a directory\ \n\ @@ -186,11 +190,15 @@ main (int argc, char **argv) while (true) { - int c = getopt_long (argc, argv, "eLmPqsz", longopts, nullptr); + int c = getopt_long (argc, argv, "EeLmPqsz", longopts, nullptr); if (c == -1) break; switch (c) { + case 'E': + can_mode &= ~CAN_MODE_MASK; + can_mode |= CAN_ALL_BUT_LAST; + break; case 'e': can_mode &= ~CAN_MODE_MASK; can_mode |= CAN_EXISTING; diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh index 2356cac58..3ba9bd36c 100755 --- a/tests/misc/realpath.sh +++ b/tests/misc/realpath.sh @@ -50,6 +50,9 @@ returns_ 1 realpath --relative-base . || fail=1 returns_ 1 realpath -e --relative-to=dir1/f --relative-base=. . || fail=1 realpath -e --relative-to=dir1/ --relative-base=. . || fail=1 +# Check that using -E after -e uses -E as specified by POSIX. +realpath -e -E --relative-to=dir1/f --relative-base=. . || fail=1 + # Note NUL params are unconditionally rejected by canonicalize_filename_mode returns_ 1 realpath -m '' || fail=1 returns_ 1 realpath --relative-base= --relative-to=. . || fail=1 -- 2.50.1