ash: improve set -x to quote strings as necessary

Basen on the patch from Martijn Dekker <martijn@inlv.org>

function                                             old     new   delta
evalcommand                                         1161    1302    +141
maybe_single_quote                                     -      60     +60
getoptscmd                                           527     546     +19
readtoken1                                          2819    2823      +4
localcmd                                             366     364      -2
evaltreenr                                           495     479     -16
evaltree                                             495     479     -16
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/3 up/down: 224/-34)           Total: 190 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-07-21 13:20:14 +02:00
parent 168f0ef8dd
commit 42ba757d5e
3 changed files with 94 additions and 11 deletions

View File

@ -1742,7 +1742,7 @@ number(const char *s)
}
/*
* Produce a possibly single quoted string suitable as input to the shell.
* Produce a single quoted string suitable as input to the shell.
* The return string is allocated on the stack.
*/
static char *
@ -1786,6 +1786,47 @@ single_quote(const char *s)
return stackblock();
}
/*
* Produce a possibly single quoted string suitable as input to the shell.
* If 'conditional' is nonzero, quoting is only done if the string contains
* non-shellsafe characters, or is identical to a shell keyword (reserved
* word); if it is zero, quoting is always done.
* If quoting was done, the return string is allocated on the stack,
* otherwise a pointer to the original string is returned.
*/
static const char *
maybe_single_quote(const char *s)
{
const char *p = s;
while (*p) {
/* Assuming ACSII */
/* quote ctrl_chars space !"#$%&'()* */
if (*p < '+')
goto need_quoting;
/* quote ;<=>? */
if (*p >= ';' && *p <= '?')
goto need_quoting;
/* quote `[\ */
if (*p == '`')
goto need_quoting;
if (*p == '[')
goto need_quoting;
if (*p == '\\')
goto need_quoting;
/* quote {|}~ DEL and high bytes */
if (*p > 'z')
goto need_quoting;
/* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
/* TODO: maybe avoid quoting % */
p++;
}
return s;
need_quoting:
return single_quote(s);
}
/* ============ nextopt */
@ -9700,18 +9741,36 @@ evalcommand(union node *cmd, int flags)
/* Print the command if xflag is set. */
if (xflag) {
int n;
const char *p = " %s" + 1;
const char *pfx = "";
fdprintf(preverrout_fd, "%s", expandstr(ps4val()));
fdprintf(preverrout_fd, p, expandstr(ps4val()));
sp = varlist.list;
for (n = 0; n < 2; n++) {
while (sp) {
fdprintf(preverrout_fd, p, sp->text);
char *varval = sp->text;
char *eq = strchrnul(varval, '=');
if (*eq)
eq++;
fdprintf(preverrout_fd, "%s%.*s%s",
pfx,
(int)(eq - varval), varval,
maybe_single_quote(eq)
);
sp = sp->next;
p = " %s";
pfx = " ";
}
sp = arglist.list;
while (sp) {
fdprintf(preverrout_fd, "%s%s",
pfx,
/* always quote if matches reserved word: */
findkwd(sp->text)
? single_quote(sp->text)
: maybe_single_quote(sp->text)
);
sp = sp->next;
pfx = " ";
}
safe_write(preverrout_fd, "\n", 1);
}

View File

@ -0,0 +1,10 @@
+ var1=val
+ var2='one two'
+ true '%s\n' one 'two '"'"'three' four
+ this=command
+ 'this=command'
./mode_x.tests: line 1: this=command: not found
+ true
+ true
+ 'if' true
./mode_x.tests: line 1: if: not found

View File

@ -0,0 +1,14 @@
set -x
var1=val
var2='one two'
true %s\\n one "two 'three" four
# assignment:
this=command
# NOT assignment, +x code should show it quoted:
"this=command"
if true; then true; fi
# +x code should quote 'if' here:
"if" true