aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-05-22 03:12:29 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-05-22 03:12:29 +0200
commit1e811b12317d0eab4e78d848caa640cca497a0a7 (patch)
treeef60262af7944dfe557542bec9e1fb8bea367b60
parentddc62f64baad8abdeb587b13afe8d47fb347981c (diff)
downloadbusybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.tar.gz
busybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.tar.bz2
busybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.zip
hush: support ${var:EXPR:EXPR}!
function old new delta handle_dollar 574 681 +107 expand_and_evaluate_arith - 77 +77 expand_vars_to_list 2302 2374 +72 add_till_closing_bracket 359 368 +9 builtin_exit 48 47 -1 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/1 up/down: 265/-1) Total: 264 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c130
-rw-r--r--shell/hush_test/hush-vars/param_expand_bash_substring.right10
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_bash_substring.tests98
3 files changed, 152 insertions, 86 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 0f0151a21..41d5fcab2 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -171,6 +171,7 @@
171#define debug_printf_env(...) do {} while (0) 171#define debug_printf_env(...) do {} while (0)
172#define debug_printf_jobs(...) do {} while (0) 172#define debug_printf_jobs(...) do {} while (0)
173#define debug_printf_expand(...) do {} while (0) 173#define debug_printf_expand(...) do {} while (0)
174#define debug_printf_varexp(...) do {} while (0)
174#define debug_printf_glob(...) do {} while (0) 175#define debug_printf_glob(...) do {} while (0)
175#define debug_printf_list(...) do {} while (0) 176#define debug_printf_list(...) do {} while (0)
176#define debug_printf_subst(...) do {} while (0) 177#define debug_printf_subst(...) do {} while (0)
@@ -743,6 +744,10 @@ static const struct built_in_command bltins2[] = {
743# define DEBUG_EXPAND 0 744# define DEBUG_EXPAND 0
744#endif 745#endif
745 746
747#ifndef debug_printf_varexp
748# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__))
749#endif
750
746#ifndef debug_printf_glob 751#ifndef debug_printf_glob
747# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) 752# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__))
748# define DEBUG_GLOB 1 753# define DEBUG_GLOB 1
@@ -1817,11 +1822,11 @@ static void o_addblock(o_string *o, const char *str, int len)
1817 o->data[o->length] = '\0'; 1822 o->data[o->length] = '\0';
1818} 1823}
1819 1824
1820#if !BB_MMU
1821static void o_addstr(o_string *o, const char *str) 1825static void o_addstr(o_string *o, const char *str)
1822{ 1826{
1823 o_addblock(o, str, strlen(str)); 1827 o_addblock(o, str, strlen(str));
1824} 1828}
1829#if !BB_MMU
1825static void nommu_addchr(o_string *o, int ch) 1830static void nommu_addchr(o_string *o, int ch)
1826{ 1831{
1827 if (o) 1832 if (o)
@@ -2597,12 +2602,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2597 } else { 2602 } else {
2598 /* maybe handle parameter expansion */ 2603 /* maybe handle parameter expansion */
2599 exp_saveptr = var + strcspn(var, "%#:-=+?"); 2604 exp_saveptr = var + strcspn(var, "%#:-=+?");
2600 exp_save = *exp_saveptr; 2605 exp_op = exp_save = *exp_saveptr;
2601 if (exp_save) { 2606 if (exp_op) {
2602 exp_word = exp_saveptr; 2607 exp_word = exp_saveptr + 1;
2603 if (exp_save == ':') 2608 if (exp_op == ':') {
2604 exp_word++; 2609 exp_op = *exp_word++;
2605 exp_op = *exp_word++; 2610 if (ENABLE_HUSH_BASH_COMPAT
2611 && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
2612 ) {
2613 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
2614 exp_op = ':';
2615 exp_word--;
2616 }
2617 }
2606 *exp_saveptr = '\0'; 2618 *exp_saveptr = '\0';
2607 } 2619 }
2608 } 2620 }
@@ -2656,39 +2668,42 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2656 *loc = '\0'; 2668 *loc = '\0';
2657 } 2669 }
2658 } 2670 }
2659 } else if (!strchr("%#:-=+?"+3, exp_op)) { 2671 } else if (exp_op == ':') {
2660#if ENABLE_HUSH_BASH_COMPAT 2672#if ENABLE_HUSH_BASH_COMPAT
2661 /* exp_op is ':' and next char isn't a subst operator. 2673 /* It's ${var:N[:M]} bashism.
2662 * Assuming it's ${var:[N][:M]} bashism. 2674 * Note that in encoded form it has TWO parts:
2663 * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc 2675 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
2664 */ 2676 */
2665 char *end; 2677 arith_t beg, len;
2666 unsigned len = INT_MAX; 2678 int errcode = 0;
2667 unsigned beg = 0; 2679
2668 end = --exp_word; 2680 beg = expand_and_evaluate_arith(exp_word, &errcode);
2669 if (*exp_word != ':') /* not ${var::...} */ 2681 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
2670 beg = bb_strtou(exp_word, &end, 0); 2682 *p++ = SPECIAL_VAR_SYMBOL;
2671 //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); 2683 exp_word = p;
2672 if (*end == ':') { 2684 p = strchr(p, SPECIAL_VAR_SYMBOL);
2673 if (end[1] != '\0') /* not ${var:NUM:} */ 2685 *p = '\0';
2674 len = bb_strtou(end + 1, &end, 0); 2686 len = expand_and_evaluate_arith(exp_word, &errcode);
2675 else { 2687 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
2676 len = 0; 2688
2677 end++; 2689 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
2678 } 2690 if (beg < 0) /* bash compat */
2679 //bb_error_msg("len:%u end:'%s'", len, end); 2691 beg = 0;
2680 } 2692 debug_printf_varexp("from val:'%s'\n", val);
2681 if (*end == '\0') {
2682 //bb_error_msg("from val:'%s'", val);
2683 if (len == 0 || !val || beg >= strlen(val)) 2693 if (len == 0 || !val || beg >= strlen(val))
2684 val = ""; 2694 val = "";
2685 else 2695 else {
2696 /* Paranoia. What if user entered 9999999999999
2697 * which fits in arith_t but not int? */
2698 if (len >= INT_MAX)
2699 len = INT_MAX;
2686 val = to_be_freed = xstrndup(val + beg, len); 2700 val = to_be_freed = xstrndup(val + beg, len);
2687 //bb_error_msg("val:'%s'", val); 2701 }
2702 debug_printf_varexp("val:'%s'\n", val);
2688 } else 2703 } else
2689#endif 2704#endif
2690 { 2705 {
2691 die_if_script("malformed ${%s...}", var); 2706 die_if_script("malformed ${%s:...}", var);
2692 val = ""; 2707 val = "";
2693 } 2708 }
2694 } else { /* one of "-=+?" */ 2709 } else { /* one of "-=+?" */
@@ -5891,21 +5906,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
5891 * echo $(echo 'TEST)' BEST) TEST) BEST 5906 * echo $(echo 'TEST)' BEST) TEST) BEST
5892 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST 5907 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5893 * 5908 *
5894 * Also adapted to eat ${var%...} constructs, since ... part 5909 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5895 * can contain arbitrary constructs, just like $(cmd). 5910 * can contain arbitrary constructs, just like $(cmd).
5911 * In bash compat mode, it needs to also be able to stop on '}' or ':'
5912 * for ${var:N[:M]} parsing.
5896 */ 5913 */
5897#define DOUBLE_CLOSE_CHAR_FLAG 0x80 5914#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5898static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch) 5915static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5899{ 5916{
5917 int ch;
5900 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; 5918 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5901 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); 5919#if ENABLE_HUSH_BASH_COMPAT
5920 char end_char2 = end_ch >> 8;
5921#endif
5922 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5923
5902 while (1) { 5924 while (1) {
5903 int ch = i_getch(input); 5925 ch = i_getch(input);
5904 if (ch == EOF) { 5926 if (ch == EOF) {
5905 syntax_error_unterm_ch(end_ch); 5927 syntax_error_unterm_ch(end_ch);
5906 /*xfunc_die(); - redundant */ 5928 /*xfunc_die(); - redundant */
5907 } 5929 }
5908 if (ch == end_ch) { 5930 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
5909 if (!dbl) 5931 if (!dbl)
5910 break; 5932 break;
5911 /* we look for closing )) of $((EXPR)) */ 5933 /* we look for closing )) of $((EXPR)) */
@@ -5947,6 +5969,7 @@ static void add_till_closing_bracket(o_string *dest, struct in_str *input, char
5947 continue; 5969 continue;
5948 } 5970 }
5949 } 5971 }
5972 return ch;
5950} 5973}
5951#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ 5974#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
5952 5975
@@ -6033,22 +6056,45 @@ static int handle_dollar(o_string *as_string,
6033 break; 6056 break;
6034 6057
6035 if (!isalnum(ch) && ch != '_') { 6058 if (!isalnum(ch) && ch != '_') {
6059 unsigned end_ch;
6060 unsigned char last_ch;
6036 /* handle parameter expansions 6061 /* handle parameter expansions
6037 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 6062 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
6038 */ 6063 */
6039 if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ 6064 if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
6040 goto bad_dollar_syntax; 6065 goto bad_dollar_syntax;
6041 /* Eat everything until closing '}' */
6042 o_addchr(dest, ch); 6066 o_addchr(dest, ch);
6067
6068 /* Eat everything until closing '}' (or ':') */
6069 end_ch = '}';
6070 if (ENABLE_HUSH_BASH_COMPAT
6071 && ch == ':'
6072 && !strchr("%#:-=+?"+3, i_peek(input))
6073 ) {
6074 /* It's ${var:N[:M]} thing */
6075 end_ch = '}' * 0x100 + ':';
6076 }
6077 again:
6043 if (!BB_MMU) 6078 if (!BB_MMU)
6044 pos = dest->length; 6079 pos = dest->length;
6045 add_till_closing_bracket(dest, input, '}'); 6080 last_ch = add_till_closing_bracket(dest, input, end_ch);
6046#if !BB_MMU
6047 if (as_string) { 6081 if (as_string) {
6048 o_addstr(as_string, dest->data + pos); 6082 o_addstr(as_string, dest->data + pos);
6049 o_addchr(as_string, '}'); 6083 o_addchr(as_string, last_ch);
6084 }
6085
6086 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
6087 /* close the first block: */
6088 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6089 /* while parsing N from ${var:N[:M]}... */
6090 if ((end_ch & 0xff) == last_ch) {
6091 /* ...got ':' - parse the rest */
6092 end_ch = '}';
6093 goto again;
6094 }
6095 /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
6096 o_addstr(dest, "999999999");
6050 } 6097 }
6051#endif
6052 break; 6098 break;
6053 } 6099 }
6054 } 6100 }
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right
index 6e3eb3ba6..53b8836ff 100644
--- a/shell/hush_test/hush-vars/param_expand_bash_substring.right
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -39,3 +39,13 @@ f:1:2=|12|
39f::2 =|01| 39f::2 =|01|
40f:1: =|| 40f:1: =||
41f:: =|| 41f:: =||
42Substrings with expressions
43f =|01234567|
44f:1+1:2+2 =|2345|
45f:-1:2+2 =|01234567|
46f:1:f =|1234567|
47f:1:$f =|1234567|
48f:1:${f} =|1234567|
49f:1:${f:3:1} =|123|
50f:1:1`echo 1`=|1|
51Done
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
index eedd435ed..a80523add 100755
--- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -1,8 +1,6 @@
1# first try some invalid patterns
1# do all of these in subshells since it's supposed to error out 2# do all of these in subshells since it's supposed to error out
2
3export var=0123456789 3export var=0123456789
4
5# first try some invalid patterns
6"$THIS_SH" -c 'echo ${:}' 4"$THIS_SH" -c 'echo ${:}'
7"$THIS_SH" -c 'echo ${::}' 5"$THIS_SH" -c 'echo ${::}'
8"$THIS_SH" -c 'echo ${:1}' 6"$THIS_SH" -c 'echo ${:1}'
@@ -15,44 +13,56 @@ export var=0123456789
15# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' 13# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
16 14
17# now some valid ones 15# now some valid ones
18"$THIS_SH" -c 'set --; echo "1 =|${1}|"' 16set --; echo "1 =|${1}|"
19"$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"' 17set --; echo "1:1 =|${1:1}|"
20"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"' 18set --; echo "1:1:2=|${1:1:2}|"
21"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"' 19set --; echo "1::2 =|${1::2}|"
22"$THIS_SH" -c 'set --; echo "1:1: =|${1:1:}|"' 20set --; echo "1:1: =|${1:1:}|"
23"$THIS_SH" -c 'set --; echo "1:: =|${1::}|"' 21set --; echo "1:: =|${1::}|"
24 22
25"$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"' 23set -- 0123; echo "1 =|${1}|"
26"$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"' 24set -- 0123; echo "1:1 =|${1:1}|"
27"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"' 25set -- 0123; echo "1:1:2=|${1:1:2}|"
28"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"' 26set -- 0123; echo "1::2 =|${1::2}|"
29"$THIS_SH" -c 'set -- 0123; echo "1:1: =|${1:1:}|"' 27set -- 0123; echo "1:1: =|${1:1:}|"
30"$THIS_SH" -c 'set -- 0123; echo "1:: =|${1::}|"' 28set -- 0123; echo "1:: =|${1::}|"
31 29
32"$THIS_SH" -c 'unset f; echo "f =|$f|"' 30unset f; echo "f =|$f|"
33"$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"' 31unset f; echo "f:1 =|${f:1}|"
34"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"' 32unset f; echo "f:1:2=|${f:1:2}|"
35"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"' 33unset f; echo "f::2 =|${f::2}|"
36"$THIS_SH" -c 'unset f; echo "f:1: =|${f:1:}|"' 34unset f; echo "f:1: =|${f:1:}|"
37"$THIS_SH" -c 'unset f; echo "f:: =|${f::}|"' 35unset f; echo "f:: =|${f::}|"
38 36
39"$THIS_SH" -c 'f=; echo "f =|$f|"' 37f=; echo "f =|$f|"
40"$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"' 38f=; echo "f:1 =|${f:1}|"
41"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"' 39f=; echo "f:1:2=|${f:1:2}|"
42"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"' 40f=; echo "f::2 =|${f::2}|"
43"$THIS_SH" -c 'f=; echo "f:1: =|${f:1:}|"' 41f=; echo "f:1: =|${f:1:}|"
44"$THIS_SH" -c 'f=; echo "f:: =|${f::}|"' 42f=; echo "f:: =|${f::}|"
45 43
46"$THIS_SH" -c 'f=a; echo "f =|$f|"' 44f=a; echo "f =|$f|"
47"$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"' 45f=a; echo "f:1 =|${f:1}|"
48"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"' 46f=a; echo "f:1:2=|${f:1:2}|"
49"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"' 47f=a; echo "f::2 =|${f::2}|"
50"$THIS_SH" -c 'f=a; echo "f:1: =|${f:1:}|"' 48f=a; echo "f:1: =|${f:1:}|"
51"$THIS_SH" -c 'f=a; echo "f:: =|${f::}|"' 49f=a; echo "f:: =|${f::}|"
52 50
53"$THIS_SH" -c 'f=0123456789; echo "f =|$f|"' 51f=0123456789; echo "f =|$f|"
54"$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"' 52f=0123456789; echo "f:1 =|${f:1}|"
55"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"' 53f=0123456789; echo "f:1:2=|${f:1:2}|"
56"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"' 54f=0123456789; echo "f::2 =|${f::2}|"
57"$THIS_SH" -c 'f=0123456789; echo "f:1: =|${f:1:}|"' 55f=0123456789; echo "f:1: =|${f:1:}|"
58"$THIS_SH" -c 'f=0123456789; echo "f:: =|${f::}|"' 56f=0123456789; echo "f:: =|${f::}|"
57
58echo "Substrings with expressions"
59f=01234567; echo 'f '"=|$f|"
60f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|"
61f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|"
62f=01234567; echo 'f:1:f '"=|${f:1:f}|"
63f=01234567; echo 'f:1:$f '"=|${f:1:$f}|"
64f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|"
65f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
66f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
67
68echo Done