%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


Reply via email to