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