]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
stat: support %Qn format to output quoted file name
authorPádraig Brady <P@draigBrady.com>
Wed, 3 Jun 2026 15:24:18 +0000 (16:24 +0100)
committerPádraig Brady <P@draigBrady.com>
Thu, 4 Jun 2026 12:14:27 +0000 (13:14 +0100)
%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
src/stat.c
tests/stat/stat-fmt.sh

index eaf65c3aa55635d51428ec5476285f1cf696bbcc..5ab056ce1cf0a8e873f5d53eaff4bd9fae613d1a 100644 (file)
@@ -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
index a159d13581333486c916c62ecdba063042ea42a8..3995acdfefe033b0be5ab2fe55e8e6e965fc36f4 100644 (file)
@@ -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\
index 3645165829d5a3f026b36ff219fc9b936c8259f1..7e62f015caca49e7ead3cb4a98fa71b9128cabcf 100755 (executable)
@@ -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='