Hey Pádraig,
I'm 99% sure you meant to do 'dirname' as well in your recent patches.
But I haven't pushed this yet in case there was some reason you
avoided this one. It is mostly a copy of your 'basename' changes.
-- 8< --
* 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.
* NEWS: Mention the improvement.
---
NEWS | 6 +++---
doc/coreutils.texi | 14 +++++++++-----
src/dirname.c | 22 +++++++++++++++++++++-
src/system.h | 2 ++
tests/misc/dirname.pl | 15 ++++++++++++++-
5 files changed, 49 insertions(+), 10 deletions(-)
diff --git a/NEWS b/NEWS
index 3a7b175e3..a85b0ada9 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 441b80c3c..939b0ccae 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -11100,10 +11100,15 @@ @node readlink invocation
@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 @@ @node basename invocation
@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 @@ @node dirname invocation
@end table
+@terminalQuoted
+
@exitstatus
Examples:
diff --git a/src/dirname.c b/src/dirname.c
index dbd21d683..1a7c61bdb 100644
--- a/src/dirname.c
+++ b/src/dirname.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <sys/types.h>
+#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 7efc78972..017491d98 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 47701750f..0b79cdee9 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$/;
}
}
--
2.55.0