Subject: [PATCH] printf: add %#s alias to %b

From: Eric Blake <[email-masked]>
Date: Thu, 31 Aug 2023 13:31:57 -0500

POSIX Issue 8 will be obsoleting %b (escape sequence interpolation) so
that future Issue 9 can change to having %b (binary literal output)
that aligns with C2x.  But since escape interpolation may still remain
useful, POSIX suggested %#s (which is undefined in all versions of C)
as a possible alias for the older %b behavior.

* src/printf.c (print_formatted, usage): Support %#s as an alias
for %b, in order to open doors to future repurposing of %b to
binary output while still allowing access to its old behavior.
* doc/coreutils.texi (printf invocation): Document it.
* NEWS: Likewise.
* tests/printf/printf-quote.sh: Add unit test coverage.
Fixes: https://bugs.gnu.org/65659
---
 NEWS                         | 9 +++++++++
 doc/coreutils.texi           | 3 ++-
 src/printf.c                 | 9 ++++++---
 tests/printf/printf-quote.sh | 4 +++-
 4 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index c00ff0cf2..2e2acc7cd 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,15 @@ GNU coreutils NEWS                                    -*- outline -*-
   numfmt options like --suffix no longer have an arbitrary 127-byte limit.
   [bug introduced with numfmt in coreutils-8.21]

+** Changes in behavior
+
+  'printf' now treats the format sequence '%#s' as an alias for the
+  older '%b' meaning escape sequence interpolation.  It is anticipated
+  that a future version of coreutils will change the meaning of '%b'
+  to output binary literals, comparable to the meaning being added to
+  printf(3) by C23, which will leave '%#s' (which C does not specify)
+  as the only way to access the older behavior of escape
+  interpolation.

 * Noteworthy changes in release 9.4 (2023-08-29) [stable]

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 373a407ed..81a8ca4ff 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -13331,7 +13331,8 @@ printf invocation

 @item
 @kindex %b
-An additional directive @samp{%b}, prints its
+@kindex %#s
+An additional directive @samp{%#s}, or its older alias @samp{%b}, prints its
 argument string with @samp{\} escapes interpreted in the same way as in
 the @var{format} string, except that octal escapes are of the form
 @samp{\0@var{ooo}} where @var{ooo} is 0 to 3 octal digits.  If
diff --git a/src/printf.c b/src/printf.c
index 9670d8043..e8866d50e 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -38,7 +38,7 @@

    Additional directive:

-   %b = print an argument string, interpreting backslash escapes,
+   %b, %#s = print an argument string, interpreting backslash escapes,
      except that octal escapes are of the form \0 or \0ooo.

    %q = print an argument string in a format that can be
@@ -126,8 +126,9 @@ FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
 "), stdout);
       fputs (_("\
   %%      a single %\n\
-  %b      ARGUMENT as a string with '\\' escapes interpreted,\n\
+  %#s     ARGUMENT as a string with '\\' escapes interpreted,\n\
           except that octal escapes are of the form \\0 or \\0NNN\n\
+  %b      obsolescent alias for %#s\n\
   %q      ARGUMENT is printed in a format that can be reused as shell input,\n\
           escaping non-printable characters with the proposed POSIX $'' syntax.\
 \n\n\
@@ -509,7 +510,7 @@ print_formatted (char const *format, int argc, char **argv)
               putchar ('%');
               break;
             }
-          if (*f == 'b')
+          if (*f == 'b' || (*f == '#' && f[1] == 's'))
             {
               /* FIXME: Field width and precision are not supported
                  for %b, even though POSIX requires it.  */
@@ -519,6 +520,8 @@ print_formatted (char const *format, int argc, char **argv)
                   ++argv;
                   --argc;
                 }
+              if (*f == '#')
+                f++;
               break;
             }

diff --git a/tests/printf/printf-quote.sh b/tests/printf/printf-quote.sh
index d1671bd9d..33ce3018e 100755
--- a/tests/printf/printf-quote.sh
+++ b/tests/printf/printf-quote.sh
@@ -22,7 +22,8 @@ print_ver_ printf
 prog='env printf'

 # Equivalent output to ls --quoting=shell-escape
-$prog '%q\n' '' "'" a 'a b' '~a' 'a~' "$($prog %b 'a\r')" > out
+$prog '%q\n' '' "'" a 'a b' '~a' 'a~' "$($prog %b 'a\r')" \
+      "$($prog %#s 'a\r')" > out
 cat <<\EOF > exp || framework_failure_
 ''
 "'"
@@ -31,6 +32,7 @@ a
 '~a'
 a~
 'a'$'\r'
+'a'$'\r'
 EOF
 compare exp out || fail=1

-- 
2.41.0