From 8de5b9f88ba9fe2f203abab9ca7d85129c3eb679 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Tue, 13 Feb 2018 14:43:29 +0100
Subject: ash : fix double-quoted "\z" handling

function                                             old     new   delta
readtoken1                                          2602    2608      +6

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c                                      | 23 ++++++++++----
 shell/ash_test/ash-quoting/bkslash_case1.right   | 10 +++++++
 shell/ash_test/ash-quoting/bkslash_case1.tests   | 38 ++++++++++++++++++++++++
 shell/hush_test/hush-quoting/bkslash_case1.right | 10 +++++++
 shell/hush_test/hush-quoting/bkslash_case1.tests | 38 ++++++++++++++++++++++++
 5 files changed, 113 insertions(+), 6 deletions(-)
 create mode 100644 shell/ash_test/ash-quoting/bkslash_case1.right
 create mode 100755 shell/ash_test/ash-quoting/bkslash_case1.tests
 create mode 100644 shell/hush_test/hush-quoting/bkslash_case1.right
 create mode 100755 shell/hush_test/hush-quoting/bkslash_case1.tests

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index 4c1b5e409..5e281b5ce 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6146,12 +6146,12 @@ rmescapes(char *str, int flag, int *slash_position)
 				if (*p == '*'
 				 || *p == '?'
 				 || *p == '['
-				 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
-				 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
-				 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
-				 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
+				 || *p == '\\' /* case '\' in \\    ) echo ok;; *) echo WRONG;; esac */
+				 || *p == ']'  /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
+				 || *p == '-'  /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
+				 || *p == '!'  /* case '!' in [\!]  ) echo ok;; *) echo WRONG;; esac */
 				/* Some libc support [^negate], that's why "^" also needs love */
-				 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
+				 || *p == '^'  /* case '^' in [\^]  ) echo ok;; *) echo WRONG;; esac */
 				) {
 					*q++ = '\\';
 				}
@@ -11992,13 +11992,24 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
 					USTPUTC(CTLESC, out);
 					USTPUTC('\\', out);
 				}
-				/* Backslash is retained if we are in "str" and next char isn't special */
+				/* Backslash is retained if we are in "str"
+				 * and next char isn't dquote-special.
+				 */
 				if (dblquote
 				 && c != '\\'
 				 && c != '`'
 				 && c != '$'
 				 && (c != '"' || eofmark != NULL)
 				) {
+//dash survives not doing USTPUTC(CTLESC), but merely by chance:
+//Example: "\z" gets encoded as "\<CTLESC>z".
+//rmescapes() then emits "\", "\z", protecting z from globbing.
+//But it's wrong, should protect _both_ from globbing:
+//everything in double quotes is not globbed.
+//Unlike dash, we have a fix in rmescapes() which emits bare "z"
+//for "<CTLESC>z" since "z" is not glob-special (else unicode may break),
+//and glob would see "\z" and eat "\". Thus:
+					USTPUTC(CTLESC, out); /* protect '\' from glob */
 					USTPUTC('\\', out);
 				}
 				USTPUTC(CTLESC, out);
diff --git a/shell/ash_test/ash-quoting/bkslash_case1.right b/shell/ash_test/ash-quoting/bkslash_case1.right
new file mode 100644
index 000000000..1b52491f7
--- /dev/null
+++ b/shell/ash_test/ash-quoting/bkslash_case1.right
@@ -0,0 +1,10 @@
+ok1
+ok2
+ok3
+ok4
+ok5
+Ok:0
+ok6
+ok7
+ok8
+Ok:0
diff --git a/shell/ash_test/ash-quoting/bkslash_case1.tests b/shell/ash_test/ash-quoting/bkslash_case1.tests
new file mode 100755
index 000000000..d0c359927
--- /dev/null
+++ b/shell/ash_test/ash-quoting/bkslash_case1.tests
@@ -0,0 +1,38 @@
+# Case argument is globbed, match patterns are not.
+# This caught some bugs in the past.
+
+case z in
+\z  ) echo ok1 ;;
+*   ) echo BUG ;;
+esac
+case \z in
+z   ) echo ok2 ;;
+*   ) echo BUG ;;
+esac
+case \z in
+\z  ) echo ok3 ;;
+*   ) echo BUG ;;
+esac
+case z in
+\z  ) echo ok4 ;;
+*   ) echo BUG ;;
+esac
+case \\z in
+\\z ) echo ok5 ;;
+*   ) echo BUG ;;
+esac
+echo Ok:$?
+
+case "\z" in
+"\z"  ) echo ok6 ;;
+*     ) echo BUG ;;
+esac
+case "\\z" in
+"\\z" ) echo ok7 ;;
+*     ) echo BUG ;;
+esac
+case "\\\z" in
+"\\\z") echo ok8 ;;
+*     ) echo BUG ;;
+esac
+echo Ok:$?
diff --git a/shell/hush_test/hush-quoting/bkslash_case1.right b/shell/hush_test/hush-quoting/bkslash_case1.right
new file mode 100644
index 000000000..1b52491f7
--- /dev/null
+++ b/shell/hush_test/hush-quoting/bkslash_case1.right
@@ -0,0 +1,10 @@
+ok1
+ok2
+ok3
+ok4
+ok5
+Ok:0
+ok6
+ok7
+ok8
+Ok:0
diff --git a/shell/hush_test/hush-quoting/bkslash_case1.tests b/shell/hush_test/hush-quoting/bkslash_case1.tests
new file mode 100755
index 000000000..d0c359927
--- /dev/null
+++ b/shell/hush_test/hush-quoting/bkslash_case1.tests
@@ -0,0 +1,38 @@
+# Case argument is globbed, match patterns are not.
+# This caught some bugs in the past.
+
+case z in
+\z  ) echo ok1 ;;
+*   ) echo BUG ;;
+esac
+case \z in
+z   ) echo ok2 ;;
+*   ) echo BUG ;;
+esac
+case \z in
+\z  ) echo ok3 ;;
+*   ) echo BUG ;;
+esac
+case z in
+\z  ) echo ok4 ;;
+*   ) echo BUG ;;
+esac
+case \\z in
+\\z ) echo ok5 ;;
+*   ) echo BUG ;;
+esac
+echo Ok:$?
+
+case "\z" in
+"\z"  ) echo ok6 ;;
+*     ) echo BUG ;;
+esac
+case "\\z" in
+"\\z" ) echo ok7 ;;
+*     ) echo BUG ;;
+esac
+case "\\\z" in
+"\\\z") echo ok8 ;;
+*     ) echo BUG ;;
+esac
+echo Ok:$?
-- 
cgit v1.2.3-55-g6feb


From aa617ac09703f31e9f80836f8d8f9349b52578ee Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Tue, 13 Feb 2018 15:30:13 +0100
Subject: hush: simplify process_command_subs()

Incidentally, this fixes LINENO bug here:

echo "1:${LINENO}"
echo "2:`echo; echo`"  # was counting lines in the `cmd` output as LINENO++
echo "3:${LINENO}"

function                                             old     new   delta
parse_and_run_file                                    53      71     +18
expand_vars_to_list                                 1187    1164     -23
setup_file_in_str                                     25       -     -25
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 1/1 up/down: 18/-48)            Total: -30 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/hush.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

(limited to 'shell')

diff --git a/shell/hush.c b/shell/hush.c
index 8f1017e3c..e005b0a20 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -6614,24 +6614,22 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
 static void parse_and_run_string(const char *s)
 {
 	struct in_str input;
+	//IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;)
+
 	setup_string_in_str(&input, s);
 	parse_and_run_stream(&input, '\0');
+	//IF_HUSH_LINENO_VAR(G.lineno = sv;)
 }
 
 static void parse_and_run_file(FILE *f)
 {
 	struct in_str input;
-#if ENABLE_HUSH_LINENO_VAR
-	unsigned sv;
+	IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;)
 
-	sv = G.lineno;
-	G.lineno = 1;
-#endif
+	IF_HUSH_LINENO_VAR(G.lineno = 1;)
 	setup_file_in_str(&input, f);
 	parse_and_run_stream(&input, ';');
-#if ENABLE_HUSH_LINENO_VAR
-	G.lineno = sv;
-#endif
+	IF_HUSH_LINENO_VAR(G.lineno = sv;)
 }
 
 #if ENABLE_HUSH_TICK
@@ -6744,16 +6742,16 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
 static int process_command_subs(o_string *dest, const char *s)
 {
 	FILE *fp;
-	struct in_str pipe_str;
 	pid_t pid;
 	int status, ch, eol_cnt;
 
 	fp = generate_stream_from_string(s, &pid);
 
 	/* Now send results of command back into original context */
-	setup_file_in_str(&pipe_str, fp);
 	eol_cnt = 0;
-	while ((ch = i_getch(&pipe_str)) != EOF) {
+	while ((ch = getc(fp)) != EOF) {
+		if (ch == '\0')
+			continue;
 		if (ch == '\n') {
 			eol_cnt++;
 			continue;
-- 
cgit v1.2.3-55-g6feb