From: Pádraig Brady Date: Thu, 30 Jul 2026 20:49:12 +0000 (+0100) Subject: readlink: quote problematic names on tty X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc5c99bacf7d6e271dc1a84b82f57ca2fe3e688b;p=thirdparty%2Fcoreutils.git readlink: quote problematic names on tty * doc/coreutils.texi (readlink invocation): Mention $QUOTING_STYLE is significant. * src/readlink.c (main): Quote output when appropriate. * tests/readlink/rl-1.sh: Add a test case. * NEWS: Mention the improvement. --- diff --git a/NEWS b/NEWS index 0667f4fddc..2db6c99ae0 100644 --- a/NEWS +++ b/NEWS @@ -84,8 +84,8 @@ GNU coreutils NEWS -*- outline -*- 'install -C' will now avoid updating file metadata when the destination already has the appropriate ownership and permissions. - 'basename' and 'realpath' now quote output in shell-escape style when - standard output is a terminal. The QUOTING_STYLE environment variable + 'basename', 'readlink', and 'realpath' now quote 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, diff --git a/doc/coreutils.texi b/doc/coreutils.texi index d9b6a8761f..b675b6345a 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -11100,6 +11100,11 @@ variable is set. @end table +When standard output is a terminal, output is quoted using the +@samp{shell-escape} style. The environment variable +@env{QUOTING_STYLE} can select the quoting style. Valid quoting styles are: +@quotingStyles + The @command{readlink} utility first appeared in OpenBSD 2.1. The @command{realpath} command without options, operates like diff --git a/src/readlink.c b/src/readlink.c index 3a2d6aaed1..f25aced2c1 100644 --- a/src/readlink.c +++ b/src/readlink.c @@ -21,6 +21,7 @@ #include #include +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" #include "canonicalize.h" #include "areadlink.h" @@ -36,6 +37,9 @@ static bool no_newline; /* If true, report error messages. */ static bool verbose; +/* If true, quote output according to the selected quoting style. */ +static bool quote_output; + static struct option const longopts[] = { {"canonicalize", no_argument, NULL, 'f'}, @@ -170,6 +174,18 @@ main (int argc, char **argv) no_newline = false; } + 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; + } + } + /* POSIX requires a diagnostic message written to standard error and a non-zero exit status when given a file that is not a symbolic link. */ if (getenv ("POSIXLY_CORRECT") != NULL) @@ -183,7 +199,7 @@ main (int argc, char **argv) : areadlink_with_size (fname, 63)); if (value) { - fputs (value, stdout); + fputs (quote_output ? quoteN (value) : value, stdout); if (! no_newline) putchar (use_nuls ? '\0' : '\n'); free (value); diff --git a/tests/readlink/rl-1.sh b/tests/readlink/rl-1.sh index b3f6a20495..4beffad967 100755 --- a/tests/readlink/rl-1.sh +++ b/tests/readlink/rl-1.sh @@ -23,6 +23,7 @@ mkdir subdir || framework_failure_ touch regfile || framework_failure_ ln -s regfile link1 || framework_failure_ ln -s missing link2 || framework_failure_ +ln -s 'q name' qlink || framework_failure_ v=$(readlink link1) || fail=1 @@ -31,6 +32,20 @@ test "$v" = regfile || fail=1 v=$(readlink link2) || fail=1 test "$v" = missing || 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" readlink qlink > 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 readlink -z qlink > out 2> err || fail=1 +printf 'q name\0' > exp || framework_failure_ +compare exp out || fail=1 +compare /dev/null err || fail=1 + v=$(returns_ 1 readlink subdir) || fail=1 test -z "$v" || fail=1