1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-01 12:19:28 +00:00

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.
This commit is contained in:
Jilles Tjoelker 2014-12-14 16:26:19 +00:00
parent cc13f05d04
commit 88ef06f3a9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=275766
3 changed files with 76 additions and 22 deletions

View File

@ -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

View File

@ -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

View File

@ -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