From: Collin Funk Date: Sun, 2 Aug 2026 02:38:50 +0000 (-0700) Subject: dirname: quote problematic names on tty X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=657394655ba848a21ac8a48cbbdd993a46f9baf3;p=thirdparty%2Fcoreutils.git dirname: quote problematic names on tty * doc/coreutils.texi (terminalQuoted): New macro. (readlink invocation, basename invocation, dirname invocation): Use it. * src/dirname.c (quote_output): New variable. (main): Quote output when appropriate. * src/system.h (quoteN_mem): New macro. * tests/misc/dirname.pl: Add test cases based on the ones added for basename in commit e897dfd02 (basename: quote problematic names on tty, 2026-07-30). Copy some logic from tests/misc/basename.pl to test the -z option. * tests/misc/tty-quoting.sh: Also test dirname. * NEWS: Mention the improvement. --- diff --git a/NEWS b/NEWS index 3a7b175e30..a85b0ada9d 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', '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. + 'basename', 'dirname', '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 441b80c3c5..939b0ccae6 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -11100,10 +11100,15 @@ variable is set. @end table +@c This is also used by basename and dirname. +@macro terminalQuoted 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: +@env{QUOTING_STYLE} specifies the quoting style. Valid quoting styles +are: @quotingStyles +@end macro +@terminalQuoted The @command{readlink} utility first appeared in OpenBSD 2.1. @@ -14203,10 +14208,7 @@ 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 +@terminalQuoted @exitstatus @@ -14263,6 +14265,8 @@ The program accepts the following option. Also see @ref{Common options}. @end table +@terminalQuoted + @exitstatus Examples: diff --git a/src/dirname.c b/src/dirname.c index dbd21d683e..1a7c61bdb5 100644 --- a/src/dirname.c +++ b/src/dirname.c @@ -22,6 +22,7 @@ #include #include +#include "argmatch.h" /* argmatch($QUOTING_STYLE). */ #include "system.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -31,6 +32,8 @@ proper_name ("David MacKenzie"), \ proper_name ("Jim Meyering") +static bool quote_output; + static struct option const longopts[] = { {"zero", no_argument, NULL, 'z'}, @@ -114,6 +117,19 @@ 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; + } + } + for (; optind < argc; optind++) { char const *result = argv[optind]; @@ -125,7 +141,11 @@ main (int argc, char **argv) result = ˙ len = 1; } - + if (quote_output) + { + result = quoteN_mem (result, len); + len = strlen (result); + } fwrite (result, 1, len, stdout); putchar (use_nuls ? '\0' :'\n'); } diff --git a/src/system.h b/src/system.h index 7efc78972d..017491d988 100644 --- a/src/system.h +++ b/src/system.h @@ -1004,6 +1004,8 @@ is_ENOTSUP (int err) /* Equivalent to quotearg(), but explicit to avoid syntax checks. */ #define quoteN(x) quotearg_style (get_quoting_style (NULL), x) +#define quoteN_mem(x, size) \ + quotearg_style_mem (get_quoting_style (NULL), x, size) #ifdef ARGMATCH /* Return the quoting style specified by the environment variable diff --git a/tests/misc/dirname.pl b/tests/misc/dirname.pl index 47701750f3..0b79cdee9b 100755 --- a/tests/misc/dirname.pl +++ b/tests/misc/dirname.pl @@ -50,9 +50,21 @@ my @Tests = ['l', qw(///a//b/), {OUT => '///a'}], ['m', qw(''), {OUT => '.'}], ['n', qw(a/b c/d), {OUT => "a\nc"}], + + # QUOTING_STYLE does not affect redirected output. + ['q-lit', q{'q name/f'}, {ENV => 'QUOTING_STYLE=literal'}, + {OUT => 'q name'}], + ['q-shell', q{'q name/f'}, {ENV => 'QUOTING_STYLE=shell-always'}, + {OUT => 'q name'}], + ['q-invalid', q{'q name/f'}, {ENV => 'QUOTING_STYLE=invalid'}, + {OUT => 'q name'}], + + ['z-quote', q{-z 'q name/f'}, {ENV => 'QUOTING_STYLE=invalid'}, + {OUT => "q name\0"}], ); # Append a newline to end of each expected 'OUT' string. +# Skip -z tests, i.e., those whose 'OUT' string has a trailing '\0'. my $t; foreach $t (@Tests) { @@ -61,7 +73,8 @@ foreach $t (@Tests) foreach $e (@$t) { $e->{OUT} = "$e->{OUT}\n" - if ref $e eq 'HASH' and exists $e->{OUT}; + if ref $e eq 'HASH' and exists $e->{OUT} + and not $e->{OUT} =~ /\0$/; } } diff --git a/tests/misc/tty-quoting.sh b/tests/misc/tty-quoting.sh index fe3f812894..a0dc1d4b8e 100755 --- a/tests/misc/tty-quoting.sh +++ b/tests/misc/tty-quoting.sh @@ -17,7 +17,7 @@ # along with this program. If not, see . . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src -print_ver_ basename du ls readlink realpath printf test +print_ver_ basename dirname du ls readlink realpath printf test require_strace_ ioctl touch 'b ar' || framework_failure_ @@ -44,15 +44,16 @@ run_tty_ env test -t 1 || run_tty_ env printf foo >printf.t && skip_ 'libc buffering induced a tty probe' -for cmd in basename du 'ls -w0' readlink 'realpath --relative-to=.'; do +for cmd in basename du dirname 'ls -w0' readlink 'realpath --relative-to=.'; do test "$cmd" = 'du' && field=2 || field=1 + test "$cmd" = 'dirname' && file='f oo/.' || file='f oo' - run_tty_ $cmd 'f oo' >quoted.t || fail=1 + run_tty_ $cmd "$file" >quoted.t || fail=1 cut -f$field- quoted.t >quoted || framework_failure_ # Note ls theoretically doesn't need isatty() for a specified QUOTING_STYLE # but it does need it to determine appropriate output format. - QUOTING_STYLE=literal run_tty_ $cmd 'f oo' >unquoted.t || fail=1 + QUOTING_STYLE=literal run_tty_ $cmd "$file" >unquoted.t || fail=1 cut -f$field- unquoted.t >unquoted || framework_failure_ env printf '%q\n' "$(cat unquoted)" >printf_quoted || framework_failure_