]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
realpath: quote problematic names on tty
authorPádraig Brady <P@draigBrady.com>
Thu, 30 Jul 2026 15:12:20 +0000 (16:12 +0100)
committerPádraig Brady <P@draigBrady.com>
Sat, 1 Aug 2026 11:38:28 +0000 (12:38 +0100)
* 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.

NEWS
doc/coreutils.texi
src/realpath.c
tests/misc/realpath.sh

diff --git a/NEWS b/NEWS
index 34e026b2bccd777928d4f9d5e1d0ab88eaaa0958..7d17ff5e637a84785995f8201456fe3dad57075f 100644 (file)
--- 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.
 
index a51740d81d0acd29ca76b408c16140f680e040dd..c17fda0f9b567519f98cf7bc83cc61f5a609b057 100644 (file)
@@ -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:
 
index 8ad0ba68a8de53435ff122e759c01ad7a4608b54..e0fc83dc3310c6d36c58b76ad9243a98cef571ec 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#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;
 
index 501a7baa605d310701a2f6b6c702ffdebfb0a244..418ff2ffa963d52bf6562b3968b6d8a35172c695 100755 (executable)
@@ -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