summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2015-11-03 12:56:22 +0000
committerPádraig Brady <P@draigBrady.com>2015-11-04 23:30:15 +0000
commit5ef08864113505c6392158c9fac9a6cb1b3ac0e6 (patch)
tree03989580af0b143935d1319e69f6fd519d880aab
parent697b8ce26549cceffa8745a1dc0f13654173c575 (diff)
downloadcoreutils-5ef08864113505c6392158c9fac9a6cb1b3ac0e6.tar.xz
printf: support the %q format to quote for shell
* 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.
-rw-r--r--NEWS6
-rw-r--r--doc/coreutils.texi10
-rw-r--r--src/printf.c20
-rw-r--r--tests/local.mk1
-rwxr-xr-xtests/misc/printf-quote.sh57
5 files changed, 92 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 30603e62e..38b466465 100644
--- 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,
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 177379b38..8618a6457 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -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
@@ -12084,6 +12084,14 @@ 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
@samp{-0003}.
diff --git a/src/printf.c b/src/printf.c
index 3e68b995f..a24494631 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -41,6 +41,10 @@
%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'] =
diff --git a/tests/local.mk b/tests/local.mk
index ee4068d64..f65f7bcc8 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -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
index 000000000..73fc404b8
--- /dev/null
+++ b/tests/misc/printf-quote.sh
@@ -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