aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorJilles Tjoelker <jilles@FreeBSD.org>2014-12-14 16:26:19 +0000
committerJilles Tjoelker <jilles@FreeBSD.org>2014-12-14 16:26:19 +0000
commit88ef06f3a9582f4f17a1cdb306cca14bf91c32bf (patch)
tree23fb567e57f826963e79f10430e2ea5fc2f2f033 /bin
parentcc13f05d04e32abe92dbfa3732ae6dc7915304de (diff)
downloadsrc-88ef06f3a9582f4f17a1cdb306cca14bf91c32bf.tar.gz
src-88ef06f3a9582f4f17a1cdb306cca14bf91c32bf.zip
sh: Make sure output suitable as shell input is also printable.
Commands like 'export -p', 'set' and 'trap', and tracing enabled via 'set -x' generate output suitable as shell input by adding quotes as necessary. If there are control characters other than newline or invalid UTF-8 sequences, use $'...' and \OOO to display them safely. The resulting output is not parsable by a strict POSIX.1-2008 shell but sh from FreeBSD 9.0 and newer and many other shells can parse it.
Notes
Notes: svn path=/head/; revision=275766
Diffstat (limited to 'bin')
-rw-r--r--bin/sh/output.c90
-rw-r--r--bin/sh/tests/execution/Makefile1
-rw-r--r--bin/sh/tests/execution/set-x4.07
3 files changed, 76 insertions, 22 deletions
diff --git a/bin/sh/output.c b/bin/sh/output.c
index c6d118f11535..39b722fdba23 100644
--- a/bin/sh/output.c
+++ b/bin/sh/output.c
@@ -54,6 +54,8 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
#include "shell.h"
#include "syntax.h"
@@ -111,42 +113,86 @@ outstr(const char *p, struct output *file)
outbin(p, strlen(p), file);
}
+static void
+byteseq(int ch, struct output *file)
+{
+ char seq[4];
+
+ seq[0] = '\\';
+ seq[1] = (ch >> 6 & 0x3) + '0';
+ seq[2] = (ch >> 3 & 0x7) + '0';
+ seq[3] = (ch & 0x7) + '0';
+ outbin(seq, 4, file);
+}
+
+static void
+outdqstr(const char *p, struct output *file)
+{
+ const char *end;
+ mbstate_t mbs;
+ size_t clen;
+ wchar_t wc;
+
+ memset(&mbs, '\0', sizeof(mbs));
+ end = p + strlen(p);
+ outstr("$'", file);
+ while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
+ if (clen == (size_t)-2) {
+ while (p < end)
+ byteseq(*p++, file);
+ break;
+ }
+ if (clen == (size_t)-1) {
+ memset(&mbs, '\0', sizeof(mbs));
+ byteseq(*p++, file);
+ continue;
+ }
+ if (wc == L'\n')
+ outcslow('\n', file), p++;
+ else if (wc == L'\r')
+ outstr("\\r", file), p++;
+ else if (wc == L'\t')
+ outstr("\\t", file), p++;
+ else if (!iswprint(wc)) {
+ for (; clen > 0; clen--)
+ byteseq(*p++, file);
+ } else {
+ if (wc == L'\'' || wc == L'\\')
+ outcslow('\\', file);
+ outbin(p, clen, file);
+ p += clen;
+ }
+ }
+ outcslow('\'', file);
+}
+
/* Like outstr(), but quote for re-input into the shell. */
void
outqstr(const char *p, struct output *file)
{
- char ch;
- int inquotes;
+ int i;
if (p[0] == '\0') {
outstr("''", file);
return;
}
- if (p[strcspn(p, "|&;<>()$`\\\"' \t\n*?[~#=")] == '\0' ||
+ for (i = 0; p[i] != '\0'; i++) {
+ if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
+ (p[i] & 0x80) != 0 || p[i] == '\'') {
+ outdqstr(p, file);
+ return;
+ }
+ }
+
+ if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
strcmp(p, "[") == 0) {
outstr(p, file);
return;
}
- inquotes = 0;
- while ((ch = *p++) != '\0') {
- switch (ch) {
- case '\'':
- /* Can't quote single quotes inside single quotes. */
- if (inquotes)
- outcslow('\'', file);
- inquotes = 0;
- outstr("\\'", file);
- break;
- default:
- if (!inquotes)
- outcslow('\'', file);
- inquotes = 1;
- outc(ch, file);
- }
- }
- if (inquotes)
- outcslow('\'', file);
+ outcslow('\'', file);
+ outstr(p, file);
+ outcslow('\'', file);
}
void
diff --git a/bin/sh/tests/execution/Makefile b/bin/sh/tests/execution/Makefile
index 2653d5ffe799..638492bb96ad 100644
--- a/bin/sh/tests/execution/Makefile
+++ b/bin/sh/tests/execution/Makefile
@@ -44,6 +44,7 @@ FILES+= set-n4.0
FILES+= set-x1.0
FILES+= set-x2.0
FILES+= set-x3.0
+FILES+= set-x4.0
FILES+= shellproc1.0
FILES+= subshell1.0 subshell1.0.stdout
FILES+= subshell2.0
diff --git a/bin/sh/tests/execution/set-x4.0 b/bin/sh/tests/execution/set-x4.0
new file mode 100644
index 000000000000..0904766ccdd1
--- /dev/null
+++ b/bin/sh/tests/execution/set-x4.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+key=`printf '\r\t\001\200\300'`
+r=`{ set -x; : "$key"; } 2>&1 >/dev/null`
+case $r in
+*[![:print:]]*) echo fail; exit 3
+esac