From: Pádraig Brady Date: Wed, 3 Jun 2026 15:24:18 +0000 (+0100) Subject: stat: support %Qn format to output quoted file name X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=49f3b667199a97a50f06b3d44aead66952365f82;p=thirdparty%2Fcoreutils.git stat: support %Qn format to output quoted file name %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. --- diff --git a/doc/coreutils.texi b/doc/coreutils.texi index eaf65c3aa5..5ab056ce1c 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 a159d13581..3995acdfef 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 3645165829..7e62f015ca 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='