On 26/05/2026 12:19, Pádraig Brady wrote:
On 26/05/2026 07:54, Michał Majchrowicz wrote:
We are reporting output-encoding vulnerabilities we identified in GNU coreutils readlink, realpath, and basename while reviewing how pathnames are rendered.
As mentioned previously, this quoting would not be appropriate for non interactive use as it would break scripts. All these commands have a --zero option to avoid the multi-line issue by using NUL terminated records. For interactive use, there is a stronger argument to quote, especially since our quoting format is now POSIX standardized. We'd have to support disabling this behavior though, which could be done I suppose with the use of the QUOTING_STYLE env var (like for ls). I'd be reluctant to add --quoting-style options to these commands. BTW we've some general notes on reasons to quote at: https://www.gnu.org/software/coreutils/quotes.html
While basename, readlink, and realpath aren't often used interactively, I have done so occasionally, especially with realpath. So it's worth these avoiding outputting arbitrary data to terminals. The attached patches implements quoting for these 3 commands when outputting to tty, but being careful to _not_ quote if not outputting to tty. tty quoting style can be configured/disabled through the $QUOTING_STYLE env variable. The default style is shell-escape (like with ls), which avoid quoting unless needed. Also attached is a similar quoting adjustment for du, which is warranted as that's usually used interactively. cheers, Padraig
From af41f84b05e0da644f7e426e255029845ca5b850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Fri, 31 Jul 2026 14:08:15 +0100 Subject: [PATCH 1/6] maint: stat: standardize QUOTING_STYLE handling * src/stat.c (getenv_quoting_style): Return the selected style. (initialize_quoting_style): New function, retaining lazy initialization. (print_statfs, print_stat): Use it. --- src/stat.c | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/stat.c b/src/stat.c index 77ea54cde..60397d368 100644 --- a/src/stat.c +++ b/src/stat.c @@ -855,32 +855,37 @@ out_file_context (char *pformat, size_t prefix_len, char const *filename) return fail; } -/* Set the quoting style default if the environment variable - QUOTING_STYLE is set. */ +/* Return the quoting style specified by the environment variable + QUOTING_STYLE if set and valid, -1 otherwise. */ -static void +static int getenv_quoting_style (void) { - static bool got_quoting_style; - if (got_quoting_style) - return; - got_quoting_style = true; - char const *q_style = getenv ("QUOTING_STYLE"); - if (q_style) + if (!q_style) + return -1; + + int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals); + if (i < 0) { - int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals); - if (0 <= i) - set_quoting_style (NULL, quoting_style_vals[i]); - else - { - set_quoting_style (NULL, shell_escape_quoting_style); - error (0, 0, _("ignoring invalid value of environment " - "variable QUOTING_STYLE: %s"), quote (q_style)); - } + error (0, 0, _("ignoring invalid value of environment " + "variable QUOTING_STYLE: %s"), quote (q_style)); + return -1; + } + return quoting_style_vals[i]; +} + +/* Set the quoting style once it is needed. */ +static void +initialize_quoting_style (void) +{ + static bool initialized; + if (!initialized) + { + initialized = true; + int qs = getenv_quoting_style (); + set_quoting_style (NULL, 0 <= qs ? qs : shell_escape_quoting_style); } - else - set_quoting_style (NULL, shell_escape_quoting_style); } /* Equivalent to quotearg(), but explicit to avoid syntax checks. */ @@ -901,7 +906,7 @@ print_statfs (char *pformat, size_t prefix_len, MAYBE_UNUSED char mod, char m, case 'n': if (mod == 'Q') { - getenv_quoting_style (); + initialize_quoting_style (); filename = quoteN (filename); } out_string (pformat, prefix_len, filename); @@ -1527,13 +1532,13 @@ print_stat (char *pformat, size_t prefix_len, char mod, char m, case 'n': if (mod == 'Q') { - getenv_quoting_style (); + initialize_quoting_style (); filename = quoteN (filename); } out_string (pformat, prefix_len, filename); break; case 'N': - getenv_quoting_style (); + initialize_quoting_style (); out_string (pformat, prefix_len, quoteN (filename)); if (S_ISLNK (statbuf->st_mode)) { -- 2.55.0
From 3babcb5d562af0ad5029fab45543a54eaae1359f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Fri, 31 Jul 2026 14:12:37 +0100 Subject: [PATCH 2/6] maint: ls,stat: factor out getenv_quoting_style * src/system.h (getenv_quoting_style): Move here from ls.c and stat.c. * src/ls.c (getenv_quoting_style): Remove. * src/stat.c (getenv_quoting_style): Remove. Ensure ARGMATCH is defined for system.h. --- src/ls.c | 23 ----------------------- src/stat.c | 25 +------------------------ src/system.h | 24 +++++++++++++++++++++++- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/src/ls.c b/src/ls.c index ba9ec6dab..c70b27866 100644 --- a/src/ls.c +++ b/src/ls.c @@ -306,8 +306,6 @@ static void queue_directory (char const *name, char const *realname, static void sort_files (void); static void parse_ls_color (void); -static int getenv_quoting_style (void); - static size_t quote_name_width (char const *name, struct quoting_options const *options, int needs_general_quoting); @@ -2896,27 +2894,6 @@ parse_ls_color (void) color_symlink_as_referent = true; } -/* Return the quoting style specified by the environment variable - QUOTING_STYLE if set and valid, -1 otherwise. */ - -static int -getenv_quoting_style (void) -{ - char const *q_style = getenv ("QUOTING_STYLE"); - if (!q_style) - return -1; - int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals); - if (i < 0) - { - error (0, 0, - _("ignoring invalid value" - " of environment variable QUOTING_STYLE: %s"), - quote (q_style)); - return -1; - } - return quoting_style_vals[i]; -} - /* Set the exit status to report a failure. If SERIOUS, it is a serious failure; otherwise, it is merely a minor problem. */ diff --git a/src/stat.c b/src/stat.c index 60397d368..ff149a76a 100644 --- a/src/stat.c +++ b/src/stat.c @@ -55,10 +55,10 @@ #include <selinux/selinux.h> #include <getopt.h> +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" #include "areadlink.h" -#include "argmatch.h" #include "c-ctype.h" #include "file-type.h" #include "filemode.h" @@ -855,26 +855,6 @@ out_file_context (char *pformat, size_t prefix_len, char const *filename) return fail; } -/* Return the quoting style specified by the environment variable - QUOTING_STYLE if set and valid, -1 otherwise. */ - -static int -getenv_quoting_style (void) -{ - char const *q_style = getenv ("QUOTING_STYLE"); - if (!q_style) - return -1; - - int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals); - if (i < 0) - { - error (0, 0, _("ignoring invalid value of environment " - "variable QUOTING_STYLE: %s"), quote (q_style)); - return -1; - } - return quoting_style_vals[i]; -} - /* Set the quoting style once it is needed. */ static void initialize_quoting_style (void) @@ -888,9 +868,6 @@ initialize_quoting_style (void) } } -/* Equivalent to quotearg(), but explicit to avoid syntax checks. */ -#define quoteN(x) quotearg_style (get_quoting_style (NULL), x) - /* Print statfs info. Return zero upon success, nonzero upon failure. */ NODISCARD static bool diff --git a/src/system.h b/src/system.h index e96127d41..7efc78972 100644 --- a/src/system.h +++ b/src/system.h @@ -1002,8 +1002,30 @@ is_ENOTSUP (int err) #define quoteaf_n(n, arg) \ quotearg_n_style (n, shell_escape_always_quoting_style, arg) +/* Equivalent to quotearg(), but explicit to avoid syntax checks. */ +#define quoteN(x) quotearg_style (get_quoting_style (NULL), x) + +#ifdef ARGMATCH +/* Return the quoting style specified by the environment variable + QUOTING_STYLE if set and valid, -1 otherwise. */ +static inline int +getenv_quoting_style (void) +{ + char const *q_style = getenv ("QUOTING_STYLE"); + if (!q_style) + return -1; + + int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals); + if (i < 0) + { + error (0, 0, _("ignoring invalid value of environment " + "variable QUOTING_STYLE: %s"), quote (q_style)); + return -1; + } + return quoting_style_vals[i]; +} + /* Used instead of XARGMATCH() to provide a custom error message. */ -#ifdef XARGMATCH static inline ptrdiff_t x_timestyle_match (char const * style, bool allow_posix, char const *const * timestyle_args, -- 2.55.0
From bb05bdef6ec8ddf191e751951cb46435034be7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Thu, 30 Jul 2026 16:12:20 +0100 Subject: [PATCH 3/6] 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. --- NEWS | 4 ++++ doc/coreutils.texi | 5 +++++ src/realpath.c | 46 ++++++++++++++++++++++++++++++++++++++++-- tests/misc/realpath.sh | 16 +++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 34e026b2b..7d17ff5e6 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 a51740d81..c17fda0f9 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 8ad0ba68a..e0fc83dc3 100644 --- a/src/realpath.c +++ b/src/realpath.c @@ -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; diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh index 501a7baa6..418ff2ffa 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 -- 2.55.0
From 570912cbf63f8745ecea6907735ee78ec3d44e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Thu, 30 Jul 2026 21:48:22 +0100 Subject: [PATCH 4/6] basename: quote problematic names on tty * doc/coreutils.texi (basename invocation): Mention $QUOTING_STYLE is significant. * src/basename.c (perform_basename, main): Quote output when appropriate. * tests/misc/basename.pl: Add a test case. * NEWS: Mention the improvement. --- NEWS | 2 +- doc/coreutils.texi | 5 +++++ src/basename.c | 17 ++++++++++++++++- tests/misc/basename.pl | 10 ++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 7d17ff5e6..0667f4fdd 100644 --- a/NEWS +++ b/NEWS @@ -84,7 +84,7 @@ 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 + 'basename' 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. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index c17fda0f9..d9b6a8761 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -14193,6 +14193,11 @@ This option implies the @option{-a} option. @end table +When standard output is a terminal, output is quoted using the +@samp{shell-escape} style. The environment variable +@env{QUOTING_STYLE} specifies the quoting style. Valid quoting styles are: +@quotingStyles + @exitstatus Examples: diff --git a/src/basename.c b/src/basename.c index 96bbb71c5..4fc086435 100644 --- a/src/basename.c +++ b/src/basename.c @@ -19,6 +19,7 @@ #include <stdio.h> #include <sys/types.h> +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" #include "quote.h" @@ -27,6 +28,8 @@ #define AUTHORS proper_name ("David MacKenzie") +static bool quote_output; + static struct option const longopts[] = { {"multiple", no_argument, NULL, 'a'}, @@ -121,7 +124,7 @@ perform_basename (char const *string, char const *suffix, idx_t suffix_len, && ! FILE_SYSTEM_PREFIX_LEN (name)) remove_suffix (name, suffix, suffix_len); - fputs (name, stdout); + fputs (quote_output ? quoteN (name) : name, stdout); putchar (use_nuls ? '\0' : '\n'); free (name); } @@ -188,6 +191,18 @@ main (int argc, char **argv) } } + 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; + } + } + idx_t suffix_len = suffix ? strlen (suffix) : 0; char **file = argv + optind; diff --git a/tests/misc/basename.pl b/tests/misc/basename.pl index 8188817b8..98607b787 100755 --- a/tests/misc/basename.pl +++ b/tests/misc/basename.pl @@ -62,12 +62,22 @@ my @Tests = ['9', qw(fs ''), {OUT => 'fs'}], ['10', qw(fs/ s/), {OUT => 'fs'}], + # QUOTING_STYLE does not affect redirected output. + ['q-lit', q{'d/q name'}, {ENV => 'QUOTING_STYLE=literal'}, + {OUT => 'q name'}], + ['q-shell', q{'d/q name'}, {ENV => 'QUOTING_STYLE=shell-always'}, + {OUT => 'q name'}], + ['q-invalid', q{'d/q name'}, {ENV => 'QUOTING_STYLE=invalid'}, + {OUT => 'q name'}], + # Exercise -z option. ['z0', qw(-z a), {OUT => "a\0"}], ['z1', qw(--zero a), {OUT => "a\0"}], ['z2', qw(-za a b), {OUT => "a\0b\0"}], ['z3', qw(-z ba a), {OUT => "b\0"}], ['z4', qw(-z -s a ba), {OUT => "b\0"}], + ['z-quote', q{-z 'q name'}, {ENV => 'QUOTING_STYLE=invalid'}, + {OUT => "q name\0"}], ); # Append a newline to end of each expected 'OUT' string. -- 2.55.0
From 0593504fb9a944ed4c1aeed0df87a7c3d5f6ae0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Thu, 30 Jul 2026 21:49:12 +0100 Subject: [PATCH 5/6] 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. --- NEWS | 4 ++-- doc/coreutils.texi | 5 +++++ src/readlink.c | 18 +++++++++++++++++- tests/readlink/rl-1.sh | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 0667f4fdd..2db6c99ae 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 d9b6a8761..b675b6345 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 3a2d6aaed..f25aced2c 100644 --- a/src/readlink.c +++ b/src/readlink.c @@ -21,6 +21,7 @@ #include <getopt.h> #include <sys/types.h> +#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 b3f6a2049..4beffad96 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 -- 2.55.0
From 8b3a225b9f688bf295af14f9516e146cbcb75eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Fri, 31 Jul 2026 17:35:52 +0100 Subject: [PATCH 6/6] du: quote problematic names on tty * doc/coreutils.texi (readlink invocation): Mention $QUOTING_STYLE is significant. * src/du.c (main): Quote output when appropriate. * tests/du/basic.sh: Add a test case. * NEWS: Mention the improvement. --- NEWS | 6 +++--- doc/coreutils.texi | 5 +++++ src/du.c | 28 ++++++++++++++++++++++------ tests/du/basic.sh | 15 +++++++++++++++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 2db6c99ae..3a7b175e3 100644 --- a/NEWS +++ b/NEWS @@ -84,9 +84,9 @@ GNU coreutils NEWS -*- outline -*- 'install -C' will now avoid updating file metadata when the destination already has the appropriate ownership and permissions. - '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. + 'basename', 'du', '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, so users can better distinguish separating commas. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index b675b6345..441b80c3c 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -12436,6 +12436,11 @@ the argument being processed is on. @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 + Since @command{du} relies on information reported by the operating system, its output might not reflect the space consumed in the underlying devices. For example; diff --git a/src/du.c b/src/du.c index eefb24f05..7c9c486f1 100644 --- a/src/du.c +++ b/src/du.c @@ -26,7 +26,7 @@ #include <config.h> #include <getopt.h> #include <sys/types.h> -#include "argmatch.h" +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" #include "argv-iter.h" #include "assure.h" @@ -135,6 +135,9 @@ static bool hash_all; /* If true, output the NUL byte instead of a newline at the end of each line. */ static bool opt_nul_terminate_output = false; +/* If true, quote output pathnames according to the selected quoting style. */ +static bool quote_output; + /* If true, print a grand total at the end. */ static bool print_grand_total = false; @@ -445,10 +448,11 @@ print_only_size (uintmax_t n_bytes) stdout); } -/* Print size (and optionally time) indicated by *PDUI, followed by STRING. */ +/* Print size (and optionally time) indicated by *PDUI, followed by STRING, + quoting STRING if QUOTE is true. */ static void -print_size (const struct duinfo *pdui, char const *string) +print_size (const struct duinfo *pdui, char const *string, bool quote) { print_only_size (opt_inodes ? pdui->inodes @@ -466,7 +470,7 @@ print_size (const struct duinfo *pdui, char const *string) } } putchar ('\t'); - fputs (string, stdout); + fputs (quote ? quoteN (string) : string, stdout); putchar (opt_nul_terminate_output ? '\0' : '\n'); if (fflush (stdout) < 0) write_error (); @@ -718,7 +722,7 @@ process_file (FTS *fts, FTSENT *ent) if (opt_threshold < 0 ? v <= -opt_threshold : v >= opt_threshold) - print_size (&dui_to_print, file); + print_size (&dui_to_print, file, quote_output); } return ok; @@ -983,6 +987,18 @@ main (int argc, char **argv) if (!ok) usage (EXIT_FAILURE); + if (!opt_nul_terminate_output && 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 (opt_all && opt_summarize_only) { error (0, 0, _("cannot both summarize and show all entries")); @@ -1192,7 +1208,7 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from)); if (print_grand_total) - print_size (&tot_dui, _("total")); + print_size (&tot_dui, _("total"), false); return ok ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/du/basic.sh b/tests/du/basic.sh index 3bf74f712..45567f71d 100755 --- a/tests/du/basic.sh +++ b/tests/du/basic.sh @@ -28,6 +28,21 @@ printf '%*s' 257 make-sure-the-file-is-non-empty > a/b/F || framework_failure_ printf %4096s x > d/1 cp d/1 d/sub/2 +# QUOTING_STYLE does not affect redirected output. +touch 'q name' || framework_failure_ +printf '0\tq name\n' > exp || framework_failure_ +for style in literal shell-always invalid; do + QUOTING_STYLE="$style" du -b 'q name' > out 2> err || fail=1 + compare exp out || fail=1 + compare /dev/null err || fail=1 +done + +# --null disables quoting, and does not inspect QUOTING_STYLE. +QUOTING_STYLE=invalid du -0b 'q name' > out 2> err || fail=1 +printf '0\tq name\0' > exp || framework_failure_ +compare exp out || fail=1 +compare /dev/null err || fail=1 + B=$(stat --format=%B a/b/F) -- 2.55.0
