[git commit master] hush: optional support for ${var:N:M} bashism

Denys Vlasenko vda.linux at googlemail.com
Thu May 20 23:15:42 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
expand_vars_to_list                                 1999    2183    +184
handle_dollar                                        682     623     -59

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 shell/hush.c                                       |   84 +++++++++++---------
 .../hush-vars/param_expand_bash_substring.right    |   29 +++++++
 .../hush-vars/param_expand_bash_substring.tests    |   46 +++++++++++
 3 files changed, 120 insertions(+), 39 deletions(-)
 create mode 100644 shell/hush_test/hush-vars/param_expand_bash_substring.right
 create mode 100755 shell/hush_test/hush-vars/param_expand_bash_substring.tests

diff --git a/shell/hush.c b/shell/hush.c
index 06e9af2..0333395 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -46,7 +46,6 @@
  *      brace expansion: one/{two,three,four}
  *      reserved words: function select
  *      advanced test: [[ ]]
- *      substrings: ${var:1:5}
  *      process substitution: <(list) and >(list)
  *      =~: regex operator
  *      let EXPR [EXPR...]
@@ -2586,7 +2585,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 				var++;
 			} else {
 				/* maybe handle parameter expansion */
-				exp_saveptr = var + strcspn(var, ":-=+?%#");
+				exp_saveptr = var + strcspn(var, "%#:-=+?");
 				exp_save = *exp_saveptr;
 				if (exp_save) {
 					exp_word = exp_saveptr;
@@ -2636,9 +2635,38 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 						else if (loc) /* % or %% and match was found */
 							*loc = '\0';
 					}
-				} else { /* one of :-=+? */
-//TODO: check validity of exp_op. Currently it can be anything.
-//TODO: handle ${VAR:N[:M]} here. N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc
+				} else if (!strchr("%#:-=+?"+3, exp_op)) {
+#if ENABLE_HUSH_BASH_COMPAT
+	/* exp_op is ':' and next char isn't a subst operator.
+	 * Assuming it's ${var:[N][:M]} bashism.
+	 * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc
+	 */
+					char *end;
+					unsigned len = INT_MAX;
+					unsigned beg = 0;
+					end = --exp_word;
+					if (*exp_word != ':') /* not ${var::...} */
+						beg = bb_strtou(exp_word, &end, 0);
+						//bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end);
+					if (*end == ':') {
+						len = bb_strtou(end + 1, &end, 0);
+						//bb_error_msg("len:%u end:'%s'", len, end);
+					}
+					if (*end == '\0'
+					 && end != exp_word /* not "${var:}" */
+					) {
+						//bb_error_msg("from val:'%s'", val);
+						if (!val || beg >= strlen(val))
+							val = "";
+						else
+							val = dyn_val = xstrndup(val + beg, len);
+						//bb_error_msg("val:'%s'", val);
+					} else
+#endif
+					{
+						die_if_script("malformed ${%s...}", var);
+					}
+				} else { /* one of "-=+?" */
 	/* Standard-mandated substitution ops:
 	 * ${var?word} - indicate error if unset
 	 *      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
 				}
 
 				*exp_saveptr = exp_save;
-			}
+			} /* if (exp_op) */
 
 			arg[0] = first_ch;
 #if ENABLE_HUSH_TICK
@@ -5926,14 +5954,15 @@ static int handle_dollar(o_string *as_string,
 		goto make_one_char_var;
 	case '{': {
 		bool first_char, all_digits;
-		int expansion;
+		bool in_expansion_param;
 
 		ch = i_getch(input);
 		nommu_addchr(as_string, ch);
 		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 
-		/* TODO: maybe someone will try to escape the '}' */
-		expansion = 0;
+// TODO: need to handle "a=ab}; echo ${a%\}}"
+// and "a=abc; c=c; echo ${a%${c}}"
+		in_expansion_param = false;
 		first_char = true;
 		all_digits = false;
 		while (1) {
@@ -5957,45 +5986,22 @@ static int handle_dollar(o_string *as_string,
 					goto char_ok;
 			}
 
-			if (expansion < 2
-			 && (  (all_digits && !isdigit(ch))
-			    || (!all_digits && !isalnum(ch) && ch != '_')
+			if (!in_expansion_param
+			 && (  (all_digits && !isdigit(ch)) /* met non-digit: 123w */
+			    || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */
 			    )
 			) {
 				/* handle parameter expansions
 				 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
 				 */
-				if (first_char)
-					goto case_default;
-				switch (ch) {
-				case ':': /* null modifier */
-					if (expansion == 0) {
-						debug_printf_parse(": null modifier\n");
-						++expansion;
-						break;
-					}
-					goto case_default;
-				case '#': /* remove prefix */
-				case '%': /* remove suffix */
-					if (expansion == 0) {
-						debug_printf_parse(": remove suffix/prefix\n");
-						expansion = 2;
-						break;
-					}
-					goto case_default;
-				case '-': /* default value */
-				case '=': /* assign default */
-				case '+': /* alternative */
-				case '?': /* error indicate */
-					debug_printf_parse(": parameter expansion\n");
-					expansion = 2;
-					break;
-				default:
-				case_default:
+				if (first_char /* bad (empty var name): "${%..." */
+				 || !strchr("%#:-=+?", ch) /* bad: "${var<bad_char>..." */
+				) {
 					syntax_error_unterm_str("${name}");
 					debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
 					return 1;
 				}
+				in_expansion_param = true;
 			}
  char_ok:
 			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 0000000..9cd4659
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -0,0 +1,29 @@
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+0123456789
+1    =||
+1:1  =||
+1:1:2=||
+1::2 =||
+1    =|0123|
+1:1  =|123|
+1:1:2=|12|
+1::2 =|01|
+f    =||
+f:1  =||
+f:1:2=||
+f::2 =||
+f    =||
+f:1  =||
+f:1:2=||
+f::2 =||
+f    =|a|
+f:1  =||
+f:1:2=||
+f::2 =|a|
+f    =|0123456789|
+f:1  =|123456789|
+f:1:2=|12|
+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 0000000..6a17655
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -0,0 +1,46 @@
+# do all of these in subshells since it's supposed to error out
+
+export var=0123456789
+
+# first try some invalid patterns
+"$THIS_SH" -c 'echo ${:}'
+"$THIS_SH" -c 'echo ${::}'
+"$THIS_SH" -c 'echo ${:1}'
+"$THIS_SH" -c 'echo ${::1}'
+
+#this also is not valid in bash, but we accept it:
+"$THIS_SH" -c 'echo ${var:}'
+
+# then some funky ones
+# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
+
+# now some valid ones
+"$THIS_SH" -c 'set --; echo "1    =|${1}|"'
+"$THIS_SH" -c 'set --; echo "1:1  =|${1:1}|"'
+"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"'
+"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"'
+
+"$THIS_SH" -c 'set -- 0123; echo "1    =|${1}|"'
+"$THIS_SH" -c 'set -- 0123; echo "1:1  =|${1:1}|"'
+"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"'
+"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"'
+
+"$THIS_SH" -c 'unset f; echo "f    =|$f|"'
+"$THIS_SH" -c 'unset f; echo "f:1  =|${f:1}|"'
+"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"'
+"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"'
+
+"$THIS_SH" -c 'f=; echo "f    =|$f|"'
+"$THIS_SH" -c 'f=; echo "f:1  =|${f:1}|"'
+"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"'
+"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"'
+
+"$THIS_SH" -c 'f=a; echo "f    =|$f|"'
+"$THIS_SH" -c 'f=a; echo "f:1  =|${f:1}|"'
+"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"'
+"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"'
+
+"$THIS_SH" -c 'f=0123456789; echo "f    =|$f|"'
+"$THIS_SH" -c 'f=0123456789; echo "f:1  =|${f:1}|"'
+"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"'
+"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"'
-- 
1.6.3.3



More information about the busybox-cvs mailing list