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 |
