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:
parent
cc13f05d04
commit
88ef06f3a9
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=275766
@ -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
|
||||
|
@ -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
|
||||
|
7
bin/sh/tests/execution/set-x4.0
Normal file
7
bin/sh/tests/execution/set-x4.0
Normal 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
|
Loading…
Reference in New Issue
Block a user