diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-07-17 14:21:38 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-07-17 14:21:38 +0200 |
commit | b762c784caa78877a9949224af425e52db237beb (patch) | |
tree | f452b87d4abd8d48bb1c711f9330c786b4c3fe6e | |
parent | 1fbb73fc4cee44f0768fbb09fe8f41a52d47ed3e (diff) | |
download | busybox-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>
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 @@ | |||
1 | Unquoted b c d | ||
2 | |b| | ||
3 | |c| | ||
4 | |d| | ||
5 | Unquoted 'b c' d | ||
6 | |b c| | ||
7 | |d| | ||
8 | Unquoted "b c" d | ||
9 | |b c| | ||
10 | |d| | ||
11 | Quoted b c d | ||
12 | |b c d| | ||
13 | Quoted 'b c' d | ||
14 | |'b c' d| | ||
15 | Quoted "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 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | x=a | ||
3 | |||
4 | echo Unquoted b c d | ||
5 | f ${x:+b c d} | ||
6 | echo Unquoted "'b c' d" | ||
7 | f ${x:+'b c' d} | ||
8 | echo Unquoted '"b c" d' | ||
9 | f ${x:+"b c" d} | ||
10 | |||
11 | echo Quoted b c d | ||
12 | f "${x:+b c d}" | ||
13 | echo Quoted "'b c' d" | ||
14 | f "${x:+'b c' d}" | ||
15 | echo Quoted '"b c" d' | ||
16 | f "${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 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | v=xx | ||
3 | |||
4 | f ${v/'x'/"y z"} | ||
5 | f ${v/"x"/'y z'} | ||
6 | f "${v/'x'/"y z"}" | ||
7 | f "${v/"x"/'y z'}" | ||
8 | |||
9 | f ${v//'x'/"y z"} | ||
10 | f ${v//"x"/'y z'} | ||
11 | f "${v//'x'/"y z"}" | ||
12 | f "${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 @@ | |||
1 | z | 1 | z |
2 | z | 2 | z |
3 | z | ||
4 | z | ||
5 | y | ||
6 | y | ||
3 | y | 7 | y |
4 | y | 8 | y |
5 | Ok:0 | 9 | Ok: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 @@ | |||
1 | x=yz | 1 | x=yz |
2 | echo ${x#'y'} | 2 | echo ${x#'y'} |
3 | echo "${x#'y'}" | 3 | echo "${x#'y'}" |
4 | echo ${x#"y"} | ||
5 | echo "${x#"y"}" | ||
4 | echo ${x%'z'} | 6 | echo ${x%'z'} |
5 | echo "${x%'z'}" | 7 | echo "${x%'z'}" |
8 | echo ${x%"z"} | ||
9 | echo "${x%"z"}" | ||
6 | echo Ok:$? | 10 | echo 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 @@ | |||
1 | Nothing: | 1 | Nothing: |
2 | Nothing: | 2 | Nothing: |
3 | Nothing: | ||
4 | Nothing: | ||
3 | Ok:0 | 5 | Ok: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 @@ | |||
1 | x='\\\\' | 1 | x='\\\\' |
2 | printf Nothing:'%s\n' ${x#'\\\\'} | 2 | printf Nothing:'%s\n' ${x#'\\\\'} |
3 | printf Nothing:'%s\n' "${x#'\\\\'}" | 3 | printf Nothing:'%s\n' "${x#'\\\\'}" |
4 | printf Nothing:'%s\n' ${x#"\\\\\\\\"} | ||
5 | printf Nothing:'%s\n' "${x#"\\\\\\\\"}" | ||
4 | echo Ok:$? | 6 | echo 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 |
4910 | static int encode_string(o_string *as_string, | 4895 | static 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 | 5728 | static 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 | ||
5752 | static 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 | ||
5763 | static 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 @@ | |||
1 | Unquoted b c d | ||
2 | |b| | ||
3 | |c| | ||
4 | |d| | ||
5 | Unquoted 'b c' d | ||
6 | |b c| | ||
7 | |d| | ||
8 | Unquoted "b c" d | ||
9 | |b c| | ||
10 | |d| | ||
11 | Quoted b c d | ||
12 | |b c d| | ||
13 | Quoted 'b c' d | ||
14 | |'b c' d| | ||
15 | Quoted "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 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | x=a | ||
3 | |||
4 | echo Unquoted b c d | ||
5 | f ${x:+b c d} | ||
6 | echo Unquoted "'b c' d" | ||
7 | f ${x:+'b c' d} | ||
8 | echo Unquoted '"b c" d' | ||
9 | f ${x:+"b c" d} | ||
10 | |||
11 | echo Quoted b c d | ||
12 | f "${x:+b c d}" | ||
13 | echo Quoted "'b c' d" | ||
14 | f "${x:+'b c' d}" | ||
15 | echo Quoted '"b c" d' | ||
16 | f "${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 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | v=xx | ||
3 | |||
4 | f ${v/'x'/"y z"} | ||
5 | f ${v/"x"/'y z'} | ||
6 | f "${v/'x'/"y z"}" | ||
7 | f "${v/"x"/'y z'}" | ||
8 | |||
9 | f ${v//'x'/"y z"} | ||
10 | f ${v//"x"/'y z'} | ||
11 | f "${v//'x'/"y z"}" | ||
12 | f "${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 @@ | |||
1 | z | 1 | z |
2 | z | 2 | z |
3 | z | ||
4 | z | ||
5 | y | ||
6 | y | ||
3 | y | 7 | y |
4 | y | 8 | y |
5 | Ok:0 | 9 | Ok: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 @@ | |||
1 | x=yz | 1 | x=yz |
2 | echo ${x#'y'} | 2 | echo ${x#'y'} |
3 | echo "${x#'y'}" | 3 | echo "${x#'y'}" |
4 | echo ${x#"y"} | ||
5 | echo "${x#"y"}" | ||
4 | echo ${x%'z'} | 6 | echo ${x%'z'} |
5 | echo "${x%'z'}" | 7 | echo "${x%'z'}" |
8 | echo ${x%"z"} | ||
9 | echo "${x%"z"}" | ||
6 | echo Ok:$? | 10 | echo 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 @@ | |||
1 | Nothing: | 1 | Nothing: |
2 | Nothing: | 2 | Nothing: |
3 | Nothing: | ||
4 | Nothing: | ||
3 | Ok:0 | 5 | Ok: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 @@ | |||
1 | x='\\\\' | 1 | x='\\\\' |
2 | printf Nothing:'%s\n' ${x#'\\\\'} | 2 | printf Nothing:'%s\n' ${x#'\\\\'} |
3 | printf Nothing:'%s\n' "${x#'\\\\'}" | 3 | printf Nothing:'%s\n' "${x#'\\\\'}" |
4 | printf Nothing:'%s\n' ${x#"\\\\\\\\"} | ||
5 | printf Nothing:'%s\n' "${x#"\\\\\\\\"}" | ||
4 | echo Ok:$? | 6 | echo 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 |