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