aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/ash.c81
-rw-r--r--shell/ash_test/ash-quoting/mode_x.right10
-rwxr-xr-xshell/ash_test/ash-quoting/mode_x.tests14
3 files changed, 94 insertions, 11 deletions
diff --git a/shell/ash.c b/shell/ash.c
index b28731eb1..a461bb7df 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -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);
- sp = sp->next;
- p = " %s";
- }
- sp = arglist.list;
+ while (sp) {
+ 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;
+ 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);
}
diff --git a/shell/ash_test/ash-quoting/mode_x.right b/shell/ash_test/ash-quoting/mode_x.right
new file mode 100644
index 000000000..c2dd3550c
--- /dev/null
+++ b/shell/ash_test/ash-quoting/mode_x.right
@@ -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
diff --git a/shell/ash_test/ash-quoting/mode_x.tests b/shell/ash_test/ash-quoting/mode_x.tests
new file mode 100755
index 000000000..16dae3f4b
--- /dev/null
+++ b/shell/ash_test/ash-quoting/mode_x.tests
@@ -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