]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
printf: support the %q format to quote for shell
authorPádraig Brady <P@draigBrady.com>
Tue, 3 Nov 2015 12:56:22 +0000 (12:56 +0000)
committerPádraig Brady <P@draigBrady.com>
Wed, 4 Nov 2015 23:30:15 +0000 (23:30 +0000)
* src/printf.c (usage): Mention the new format.
(print_formatted): Handle the quoting by calling
out to the quotearg module with "shell-escape" mode.
* doc/coreutils.texi (printf invocation): Document %q.
* tests/misc/printf-quote.sh: New test.
* tests/local.mk: Reference new test.
* NEWS: Mention the new feature.

NEWS
doc/coreutils.texi
src/printf.c
tests/local.mk
tests/misc/printf-quote.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 30603e62ea0e13dd48b34602381b52318c85368c..38b466465704ca931a6a865674faa9c777d2d155 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,12 @@ GNU coreutils NEWS                                    -*- outline -*-
   base32 is added to complement the existing base64 command,
   and encodes and decodes printable text as per RFC 4648.
 
+** New features
+
+  printf now supports the '%q' format to print arguments in a form that
+  is reusable by most shells, with non-printable characters escaped
+  with the POSIX proposed $'...' syntax.
+
 ** Changes in behavior
 
   base64 no longer supports hex or oct --wrap parameters,
index 177379b3812a64ad286f0c64a6053ad07dc1c13a..8618a645739b0fb40dd3bc3fc83649506c1be76f 100644 (file)
@@ -12075,7 +12075,7 @@ one.
 
 @item
 @kindex %b
-@command{printf} has an additional directive, @samp{%b}, which prints its
+An additional directive @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
@@ -12083,6 +12083,14 @@ the @var{format} string, except that octal escapes are of the form
 If a precision is also given, it limits the number of bytes printed
 from the converted string.
 
+@item
+@kindex %q
+An additional directive @samp{%q}, prints its argument string
+in a format that can be reused as input by most shells.
+Non-printable characters are escaped with the POSIX proposed @samp{$''} syntax,
+and shell metacharacters are quoted appropriately.
+This is an equivalent format to @command{ls --quoting=shell-escape} output.
+
 @item
 Numeric arguments must be single C constants, possibly with leading
 @samp{+} or @samp{-}.  For example, @samp{printf %.4d -3} outputs
index 3e68b995f72616573c7df9c05418fd55f38e7898..a244946318a9d6a3788216174428dc65e26cb306 100644 (file)
    %b = 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
+     reused as shell input.  Escaped characters used the proposed
+     POSIX $'' syntax supported by most shells.
+
    The 'format' argument is re-used as many times as necessary
    to convert all of the given arguments.
 
@@ -124,7 +128,9 @@ FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
   %%      a single %\n\
   %b      ARGUMENT as a string with '\\' escapes interpreted,\n\
           except that octal escapes are of the form \\0 or \\0NNN\n\
-\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\
 and all C format specifications ending with one of diouxXfeEgGcs, with\n\
 ARGUMENTs converted to proper type first.  Variable widths are handled.\n\
 "), stdout);
@@ -506,6 +512,18 @@ print_formatted (const char *format, int argc, char **argv)
               break;
             }
 
+          if (*f == 'q')
+            {
+              if (argc > 0)
+                {
+                  fputs (quotearg_style (shell_escape_quoting_style, *argv),
+                         stdout);
+                  ++argv;
+                  --argc;
+                }
+              break;
+            }
+
           memset (ok, 0, sizeof ok);
           ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] =
             ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] =
index ee4068d643d62a662fef02c473c77c5f79d13895..f65f7bcc8cdfa281f220b53f3de0ebbceb9e39cb 100644 (file)
@@ -313,6 +313,7 @@ all_tests =                                 \
   tests/misc/printf-cov.pl                     \
   tests/misc/printf-hex.sh                     \
   tests/misc/printf-surprise.sh                        \
+  tests/misc/printf-quote.sh                   \
   tests/misc/pwd-long.sh                       \
   tests/misc/readlink-fp-loop.sh               \
   tests/misc/readlink-root.sh                  \
diff --git a/tests/misc/printf-quote.sh b/tests/misc/printf-quote.sh
new file mode 100755 (executable)
index 0000000..73fc404
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+# tests for printf %q
+
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+prog='env printf'
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ printf
+
+# Equivalent output to ls --quoting=shell-escape
+env printf '%q\n' '' "'" a 'a b' '~a' 'a~' "$(env printf %b 'a\r')" > out
+cat <<\EOF > exp || framework_failure_
+''
+''\'''
+a
+'a b'
+'~a'
+a~
+'a'$'\r'
+EOF
+compare exp out || fail=1
+
+unset LC_ALL
+f=$LOCALE_FR_UTF8
+: ${LOCALE_FR_UTF8=none}
+if test "$LOCALE_FR_UTF8" != "none"; then
+  (
+   #printable multi-byte
+   LC_ALL=$f env printf '%q\n' 'áḃç' > out
+   #non-printable multi-byte
+   LC_ALL=$f env printf '%q\n' "$(LC_ALL=$f env printf '\u0378')" >> out
+   #printable multi-byte in C locale
+   LC_ALL=C env printf '%q\n' 'áḃç' >> out
+  )
+  cat <<\EOF > exp || framework_failure_
+áḃç
+''$'\315\270'
+''$'\303\241\341\270\203\303\247'
+EOF
+  compare exp out || fail=1
+fi
+
+Exit $fail