%N also outputs the target of a symlink
so support %Qn to give more control over quoted names.
* src/stat.c (print_it): Validate %Qn.
(print_stat): Output quoted file name with %Qn.
(usage): Add %Qn description.
* doc/coreutils.texi (stat invocation): Likewise.
* tests/stat/stat-fmt.sh: Add a test case.
---
doc/coreutils.texi | 4 ++-
src/stat.c | 81 +++++++++++++++++++++++++-----------------
tests/stat/stat-fmt.sh | 9 +++++
3 files changed, 60 insertions(+), 34 deletions(-)
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index eaf65c3aa..5ab056ce1 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -12571,6 +12571,7 @@ The valid @var{format} directives for files with
@option{--format} and
@item %i -- Inode number
@item %m -- Mount point (see selow)
@item %n -- File name
+@item %Qn -- Quoted file name (see below)
@item %N -- Quoted file name with dereference if symbolic link (see below)
@item %o -- Optimal I/O transfer size hint
@item %s -- Total size, in bytes
@@ -12597,7 +12598,7 @@ to control the zero padding of the output with the
@samp{#} and @samp{0}
printf flags. For example to pad to at least 3 wide while making larger
numbers unambiguously octal, you can use @samp{%#03a}.
-The @samp{%N} format can be set with the environment variable
+The @samp{%N} and @samp{%Qn} formats can be set with the environment variable
@env{QUOTING_STYLE}@. If that environment variable is not set,
the default value is @samp{shell-escape-always}. Valid quoting styles are:
@quotingStyles
@@ -12661,6 +12662,7 @@ you must use a different set of @var{format} directives:
@item %i -- File System ID in hex
@item %l -- Maximum length of file names
@item %n -- File name
+@item %Qn -- Quoted file name
@item %s -- Block size (for faster transfers)
@item %S -- Fundamental block size (for block counts)
@item %t -- Type in hex
diff --git a/src/stat.c b/src/stat.c
index a159d1358..3995acdfe 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -855,6 +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. */
+
+static void
+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)
+ {
+ 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_always_quoting_style);
+ error (0, 0, _("ignoring invalid value of environment "
+ "variable QUOTING_STYLE: %s"), quote (q_style));
+ }
+ }
+ else
+ set_quoting_style (NULL, shell_escape_always_quoting_style);
+}
+
+/* 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
@@ -868,6 +899,11 @@ print_statfs (char *pformat, size_t prefix_len,
MAYBE_UNUSED char mod, char m,
switch (m)
{
case 'n':
+ if (mod == 'Q')
+ {
+ getenv_quoting_style ();
+ filename = quoteN (filename);
+ }
out_string (pformat, prefix_len, filename);
break;
@@ -1044,37 +1080,6 @@ neg_to_zero (struct timespec ts)
return z;
}
-/* Set the quoting style default if the environment variable
- QUOTING_STYLE is set. */
-
-static void
-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)
- {
- 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_always_quoting_style);
- error (0, 0, _("ignoring invalid value of environment "
- "variable QUOTING_STYLE: %s"), quote (q_style));
- }
- }
- else
- set_quoting_style (NULL, shell_escape_always_quoting_style);
-}
-
-/* Equivalent to quotearg(), but explicit to avoid syntax checks. */
-#define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
-
/* Output a single-character \ escape. */
static void
@@ -1180,10 +1185,13 @@ print_it (char const *format, int fd, char const
*filename,
break;
case 'H':
case 'L':
+ case 'Q':
mod_char = fmt_char;
fmt_char = *(b + 1);
- if (print_func == print_stat
- && (fmt_char == 'd' || fmt_char == 'r'))
+ if ((mod_char == 'Q' && fmt_char == 'n')
+ || (print_func == print_stat
+ && (mod_char == 'H' || mod_char == 'L')
+ && (fmt_char == 'd' || fmt_char == 'r')))
{
b++;
}
@@ -1517,6 +1525,11 @@ print_stat (char *pformat, size_t prefix_len, char mod,
char m,
switch (m)
{
case 'n':
+ if (mod == 'Q')
+ {
+ getenv_quoting_style ();
+ filename = quoteN (filename);
+ }
out_string (pformat, prefix_len, filename);
break;
case 'N':
@@ -1827,6 +1840,7 @@ The valid format sequences for files (without
--file-system):\n\
%i inode number\n\
%m mount point\n\
%n file name\n\
+ %Qn quoted file name\n\
%N quoted file name with dereference if symbolic link\n\
%o optimal I/O transfer size hint\n\
%s total size, in bytes\n\
@@ -1864,6 +1878,7 @@ Valid format sequences for file systems:\n\
%i file system ID in hex\n\
%l maximum length of filenames\n\
%n file name\n\
+ %Qn quoted file name\n\
%s block size (for faster transfers)\n\
%S fundamental block size (for block counts)\n\
%t file system type in hex\n\
diff --git a/tests/stat/stat-fmt.sh b/tests/stat/stat-fmt.sh
index 364516582..7e62f015c 100755
--- a/tests/stat/stat-fmt.sh
+++ b/tests/stat/stat-fmt.sh
@@ -40,6 +40,15 @@ cat <<\EOF >exp
EOF
compare exp out || fail=1
+# ensure QUOTING_STYLE is honored by %Qn
+stat -c%Qn \' > out || fail=1
+QUOTING_STYLE=locale stat -c%Qn \' >> out || fail=1
+cat <<\EOF >exp
+"'"
+'\''
+EOF
+compare exp out || fail=1
+
# ensure control characters in file names are escaped by %N
# using the default shell-escape quoting style.
nl='
--
2.54.0