aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-05-21 01:15:42 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-05-21 01:15:42 +0200
commit4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f (patch)
treeda52c662b6253597bcaa9a0f5cfa5f2d75c0164d
parent53b513331add89e83c1327579f3da91ebbe97570 (diff)
downloadbusybox-w32-4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f.tar.gz
busybox-w32-4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f.tar.bz2
busybox-w32-4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f.zip
hush: optional support for ${var:N:M} bashism
function old new delta expand_vars_to_list 1999 2183 +184 handle_dollar 682 623 -59 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c84
-rw-r--r--shell/hush_test/hush-vars/param_expand_bash_substring.right29
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_bash_substring.tests46
3 files changed, 120 insertions, 39 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 06e9af21a..0333395f8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -46,7 +46,6 @@
46 * brace expansion: one/{two,three,four} 46 * brace expansion: one/{two,three,four}
47 * reserved words: function select 47 * reserved words: function select
48 * advanced test: [[ ]] 48 * advanced test: [[ ]]
49 * substrings: ${var:1:5}
50 * process substitution: <(list) and >(list) 49 * process substitution: <(list) and >(list)
51 * =~: regex operator 50 * =~: regex operator
52 * let EXPR [EXPR...] 51 * let EXPR [EXPR...]
@@ -2586,7 +2585,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2586 var++; 2585 var++;
2587 } else { 2586 } else {
2588 /* maybe handle parameter expansion */ 2587 /* maybe handle parameter expansion */
2589 exp_saveptr = var + strcspn(var, ":-=+?%#"); 2588 exp_saveptr = var + strcspn(var, "%#:-=+?");
2590 exp_save = *exp_saveptr; 2589 exp_save = *exp_saveptr;
2591 if (exp_save) { 2590 if (exp_save) {
2592 exp_word = exp_saveptr; 2591 exp_word = exp_saveptr;
@@ -2636,9 +2635,38 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2636 else if (loc) /* % or %% and match was found */ 2635 else if (loc) /* % or %% and match was found */
2637 *loc = '\0'; 2636 *loc = '\0';
2638 } 2637 }
2639 } else { /* one of :-=+? */ 2638 } else if (!strchr("%#:-=+?"+3, exp_op)) {
2640//TODO: check validity of exp_op. Currently it can be anything. 2639#if ENABLE_HUSH_BASH_COMPAT
2641//TODO: handle ${VAR:N[:M]} here. N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc 2640 /* exp_op is ':' and next char isn't a subst operator.
2641 * Assuming it's ${var:[N][:M]} bashism.
2642 * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc
2643 */
2644 char *end;
2645 unsigned len = INT_MAX;
2646 unsigned beg = 0;
2647 end = --exp_word;
2648 if (*exp_word != ':') /* not ${var::...} */
2649 beg = bb_strtou(exp_word, &end, 0);
2650 //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end);
2651 if (*end == ':') {
2652 len = bb_strtou(end + 1, &end, 0);
2653 //bb_error_msg("len:%u end:'%s'", len, end);
2654 }
2655 if (*end == '\0'
2656 && end != exp_word /* not "${var:}" */
2657 ) {
2658 //bb_error_msg("from val:'%s'", val);
2659 if (!val || beg >= strlen(val))
2660 val = "";
2661 else
2662 val = dyn_val = xstrndup(val + beg, len);
2663 //bb_error_msg("val:'%s'", val);
2664 } else
2665#endif
2666 {
2667 die_if_script("malformed ${%s...}", var);
2668 }
2669 } else { /* one of "-=+?" */
2642 /* Standard-mandated substitution ops: 2670 /* Standard-mandated substitution ops:
2643 * ${var?word} - indicate error if unset 2671 * ${var?word} - indicate error if unset
2644 * If var is unset, word (or a message indicating it is unset 2672 * If var is unset, word (or a message indicating it is unset
@@ -2693,7 +2721,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2693 } 2721 }
2694 2722
2695 *exp_saveptr = exp_save; 2723 *exp_saveptr = exp_save;
2696 } 2724 } /* if (exp_op) */
2697 2725
2698 arg[0] = first_ch; 2726 arg[0] = first_ch;
2699#if ENABLE_HUSH_TICK 2727#if ENABLE_HUSH_TICK
@@ -5926,14 +5954,15 @@ static int handle_dollar(o_string *as_string,
5926 goto make_one_char_var; 5954 goto make_one_char_var;
5927 case '{': { 5955 case '{': {
5928 bool first_char, all_digits; 5956 bool first_char, all_digits;
5929 int expansion; 5957 bool in_expansion_param;
5930 5958
5931 ch = i_getch(input); 5959 ch = i_getch(input);
5932 nommu_addchr(as_string, ch); 5960 nommu_addchr(as_string, ch);
5933 o_addchr(dest, SPECIAL_VAR_SYMBOL); 5961 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5934 5962
5935 /* TODO: maybe someone will try to escape the '}' */ 5963// TODO: need to handle "a=ab}; echo ${a%\}}"
5936 expansion = 0; 5964// and "a=abc; c=c; echo ${a%${c}}"
5965 in_expansion_param = false;
5937 first_char = true; 5966 first_char = true;
5938 all_digits = false; 5967 all_digits = false;
5939 while (1) { 5968 while (1) {
@@ -5957,45 +5986,22 @@ static int handle_dollar(o_string *as_string,
5957 goto char_ok; 5986 goto char_ok;
5958 } 5987 }
5959 5988
5960 if (expansion < 2 5989 if (!in_expansion_param
5961 && ( (all_digits && !isdigit(ch)) 5990 && ( (all_digits && !isdigit(ch)) /* met non-digit: 123w */
5962 || (!all_digits && !isalnum(ch) && ch != '_') 5991 || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */
5963 ) 5992 )
5964 ) { 5993 ) {
5965 /* handle parameter expansions 5994 /* handle parameter expansions
5966 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 5995 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5967 */ 5996 */
5968 if (first_char) 5997 if (first_char /* bad (empty var name): "${%..." */
5969 goto case_default; 5998 || !strchr("%#:-=+?", ch) /* bad: "${var<bad_char>..." */
5970 switch (ch) { 5999 ) {
5971 case ':': /* null modifier */
5972 if (expansion == 0) {
5973 debug_printf_parse(": null modifier\n");
5974 ++expansion;
5975 break;
5976 }
5977 goto case_default;
5978 case '#': /* remove prefix */
5979 case '%': /* remove suffix */
5980 if (expansion == 0) {
5981 debug_printf_parse(": remove suffix/prefix\n");
5982 expansion = 2;
5983 break;
5984 }
5985 goto case_default;
5986 case '-': /* default value */
5987 case '=': /* assign default */
5988 case '+': /* alternative */
5989 case '?': /* error indicate */
5990 debug_printf_parse(": parameter expansion\n");
5991 expansion = 2;
5992 break;
5993 default:
5994 case_default:
5995 syntax_error_unterm_str("${name}"); 6000 syntax_error_unterm_str("${name}");
5996 debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); 6001 debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
5997 return 1; 6002 return 1;
5998 } 6003 }
6004 in_expansion_param = true;
5999 } 6005 }
6000 char_ok: 6006 char_ok:
6001 debug_printf_parse(": '%c'\n", ch); 6007 debug_printf_parse(": '%c'\n", ch);
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right
new file mode 100644
index 000000000..9cd465938
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -0,0 +1,29 @@
1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name}
3hush: syntax error: unterminated ${name}
4hush: syntax error: unterminated ${name}
50123456789
61 =||
71:1 =||
81:1:2=||
91::2 =||
101 =|0123|
111:1 =|123|
121:1:2=|12|
131::2 =|01|
14f =||
15f:1 =||
16f:1:2=||
17f::2 =||
18f =||
19f:1 =||
20f:1:2=||
21f::2 =||
22f =|a|
23f:1 =||
24f:1:2=||
25f::2 =|a|
26f =|0123456789|
27f:1 =|123456789|
28f:1:2=|12|
29f::2 =|01|
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
new file mode 100755
index 000000000..6a1765559
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -0,0 +1,46 @@
1# do all of these in subshells since it's supposed to error out
2
3export var=0123456789
4
5# first try some invalid patterns
6"$THIS_SH" -c 'echo ${:}'
7"$THIS_SH" -c 'echo ${::}'
8"$THIS_SH" -c 'echo ${:1}'
9"$THIS_SH" -c 'echo ${::1}'
10
11#this also is not valid in bash, but we accept it:
12"$THIS_SH" -c 'echo ${var:}'
13
14# then some funky ones
15# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
16
17# now some valid ones
18"$THIS_SH" -c 'set --; echo "1 =|${1}|"'
19"$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"'
20"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"'
21"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"'
22
23"$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"'
24"$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"'
25"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"'
26"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"'
27
28"$THIS_SH" -c 'unset f; echo "f =|$f|"'
29"$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"'
30"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"'
31"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"'
32
33"$THIS_SH" -c 'f=; echo "f =|$f|"'
34"$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"'
35"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"'
36"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"'
37
38"$THIS_SH" -c 'f=a; echo "f =|$f|"'
39"$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"'
40"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"'
41"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"'
42
43"$THIS_SH" -c 'f=0123456789; echo "f =|$f|"'
44"$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"'
45"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"'
46"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"'