aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-07-17 14:21:38 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-07-17 14:21:38 +0200
commitb762c784caa78877a9949224af425e52db237beb (patch)
treef452b87d4abd8d48bb1c711f9330c786b4c3fe6e
parent1fbb73fc4cee44f0768fbb09fe8f41a52d47ed3e (diff)
downloadbusybox-w32-b762c784caa78877a9949224af425e52db237beb.tar.gz
busybox-w32-b762c784caa78877a9949224af425e52db237beb.tar.bz2
busybox-w32-b762c784caa78877a9949224af425e52db237beb.zip
hush: improve ${var#...}, ${var:+...} and ${var/.../...} - handle quoting
dollar_altvalue1 test partially fails: word splitting of unquoted ${var:+...} is not correct function old new delta encode_then_expand_vararg - 443 +443 expand_one_var 1599 1610 +11 parse_stream 2756 2753 -3 encode_string 250 242 -8 setup_heredoc 308 298 -10 expand_and_evaluate_arith 106 96 -10 encode_then_expand_string 142 126 -16 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/5 up/down: 454/-47) Total: 407 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash_test/ash-quoting/dollar_altvalue1.right16
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_altvalue1.tests16
-rw-r--r--shell/ash_test/ash-quoting/dollar_repl_bash1.right14
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_bash1.tests12
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp.right4
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp.tests4
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp2.right2
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp2.tests2
-rwxr-xr-xshell/ash_test/ash-z_slow/many_ifs.tests4
-rw-r--r--shell/hush.c200
-rw-r--r--shell/hush_test/hush-quoting/dollar_altvalue1.right16
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_altvalue1.tests16
-rw-r--r--shell/hush_test/hush-quoting/dollar_repl_bash1.right14
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_repl_bash1.tests12
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp.right4
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp.tests4
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp2.right2
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp2.tests2
-rwxr-xr-xshell/hush_test/hush-z_slow/many_ifs.tests4
19 files changed, 291 insertions, 57 deletions
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue1.right b/shell/ash_test/ash-quoting/dollar_altvalue1.right
new file mode 100644
index 000000000..5cd495d3b
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_altvalue1.right
@@ -0,0 +1,16 @@
1Unquoted b c d
2|b|
3|c|
4|d|
5Unquoted 'b c' d
6|b c|
7|d|
8Unquoted "b c" d
9|b c|
10|d|
11Quoted b c d
12|b c d|
13Quoted 'b c' d
14|'b c' d|
15Quoted "b c" d
16|b c d|
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue1.tests b/shell/ash_test/ash-quoting/dollar_altvalue1.tests
new file mode 100755
index 000000000..f4dc8caec
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_altvalue1.tests
@@ -0,0 +1,16 @@
1f() { for i; do echo "|$i|"; done; }
2x=a
3
4echo Unquoted b c d
5f ${x:+b c d}
6echo Unquoted "'b c' d"
7f ${x:+'b c' d}
8echo Unquoted '"b c" d'
9f ${x:+"b c" d}
10
11echo Quoted b c d
12f "${x:+b c d}"
13echo Quoted "'b c' d"
14f "${x:+'b c' d}"
15echo Quoted '"b c" d'
16f "${x:+"b c" d}"
diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash1.right b/shell/ash_test/ash-quoting/dollar_repl_bash1.right
new file mode 100644
index 000000000..f5e9309f4
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_bash1.right
@@ -0,0 +1,14 @@
1|y|
2|zx|
3|y|
4|zx|
5|y zx|
6|y zx|
7|y|
8|zy|
9|z|
10|y|
11|zy|
12|z|
13|y zy z|
14|y zy z|
diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_bash1.tests
new file mode 100755
index 000000000..912635925
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_bash1.tests
@@ -0,0 +1,12 @@
1f() { for i; do echo "|$i|"; done; }
2v=xx
3
4f ${v/'x'/"y z"}
5f ${v/"x"/'y z'}
6f "${v/'x'/"y z"}"
7f "${v/"x"/'y z'}"
8
9f ${v//'x'/"y z"}
10f ${v//"x"/'y z'}
11f "${v//'x'/"y z"}"
12f "${v//"x"/'y z'}"
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp.right b/shell/ash_test/ash-quoting/squote_in_varexp.right
index a75c0bfd6..4a457021b 100644
--- a/shell/ash_test/ash-quoting/squote_in_varexp.right
+++ b/shell/ash_test/ash-quoting/squote_in_varexp.right
@@ -1,5 +1,9 @@
1z 1z
2z 2z
3z
4z
5y
6y
3y 7y
4y 8y
5Ok:0 9Ok:0
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp.tests b/shell/ash_test/ash-quoting/squote_in_varexp.tests
index a2d05a246..4afc52107 100755
--- a/shell/ash_test/ash-quoting/squote_in_varexp.tests
+++ b/shell/ash_test/ash-quoting/squote_in_varexp.tests
@@ -1,6 +1,10 @@
1x=yz 1x=yz
2echo ${x#'y'} 2echo ${x#'y'}
3echo "${x#'y'}" 3echo "${x#'y'}"
4echo ${x#"y"}
5echo "${x#"y"}"
4echo ${x%'z'} 6echo ${x%'z'}
5echo "${x%'z'}" 7echo "${x%'z'}"
8echo ${x%"z"}
9echo "${x%"z"}"
6echo Ok:$? 10echo Ok:$?
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp2.right b/shell/ash_test/ash-quoting/squote_in_varexp2.right
index 9d0add3c5..d03047024 100644
--- a/shell/ash_test/ash-quoting/squote_in_varexp2.right
+++ b/shell/ash_test/ash-quoting/squote_in_varexp2.right
@@ -1,3 +1,5 @@
1Nothing: 1Nothing:
2Nothing: 2Nothing:
3Nothing:
4Nothing:
3Ok:0 5Ok:0
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp2.tests b/shell/ash_test/ash-quoting/squote_in_varexp2.tests
index 806ad12b9..2797725cc 100755
--- a/shell/ash_test/ash-quoting/squote_in_varexp2.tests
+++ b/shell/ash_test/ash-quoting/squote_in_varexp2.tests
@@ -1,4 +1,6 @@
1x='\\\\' 1x='\\\\'
2printf Nothing:'%s\n' ${x#'\\\\'} 2printf Nothing:'%s\n' ${x#'\\\\'}
3printf Nothing:'%s\n' "${x#'\\\\'}" 3printf Nothing:'%s\n' "${x#'\\\\'}"
4printf Nothing:'%s\n' ${x#"\\\\\\\\"}
5printf Nothing:'%s\n' "${x#"\\\\\\\\"}"
4echo Ok:$? 6echo Ok:$?
diff --git a/shell/ash_test/ash-z_slow/many_ifs.tests b/shell/ash_test/ash-z_slow/many_ifs.tests
index 1f5b1b3a6..cf9a89874 100755
--- a/shell/ash_test/ash-z_slow/many_ifs.tests
+++ b/shell/ash_test/ash-z_slow/many_ifs.tests
@@ -229,8 +229,8 @@ do
229 '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; 229 '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;;
230 ' ') ;; 230 ' ') ;;
231 *) x=$f2$d2$f3$d3 231 *) x=$f2$d2$f3$d3
232 x=${x# } #was x=${x#' '} hush needs fixing for this to work 232 x=${x#' '}
233 x=${x% } #was x=${x%' '} 233 x=${x%' '}
234 split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" 234 split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)"
235 ;; 235 ;;
236 esac 236 esac
diff --git a/shell/hush.c b/shell/hush.c
index 9d3f06db0..415993e71 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -4888,34 +4888,15 @@ static int parse_dollar(o_string *as_string,
4888} 4888}
4889 4889
4890#if BB_MMU 4890#if BB_MMU
4891# if BASH_PATTERN_SUBST 4891#define encode_string(as_string, dest, input, dquote_end) \
4892#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
4893 encode_string(dest, input, dquote_end, process_bkslash)
4894# else
4895/* only ${var/pattern/repl} (its pattern part) needs additional mode */
4896#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
4897 encode_string(dest, input, dquote_end) 4892 encode_string(dest, input, dquote_end)
4898# endif
4899#define as_string NULL 4893#define as_string NULL
4900
4901#else /* !MMU */
4902
4903# if BASH_PATTERN_SUBST
4904/* all parameters are needed, no macro tricks */
4905# else
4906#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
4907 encode_string(as_string, dest, input, dquote_end)
4908# endif
4909#endif 4894#endif
4910static int encode_string(o_string *as_string, 4895static int encode_string(o_string *as_string,
4911 o_string *dest, 4896 o_string *dest,
4912 struct in_str *input, 4897 struct in_str *input,
4913 int dquote_end, 4898 int dquote_end)
4914 int process_bkslash)
4915{ 4899{
4916#if !BASH_PATTERN_SUBST
4917 const int process_bkslash = 1;
4918#endif
4919 int ch; 4900 int ch;
4920 int next; 4901 int next;
4921 4902
@@ -4938,7 +4919,7 @@ static int encode_string(o_string *as_string,
4938 } 4919 }
4939 debug_printf_parse("\" ch=%c (%d) escape=%d\n", 4920 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
4940 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 4921 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
4941 if (process_bkslash && ch == '\\') { 4922 if (ch == '\\') {
4942 if (next == EOF) { 4923 if (next == EOF) {
4943 /* Testcase: in interactive shell a file with 4924 /* Testcase: in interactive shell a file with
4944 * echo "unterminated string\<eof> 4925 * echo "unterminated string\<eof>
@@ -5447,7 +5428,7 @@ static struct pipe *parse_stream(char **pstring,
5447 } 5428 }
5448 if (ctx.is_assignment == NOT_ASSIGNMENT) 5429 if (ctx.is_assignment == NOT_ASSIGNMENT)
5449 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 5430 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
5450 if (!encode_string(&ctx.as_string, &ctx.word, input, '"', /*process_bkslash:*/ 1)) 5431 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
5451 goto parse_error; 5432 goto parse_error;
5452 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; 5433 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
5453 continue; /* get next char */ 5434 continue; /* get next char */
@@ -5744,16 +5725,8 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha
5744 * Returns malloced string. 5725 * Returns malloced string.
5745 * As an optimization, we return NULL if expansion is not needed. 5726 * As an optimization, we return NULL if expansion is not needed.
5746 */ 5727 */
5747#if !BASH_PATTERN_SUBST 5728static char *encode_then_expand_string(const char *str)
5748/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5749#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
5750 encode_then_expand_string(str)
5751#endif
5752static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
5753{ 5729{
5754#if !BASH_PATTERN_SUBST
5755 enum { do_unbackslash = 1 };
5756#endif
5757 char *exp_str; 5730 char *exp_str;
5758 struct in_str input; 5731 struct in_str input;
5759 o_string dest = NULL_O_STRING; 5732 o_string dest = NULL_O_STRING;
@@ -5771,14 +5744,135 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int
5771 * echo $(($a + `echo 1`)) $((1 + $((2)) )) 5744 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
5772 */ 5745 */
5773 setup_string_in_str(&input, str); 5746 setup_string_in_str(&input, str);
5774 encode_string(NULL, &dest, &input, EOF, process_bkslash); 5747 encode_string(NULL, &dest, &input, EOF);
5775//TODO: error check (encode_string returns 0 on error)? 5748//TODO: error check (encode_string returns 0 on error)?
5776 //bb_error_msg("'%s' -> '%s'", str, dest.data); 5749 //bb_error_msg("'%s' -> '%s'", str, dest.data);
5777 exp_str = expand_string_to_string(dest.data, 5750 exp_str = expand_string_to_string(dest.data,
5751 EXP_FLAG_ESC_GLOB_CHARS,
5752 /*unbackslash:*/ 1
5753 );
5754 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
5755 o_free_unsafe(&dest);
5756 return exp_str;
5757}
5758
5759#if !BASH_PATTERN_SUBST
5760#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
5761 encode_then_expand_vararg(str, handle_squotes)
5762#endif
5763static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
5764{
5765#if !BASH_PATTERN_SUBST
5766 const int do_unbackslash = 0;
5767#endif
5768 char *exp_str;
5769 struct in_str input;
5770 o_string dest = NULL_O_STRING;
5771
5772 if (!strchr(str, '$')
5773 && !strchr(str, '\\')
5774 && !strchr(str, '\'')
5775//todo:better code
5776 && !strchr(str, '"')
5777#if ENABLE_HUSH_TICK
5778 && !strchr(str, '`')
5779#endif
5780 ) {
5781 return NULL;
5782 }
5783
5784 /* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
5785 * These can contain single- and double-quoted strings,
5786 * and treated as if the ARG string is initially unquoted. IOW:
5787 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
5788 * a dquoted string: "${var#"zz"}"), the difference only comes later
5789 * (word splitting and globbing of the ${var...} result).
5790 */
5791
5792 setup_string_in_str(&input, str);
5793 o_addchr(&dest, '\0');
5794 dest.length = 0;
5795 exp_str = NULL;
5796
5797 for (;;) {
5798 int ch;
5799 int next;
5800
5801 ch = i_getch(&input);
5802 if (ch == EOF) {
5803 if (dest.o_expflags) { /* EXP_FLAG_ESC_GLOB_CHARS set? */
5804 syntax_error_unterm_ch('"');
5805 goto ret; /* error */
5806 }
5807 break;
5808 }
5809 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
5810 __func__, ch, ch, !!dest.o_expflags);
5811 if (ch == '\'' && handle_squotes && !dest.o_expflags) {
5812//quoting version of add_till_single_quote() (try to merge?):
5813 for (;;) {
5814 ch = i_getch(&input);
5815 if (ch == EOF) {
5816 syntax_error_unterm_ch('\'');
5817 goto ret; /* error */
5818 }
5819 if (ch == '\'')
5820 break;
5821 o_addqchr(&dest, ch);
5822 }
5823 continue;
5824 }
5825 if (ch == '"') {
5826 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
5827 continue;
5828 }
5829 if (ch == '\\') {
5830 ch = i_getch(&input);
5831 if (ch == EOF) {
5832//example? error message? syntax_error_unterm_ch('"');
5833 debug_printf_parse("%s: error: \\<eof>\n", __func__);
5834 goto ret;
5835 }
5836 o_addqchr(&dest, ch);
5837 continue;
5838 }
5839 next = '\0';
5840 if (ch != '\n') {
5841 next = i_peek(&input);
5842 }
5843 if (ch == '$') {
5844 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
5845 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
5846 goto ret;
5847 }
5848 continue;
5849 }
5850#if ENABLE_HUSH_TICK
5851 if (ch == '`') {
5852 //unsigned pos = dest->length;
5853 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
5854 o_addchr(&dest, 0x80 | '`');
5855 if (!add_till_backquote(&dest, &input,
5856 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
5857 )
5858 ) {
5859 goto ret; /* error */
5860 }
5861 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
5862 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5863 continue;
5864 }
5865#endif
5866 o_addQchr(&dest, ch);
5867 } /* for (;;) */
5868
5869 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
5870 exp_str = expand_string_to_string(dest.data,
5778 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, 5871 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
5779 do_unbackslash 5872 do_unbackslash
5780 ); 5873 );
5781 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 5874 ret:
5875 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
5782 o_free_unsafe(&dest); 5876 o_free_unsafe(&dest);
5783 return exp_str; 5877 return exp_str;
5784} 5878}
@@ -5793,7 +5887,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5793 math_state.lookupvar = get_local_var_value; 5887 math_state.lookupvar = get_local_var_value;
5794 math_state.setvar = set_local_var_from_halves; 5888 math_state.setvar = set_local_var_from_halves;
5795 //math_state.endofname = endofname; 5889 //math_state.endofname = endofname;
5796 exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); 5890 exp_str = encode_then_expand_string(arg);
5797 res = arith(&math_state, exp_str ? exp_str : arg); 5891 res = arith(&math_state, exp_str ? exp_str : arg);
5798 free(exp_str); 5892 free(exp_str);
5799 if (errmsg_p) 5893 if (errmsg_p)
@@ -5869,7 +5963,6 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5869 char *to_be_freed; 5963 char *to_be_freed;
5870 char *p; 5964 char *p;
5871 char *var; 5965 char *var;
5872 char first_char;
5873 char exp_op; 5966 char exp_op;
5874 char exp_save = exp_save; /* for compiler */ 5967 char exp_save = exp_save; /* for compiler */
5875 char *exp_saveptr; /* points to expansion operator */ 5968 char *exp_saveptr; /* points to expansion operator */
@@ -5883,10 +5976,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5883 var = arg; 5976 var = arg;
5884 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; 5977 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
5885 arg0 = arg[0]; 5978 arg0 = arg[0];
5886 first_char = arg[0] = arg0 & 0x7f; 5979 arg[0] = (arg0 & 0x7f);
5887 exp_op = 0; 5980 exp_op = 0;
5888 5981
5889 if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ 5982 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
5890 && (!exp_saveptr /* and ( not(${#<op_char>...}) */ 5983 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
5891 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ 5984 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
5892 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ 5985 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
@@ -5897,7 +5990,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5897 } else { 5990 } else {
5898 /* Maybe handle parameter expansion */ 5991 /* Maybe handle parameter expansion */
5899 if (exp_saveptr /* if 2nd char is one of expansion operators */ 5992 if (exp_saveptr /* if 2nd char is one of expansion operators */
5900 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ 5993 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
5901 ) { 5994 ) {
5902 /* ${?:0}, ${#[:]%0} etc */ 5995 /* ${?:0}, ${#[:]%0} etc */
5903 exp_saveptr = var + 1; 5996 exp_saveptr = var + 1;
@@ -5966,6 +6059,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5966 * Word is expanded to produce a glob pattern. 6059 * Word is expanded to produce a glob pattern.
5967 * Then var's value is matched to it and matching part removed. 6060 * Then var's value is matched to it and matching part removed.
5968 */ 6061 */
6062//FIXME: ${x#...${...}...}
6063//should evaluate inner ${...} even if x is "" and no shrinking of it is possible -
6064//inner ${...} may have side effects!
5969 if (val && val[0]) { 6065 if (val && val[0]) {
5970 char *t; 6066 char *t;
5971 char *exp_exp_word; 6067 char *exp_exp_word;
@@ -5974,20 +6070,16 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5974 if (exp_op == *exp_word) /* ## or %% */ 6070 if (exp_op == *exp_word) /* ## or %% */
5975 exp_word++; 6071 exp_word++;
5976 debug_printf_expand("expand: exp_word:'%s'\n", exp_word); 6072 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
5977 /* 6073 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
5978 * process_bkslash:1 unbackslash:1 breaks this:
5979 * a='a\\'; echo ${a%\\\\} # correct output is: a
5980 * process_bkslash:1 unbackslash:0 breaks this:
5981 * a='a}'; echo ${a%\}} # correct output is: a
5982 */
5983 exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0);
5984 if (exp_exp_word) 6074 if (exp_exp_word)
5985 exp_word = exp_exp_word; 6075 exp_word = exp_exp_word;
5986 debug_printf_expand("expand: exp_exp_word:'%s'\n", exp_word); 6076 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
5987 /* HACK ALERT. We depend here on the fact that 6077 /*
6078 * HACK ALERT. We depend here on the fact that
5988 * G.global_argv and results of utoa and get_local_var_value 6079 * G.global_argv and results of utoa and get_local_var_value
5989 * are actually in writable memory: 6080 * are actually in writable memory:
5990 * scan_and_match momentarily stores NULs there. */ 6081 * scan_and_match momentarily stores NULs there.
6082 */
5991 t = (char*)val; 6083 t = (char*)val;
5992 loc = scan_and_match(t, exp_word, scan_flags); 6084 loc = scan_and_match(t, exp_word, scan_flags);
5993 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc); 6085 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
@@ -6020,7 +6112,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
6020 * (note that a*z _pattern_ is never globbed!) 6112 * (note that a*z _pattern_ is never globbed!)
6021 */ 6113 */
6022 char *pattern, *repl, *t; 6114 char *pattern, *repl, *t;
6023 pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0); 6115 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
6024 if (!pattern) 6116 if (!pattern)
6025 pattern = xstrdup(exp_word); 6117 pattern = xstrdup(exp_word);
6026 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); 6118 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
@@ -6028,7 +6120,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
6028 exp_word = p; 6120 exp_word = p;
6029 p = strchr(p, SPECIAL_VAR_SYMBOL); 6121 p = strchr(p, SPECIAL_VAR_SYMBOL);
6030 *p = '\0'; 6122 *p = '\0';
6031 repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 1); 6123 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
6032 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); 6124 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
6033 /* HACK ALERT. We depend here on the fact that 6125 /* HACK ALERT. We depend here on the fact that
6034 * G.global_argv and results of utoa and get_local_var_value 6126 * G.global_argv and results of utoa and get_local_var_value
@@ -6131,7 +6223,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
6131 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, 6223 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
6132 (exp_save == ':') ? "true" : "false", use_word); 6224 (exp_save == ':') ? "true" : "false", use_word);
6133 if (use_word) { 6225 if (use_word) {
6134 to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); 6226//FIXME: unquoted ${x:+"b c" d} and ${x:+'b c' d} should expand to two words
6227//currently it expands to three.
6228 to_be_freed = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ !(arg0 & 0x80), /*unbackslash:*/ 0);
6135 if (to_be_freed) 6229 if (to_be_freed)
6136 exp_word = to_be_freed; 6230 exp_word = to_be_freed;
6137 if (exp_op == '?') { 6231 if (exp_op == '?') {
@@ -6934,7 +7028,7 @@ static void setup_heredoc(struct redir_struct *redir)
6934 7028
6935 expanded = NULL; 7029 expanded = NULL;
6936 if (!(redir->rd_dup & HEREDOC_QUOTED)) { 7030 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
6937 expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); 7031 expanded = encode_then_expand_string(heredoc);
6938 if (expanded) 7032 if (expanded)
6939 heredoc = expanded; 7033 heredoc = expanded;
6940 } 7034 }
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue1.right b/shell/hush_test/hush-quoting/dollar_altvalue1.right
new file mode 100644
index 000000000..5cd495d3b
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_altvalue1.right
@@ -0,0 +1,16 @@
1Unquoted b c d
2|b|
3|c|
4|d|
5Unquoted 'b c' d
6|b c|
7|d|
8Unquoted "b c" d
9|b c|
10|d|
11Quoted b c d
12|b c d|
13Quoted 'b c' d
14|'b c' d|
15Quoted "b c" d
16|b c d|
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue1.tests b/shell/hush_test/hush-quoting/dollar_altvalue1.tests
new file mode 100755
index 000000000..f4dc8caec
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_altvalue1.tests
@@ -0,0 +1,16 @@
1f() { for i; do echo "|$i|"; done; }
2x=a
3
4echo Unquoted b c d
5f ${x:+b c d}
6echo Unquoted "'b c' d"
7f ${x:+'b c' d}
8echo Unquoted '"b c" d'
9f ${x:+"b c" d}
10
11echo Quoted b c d
12f "${x:+b c d}"
13echo Quoted "'b c' d"
14f "${x:+'b c' d}"
15echo Quoted '"b c" d'
16f "${x:+"b c" d}"
diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash1.right b/shell/hush_test/hush-quoting/dollar_repl_bash1.right
new file mode 100644
index 000000000..f5e9309f4
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_bash1.right
@@ -0,0 +1,14 @@
1|y|
2|zx|
3|y|
4|zx|
5|y zx|
6|y zx|
7|y|
8|zy|
9|z|
10|y|
11|zy|
12|z|
13|y zy z|
14|y zy z|
diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash1.tests b/shell/hush_test/hush-quoting/dollar_repl_bash1.tests
new file mode 100755
index 000000000..912635925
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_bash1.tests
@@ -0,0 +1,12 @@
1f() { for i; do echo "|$i|"; done; }
2v=xx
3
4f ${v/'x'/"y z"}
5f ${v/"x"/'y z'}
6f "${v/'x'/"y z"}"
7f "${v/"x"/'y z'}"
8
9f ${v//'x'/"y z"}
10f ${v//"x"/'y z'}
11f "${v//'x'/"y z"}"
12f "${v//"x"/'y z'}"
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp.right b/shell/hush_test/hush-quoting/squote_in_varexp.right
index a75c0bfd6..4a457021b 100644
--- a/shell/hush_test/hush-quoting/squote_in_varexp.right
+++ b/shell/hush_test/hush-quoting/squote_in_varexp.right
@@ -1,5 +1,9 @@
1z 1z
2z 2z
3z
4z
5y
6y
3y 7y
4y 8y
5Ok:0 9Ok:0
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp.tests b/shell/hush_test/hush-quoting/squote_in_varexp.tests
index a2d05a246..4afc52107 100755
--- a/shell/hush_test/hush-quoting/squote_in_varexp.tests
+++ b/shell/hush_test/hush-quoting/squote_in_varexp.tests
@@ -1,6 +1,10 @@
1x=yz 1x=yz
2echo ${x#'y'} 2echo ${x#'y'}
3echo "${x#'y'}" 3echo "${x#'y'}"
4echo ${x#"y"}
5echo "${x#"y"}"
4echo ${x%'z'} 6echo ${x%'z'}
5echo "${x%'z'}" 7echo "${x%'z'}"
8echo ${x%"z"}
9echo "${x%"z"}"
6echo Ok:$? 10echo Ok:$?
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp2.right b/shell/hush_test/hush-quoting/squote_in_varexp2.right
index 9d0add3c5..d03047024 100644
--- a/shell/hush_test/hush-quoting/squote_in_varexp2.right
+++ b/shell/hush_test/hush-quoting/squote_in_varexp2.right
@@ -1,3 +1,5 @@
1Nothing: 1Nothing:
2Nothing: 2Nothing:
3Nothing:
4Nothing:
3Ok:0 5Ok:0
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp2.tests b/shell/hush_test/hush-quoting/squote_in_varexp2.tests
index 806ad12b9..2797725cc 100755
--- a/shell/hush_test/hush-quoting/squote_in_varexp2.tests
+++ b/shell/hush_test/hush-quoting/squote_in_varexp2.tests
@@ -1,4 +1,6 @@
1x='\\\\' 1x='\\\\'
2printf Nothing:'%s\n' ${x#'\\\\'} 2printf Nothing:'%s\n' ${x#'\\\\'}
3printf Nothing:'%s\n' "${x#'\\\\'}" 3printf Nothing:'%s\n' "${x#'\\\\'}"
4printf Nothing:'%s\n' ${x#"\\\\\\\\"}
5printf Nothing:'%s\n' "${x#"\\\\\\\\"}"
4echo Ok:$? 6echo Ok:$?
diff --git a/shell/hush_test/hush-z_slow/many_ifs.tests b/shell/hush_test/hush-z_slow/many_ifs.tests
index 1f5b1b3a6..cf9a89874 100755
--- a/shell/hush_test/hush-z_slow/many_ifs.tests
+++ b/shell/hush_test/hush-z_slow/many_ifs.tests
@@ -229,8 +229,8 @@ do
229 '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; 229 '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;;
230 ' ') ;; 230 ' ') ;;
231 *) x=$f2$d2$f3$d3 231 *) x=$f2$d2$f3$d3
232 x=${x# } #was x=${x#' '} hush needs fixing for this to work 232 x=${x#' '}
233 x=${x% } #was x=${x%' '} 233 x=${x%' '}
234 split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" 234 split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)"
235 ;; 235 ;;
236 esac 236 esac