diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-21 01:15:42 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-21 01:15:42 +0200 |
commit | 4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f (patch) | |
tree | da52c662b6253597bcaa9a0f5cfa5f2d75c0164d | |
parent | 53b513331add89e83c1327579f3da91ebbe97570 (diff) | |
download | busybox-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.c | 84 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_bash_substring.right | 29 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_bash_substring.tests | 46 |
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 @@ | |||
1 | hush: syntax error: unterminated ${name} | ||
2 | hush: syntax error: unterminated ${name} | ||
3 | hush: syntax error: unterminated ${name} | ||
4 | hush: syntax error: unterminated ${name} | ||
5 | 0123456789 | ||
6 | 1 =|| | ||
7 | 1:1 =|| | ||
8 | 1:1:2=|| | ||
9 | 1::2 =|| | ||
10 | 1 =|0123| | ||
11 | 1:1 =|123| | ||
12 | 1:1:2=|12| | ||
13 | 1::2 =|01| | ||
14 | f =|| | ||
15 | f:1 =|| | ||
16 | f:1:2=|| | ||
17 | f::2 =|| | ||
18 | f =|| | ||
19 | f:1 =|| | ||
20 | f:1:2=|| | ||
21 | f::2 =|| | ||
22 | f =|a| | ||
23 | f:1 =|| | ||
24 | f:1:2=|| | ||
25 | f::2 =|a| | ||
26 | f =|0123456789| | ||
27 | f:1 =|123456789| | ||
28 | f:1:2=|12| | ||
29 | f::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 | |||
3 | export 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}|"' | ||