From: Pádraig Brady Date: Thu, 30 Jul 2026 15:12:20 +0000 (+0100) Subject: realpath: quote problematic names on tty X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05ab72f236bea585f6698c1099641f3de91762e5;p=thirdparty%2Fcoreutils.git realpath: quote problematic names on tty * doc/coreutils.texi: Mention $QUOTING_STYLE is significant. * src/realpath.c (print_relative_path): New function to buffer relative paths when quoting, and otherwise stream them. (main): Look up $QUOTING_STYLE when outputting to a tty and --zero is not specified. * tests/misc/realpath.sh: Add a test case. * NEWS: Mention the improvement. --- diff --git a/NEWS b/NEWS index 34e026b2bc..7d17ff5e63 100644 --- a/NEWS +++ b/NEWS @@ -84,6 +84,10 @@ GNU coreutils NEWS -*- outline -*- 'install -C' will now avoid updating file metadata when the destination already has the appropriate ownership and permissions. + 'realpath' now quotes output in shell-escape style when + standard output is a terminal. The QUOTING_STYLE environment variable + can be used to adjust or disable the quoting. + 'ls -m' now quotes files names containing commas when appropriate, so users can better distinguish separating commas. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index a51740d81d..c17fda0f9b 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -14596,6 +14596,11 @@ only on the file name, and does not touch any actual file. @end table +When standard output is a terminal, file names are quoted using +the @samp{shell-escape} style. The environment variable +@env{QUOTING_STYLE} can select the quoting style. Valid quoting styles are: +@quotingStyles + @cindex exit status of @command{realpath} Exit status: diff --git a/src/realpath.c b/src/realpath.c index 8ad0ba68a8..e0fc83dc33 100644 --- a/src/realpath.c +++ b/src/realpath.c @@ -21,6 +21,7 @@ #include #include +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" #include "canonicalize.h" #include "relpath.h" @@ -39,6 +40,7 @@ enum static bool verbose = true; static bool logical; static bool use_nuls; +static bool quote_output; static char const *can_relative_to; static char const *can_relative_base; @@ -164,6 +166,34 @@ isdir (char const *path) return S_ISDIR (sb.st_mode); } +static void +print_path (char const *path) +{ + fputs (quote_output ? quoteN (path) : path, stdout); +} + +/* Print CAN_FNAME relative to can_relative_to, quoting the complete + relative file name when appropriate. */ +static bool +print_relative_path (char const *can_fname) +{ + if (!quote_output) + return relpath (can_fname, can_relative_to, NULL, 0); + + size_t size; + if (ckd_mul (&size, strlen (can_relative_to), 2) + || ckd_add (&size, size, strlen (can_fname)) + || ckd_add (&size, size, 1)) + xalloc_die (); + + char *relative_path = xmalloc (size); + bool success = relpath (can_fname, can_relative_to, relative_path, size); + if (success) + print_path (relative_path); + free (relative_path); + return success; +} + static bool process_path (char const *fname, int can_mode) { @@ -177,8 +207,8 @@ process_path (char const *fname, int can_mode) if (!can_relative_to || (can_relative_base && !path_prefix (can_relative_base, can_fname)) - || (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0))) - fputs (can_fname, stdout); + || (can_relative_to && !print_relative_path (can_fname))) + print_path (can_fname); putchar (use_nuls ? '\0' : '\n'); @@ -262,6 +292,18 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (! use_nuls && isatty (STDOUT_FILENO)) + { + int qs = getenv_quoting_style (); + if (qs < 0) + qs = shell_escape_quoting_style; + if (qs != literal_quoting_style) + { + set_quoting_style (NULL, qs); + quote_output = true; + } + } + if (relative_base && !relative_to) relative_to = relative_base; diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh index 501a7baa60..418ff2ffa9 100755 --- a/tests/misc/realpath.sh +++ b/tests/misc/realpath.sh @@ -57,6 +57,22 @@ realpath -e -E --relative-to=dir1/f --relative-base=. . || fail=1 returns_ 1 realpath -m '' || fail=1 returns_ 1 realpath --relative-base= --relative-to=. . || fail=1 +# QUOTING_STYLE does not affect redirected output. +printf 'q name\n' > exp || framework_failure_ +for style in literal shell-always invalid; do + QUOTING_STYLE="$style" \ + realpath -m --relative-to=. 'q name' > out 2> err || fail=1 + compare exp out || fail=1 + compare /dev/null err || fail=1 +done + +# --zero disables quoting, and does not inspect QUOTING_STYLE. +QUOTING_STYLE=invalid \ + realpath -zm --relative-to=. 'q name' > out 2> err || fail=1 +printf 'q name\0' > exp || framework_failure_ +compare exp out || fail=1 +compare /dev/null err || fail=1 + # symlink resolution this=$(realpath .) test "$(realpath ldir2/..)" = "$this/dir1" || fail=1