diff options
author | Jilles Tjoelker <jilles@FreeBSD.org> | 2014-12-14 16:26:19 +0000 |
---|---|---|
committer | Jilles Tjoelker <jilles@FreeBSD.org> | 2014-12-14 16:26:19 +0000 |
commit | 88ef06f3a9582f4f17a1cdb306cca14bf91c32bf (patch) | |
tree | 23fb567e57f826963e79f10430e2ea5fc2f2f033 /bin/sh/output.c | |
parent | cc13f05d04e32abe92dbfa3732ae6dc7915304de (diff) | |
download | src-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/sh/output.c')
-rw-r--r-- | bin/sh/output.c | 90 |
1 files changed, 68 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 |