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>
This commit is contained in:
130
shell/hush.c
130
shell/hush.c
@@ -171,6 +171,7 @@
|
|||||||
#define debug_printf_env(...) do {} while (0)
|
#define debug_printf_env(...) do {} while (0)
|
||||||
#define debug_printf_jobs(...) do {} while (0)
|
#define debug_printf_jobs(...) do {} while (0)
|
||||||
#define debug_printf_expand(...) do {} while (0)
|
#define debug_printf_expand(...) do {} while (0)
|
||||||
|
#define debug_printf_varexp(...) do {} while (0)
|
||||||
#define debug_printf_glob(...) do {} while (0)
|
#define debug_printf_glob(...) do {} while (0)
|
||||||
#define debug_printf_list(...) do {} while (0)
|
#define debug_printf_list(...) do {} while (0)
|
||||||
#define debug_printf_subst(...) do {} while (0)
|
#define debug_printf_subst(...) do {} while (0)
|
||||||
@@ -743,6 +744,10 @@ static const struct built_in_command bltins2[] = {
|
|||||||
# define DEBUG_EXPAND 0
|
# define DEBUG_EXPAND 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_printf_varexp
|
||||||
|
# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__))
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef debug_printf_glob
|
#ifndef debug_printf_glob
|
||||||
# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__))
|
# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__))
|
||||||
# define DEBUG_GLOB 1
|
# define DEBUG_GLOB 1
|
||||||
@@ -1817,11 +1822,11 @@ static void o_addblock(o_string *o, const char *str, int len)
|
|||||||
o->data[o->length] = '\0';
|
o->data[o->length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !BB_MMU
|
|
||||||
static void o_addstr(o_string *o, const char *str)
|
static void o_addstr(o_string *o, const char *str)
|
||||||
{
|
{
|
||||||
o_addblock(o, str, strlen(str));
|
o_addblock(o, str, strlen(str));
|
||||||
}
|
}
|
||||||
|
#if !BB_MMU
|
||||||
static void nommu_addchr(o_string *o, int ch)
|
static void nommu_addchr(o_string *o, int ch)
|
||||||
{
|
{
|
||||||
if (o)
|
if (o)
|
||||||
@@ -2597,12 +2602,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
} else {
|
} else {
|
||||||
/* maybe handle parameter expansion */
|
/* maybe handle parameter expansion */
|
||||||
exp_saveptr = var + strcspn(var, "%#:-=+?");
|
exp_saveptr = var + strcspn(var, "%#:-=+?");
|
||||||
exp_save = *exp_saveptr;
|
exp_op = exp_save = *exp_saveptr;
|
||||||
if (exp_save) {
|
if (exp_op) {
|
||||||
exp_word = exp_saveptr;
|
exp_word = exp_saveptr + 1;
|
||||||
if (exp_save == ':')
|
if (exp_op == ':') {
|
||||||
exp_word++;
|
exp_op = *exp_word++;
|
||||||
exp_op = *exp_word++;
|
if (ENABLE_HUSH_BASH_COMPAT
|
||||||
|
&& (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
|
||||||
|
) {
|
||||||
|
/* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
|
||||||
|
exp_op = ':';
|
||||||
|
exp_word--;
|
||||||
|
}
|
||||||
|
}
|
||||||
*exp_saveptr = '\0';
|
*exp_saveptr = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2656,39 +2668,42 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
*loc = '\0';
|
*loc = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!strchr("%#:-=+?"+3, exp_op)) {
|
} else if (exp_op == ':') {
|
||||||
#if ENABLE_HUSH_BASH_COMPAT
|
#if ENABLE_HUSH_BASH_COMPAT
|
||||||
/* exp_op is ':' and next char isn't a subst operator.
|
/* It's ${var:N[:M]} bashism.
|
||||||
* Assuming it's ${var:[N][:M]} bashism.
|
* Note that in encoded form it has TWO parts:
|
||||||
* TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc
|
* var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
|
||||||
*/
|
*/
|
||||||
char *end;
|
arith_t beg, len;
|
||||||
unsigned len = INT_MAX;
|
int errcode = 0;
|
||||||
unsigned beg = 0;
|
|
||||||
end = --exp_word;
|
beg = expand_and_evaluate_arith(exp_word, &errcode);
|
||||||
if (*exp_word != ':') /* not ${var::...} */
|
debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
|
||||||
beg = bb_strtou(exp_word, &end, 0);
|
*p++ = SPECIAL_VAR_SYMBOL;
|
||||||
//bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end);
|
exp_word = p;
|
||||||
if (*end == ':') {
|
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
||||||
if (end[1] != '\0') /* not ${var:NUM:} */
|
*p = '\0';
|
||||||
len = bb_strtou(end + 1, &end, 0);
|
len = expand_and_evaluate_arith(exp_word, &errcode);
|
||||||
else {
|
debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
|
||||||
len = 0;
|
|
||||||
end++;
|
if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
|
||||||
}
|
if (beg < 0) /* bash compat */
|
||||||
//bb_error_msg("len:%u end:'%s'", len, end);
|
beg = 0;
|
||||||
}
|
debug_printf_varexp("from val:'%s'\n", val);
|
||||||
if (*end == '\0') {
|
|
||||||
//bb_error_msg("from val:'%s'", val);
|
|
||||||
if (len == 0 || !val || beg >= strlen(val))
|
if (len == 0 || !val || beg >= strlen(val))
|
||||||
val = "";
|
val = "";
|
||||||
else
|
else {
|
||||||
|
/* Paranoia. What if user entered 9999999999999
|
||||||
|
* which fits in arith_t but not int? */
|
||||||
|
if (len >= INT_MAX)
|
||||||
|
len = INT_MAX;
|
||||||
val = to_be_freed = xstrndup(val + beg, len);
|
val = to_be_freed = xstrndup(val + beg, len);
|
||||||
//bb_error_msg("val:'%s'", val);
|
}
|
||||||
|
debug_printf_varexp("val:'%s'\n", val);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
die_if_script("malformed ${%s...}", var);
|
die_if_script("malformed ${%s:...}", var);
|
||||||
val = "";
|
val = "";
|
||||||
}
|
}
|
||||||
} else { /* one of "-=+?" */
|
} else { /* one of "-=+?" */
|
||||||
@@ -5891,21 +5906,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
|
|||||||
* echo $(echo 'TEST)' BEST) TEST) BEST
|
* echo $(echo 'TEST)' BEST) TEST) BEST
|
||||||
* echo $(echo \(\(TEST\) BEST) ((TEST) BEST
|
* echo $(echo \(\(TEST\) BEST) ((TEST) BEST
|
||||||
*
|
*
|
||||||
* Also adapted to eat ${var%...} constructs, since ... part
|
* Also adapted to eat ${var%...} and $((...)) constructs, since ... part
|
||||||
* can contain arbitrary constructs, just like $(cmd).
|
* can contain arbitrary constructs, just like $(cmd).
|
||||||
|
* In bash compat mode, it needs to also be able to stop on '}' or ':'
|
||||||
|
* for ${var:N[:M]} parsing.
|
||||||
*/
|
*/
|
||||||
#define DOUBLE_CLOSE_CHAR_FLAG 0x80
|
#define DOUBLE_CLOSE_CHAR_FLAG 0x80
|
||||||
static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch)
|
static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
|
||||||
{
|
{
|
||||||
|
int ch;
|
||||||
char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
|
char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
|
||||||
end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1);
|
#if ENABLE_HUSH_BASH_COMPAT
|
||||||
|
char end_char2 = end_ch >> 8;
|
||||||
|
#endif
|
||||||
|
end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax_error_unterm_ch(end_ch);
|
syntax_error_unterm_ch(end_ch);
|
||||||
/*xfunc_die(); - redundant */
|
/*xfunc_die(); - redundant */
|
||||||
}
|
}
|
||||||
if (ch == end_ch) {
|
if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
|
||||||
if (!dbl)
|
if (!dbl)
|
||||||
break;
|
break;
|
||||||
/* we look for closing )) of $((EXPR)) */
|
/* we look for closing )) of $((EXPR)) */
|
||||||
@@ -5947,6 +5969,7 @@ static void add_till_closing_bracket(o_string *dest, struct in_str *input, char
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ch;
|
||||||
}
|
}
|
||||||
#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
|
#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
|
||||||
|
|
||||||
@@ -6033,22 +6056,45 @@ static int handle_dollar(o_string *as_string,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (!isalnum(ch) && ch != '_') {
|
if (!isalnum(ch) && ch != '_') {
|
||||||
|
unsigned end_ch;
|
||||||
|
unsigned char last_ch;
|
||||||
/* handle parameter expansions
|
/* handle parameter expansions
|
||||||
* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
|
* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
|
||||||
*/
|
*/
|
||||||
if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
|
if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
|
||||||
goto bad_dollar_syntax;
|
goto bad_dollar_syntax;
|
||||||
/* Eat everything until closing '}' */
|
|
||||||
o_addchr(dest, ch);
|
o_addchr(dest, ch);
|
||||||
|
|
||||||
|
/* Eat everything until closing '}' (or ':') */
|
||||||
|
end_ch = '}';
|
||||||
|
if (ENABLE_HUSH_BASH_COMPAT
|
||||||
|
&& ch == ':'
|
||||||
|
&& !strchr("%#:-=+?"+3, i_peek(input))
|
||||||
|
) {
|
||||||
|
/* It's ${var:N[:M]} thing */
|
||||||
|
end_ch = '}' * 0x100 + ':';
|
||||||
|
}
|
||||||
|
again:
|
||||||
if (!BB_MMU)
|
if (!BB_MMU)
|
||||||
pos = dest->length;
|
pos = dest->length;
|
||||||
add_till_closing_bracket(dest, input, '}');
|
last_ch = add_till_closing_bracket(dest, input, end_ch);
|
||||||
#if !BB_MMU
|
|
||||||
if (as_string) {
|
if (as_string) {
|
||||||
o_addstr(as_string, dest->data + pos);
|
o_addstr(as_string, dest->data + pos);
|
||||||
o_addchr(as_string, '}');
|
o_addchr(as_string, last_ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
|
||||||
|
/* close the first block: */
|
||||||
|
o_addchr(dest, SPECIAL_VAR_SYMBOL);
|
||||||
|
/* while parsing N from ${var:N[:M]}... */
|
||||||
|
if ((end_ch & 0xff) == last_ch) {
|
||||||
|
/* ...got ':' - parse the rest */
|
||||||
|
end_ch = '}';
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
/* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
|
||||||
|
o_addstr(dest, "999999999");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,3 +39,13 @@ f:1:2=|12|
|
|||||||
f::2 =|01|
|
f::2 =|01|
|
||||||
f:1: =||
|
f:1: =||
|
||||||
f:: =||
|
f:: =||
|
||||||
|
Substrings with expressions
|
||||||
|
f =|01234567|
|
||||||
|
f:1+1:2+2 =|2345|
|
||||||
|
f:-1:2+2 =|01234567|
|
||||||
|
f:1:f =|1234567|
|
||||||
|
f:1:$f =|1234567|
|
||||||
|
f:1:${f} =|1234567|
|
||||||
|
f:1:${f:3:1} =|123|
|
||||||
|
f:1:1`echo 1`=|1|
|
||||||
|
Done
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
# do all of these in subshells since it's supposed to error out
|
|
||||||
|
|
||||||
export var=0123456789
|
|
||||||
|
|
||||||
# first try some invalid patterns
|
# first try some invalid patterns
|
||||||
|
# do all of these in subshells since it's supposed to error out
|
||||||
|
export var=0123456789
|
||||||
"$THIS_SH" -c 'echo ${:}'
|
"$THIS_SH" -c 'echo ${:}'
|
||||||
"$THIS_SH" -c 'echo ${::}'
|
"$THIS_SH" -c 'echo ${::}'
|
||||||
"$THIS_SH" -c 'echo ${:1}'
|
"$THIS_SH" -c 'echo ${:1}'
|
||||||
@@ -15,44 +13,56 @@ export var=0123456789
|
|||||||
# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
|
# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
|
||||||
|
|
||||||
# now some valid ones
|
# now some valid ones
|
||||||
"$THIS_SH" -c 'set --; echo "1 =|${1}|"'
|
set --; echo "1 =|${1}|"
|
||||||
"$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"'
|
set --; echo "1:1 =|${1:1}|"
|
||||||
"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"'
|
set --; echo "1:1:2=|${1:1:2}|"
|
||||||
"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"'
|
set --; echo "1::2 =|${1::2}|"
|
||||||
"$THIS_SH" -c 'set --; echo "1:1: =|${1:1:}|"'
|
set --; echo "1:1: =|${1:1:}|"
|
||||||
"$THIS_SH" -c 'set --; echo "1:: =|${1::}|"'
|
set --; echo "1:: =|${1::}|"
|
||||||
|
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"'
|
set -- 0123; echo "1 =|${1}|"
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"'
|
set -- 0123; echo "1:1 =|${1:1}|"
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"'
|
set -- 0123; echo "1:1:2=|${1:1:2}|"
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"'
|
set -- 0123; echo "1::2 =|${1::2}|"
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1:1: =|${1:1:}|"'
|
set -- 0123; echo "1:1: =|${1:1:}|"
|
||||||
"$THIS_SH" -c 'set -- 0123; echo "1:: =|${1::}|"'
|
set -- 0123; echo "1:: =|${1::}|"
|
||||||
|
|
||||||
"$THIS_SH" -c 'unset f; echo "f =|$f|"'
|
unset f; echo "f =|$f|"
|
||||||
"$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"'
|
unset f; echo "f:1 =|${f:1}|"
|
||||||
"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"'
|
unset f; echo "f:1:2=|${f:1:2}|"
|
||||||
"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"'
|
unset f; echo "f::2 =|${f::2}|"
|
||||||
"$THIS_SH" -c 'unset f; echo "f:1: =|${f:1:}|"'
|
unset f; echo "f:1: =|${f:1:}|"
|
||||||
"$THIS_SH" -c 'unset f; echo "f:: =|${f::}|"'
|
unset f; echo "f:: =|${f::}|"
|
||||||
|
|
||||||
"$THIS_SH" -c 'f=; echo "f =|$f|"'
|
f=; echo "f =|$f|"
|
||||||
"$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"'
|
f=; echo "f:1 =|${f:1}|"
|
||||||
"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"'
|
f=; echo "f:1:2=|${f:1:2}|"
|
||||||
"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"'
|
f=; echo "f::2 =|${f::2}|"
|
||||||
"$THIS_SH" -c 'f=; echo "f:1: =|${f:1:}|"'
|
f=; echo "f:1: =|${f:1:}|"
|
||||||
"$THIS_SH" -c 'f=; echo "f:: =|${f::}|"'
|
f=; echo "f:: =|${f::}|"
|
||||||
|
|
||||||
"$THIS_SH" -c 'f=a; echo "f =|$f|"'
|
f=a; echo "f =|$f|"
|
||||||
"$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"'
|
f=a; echo "f:1 =|${f:1}|"
|
||||||
"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"'
|
f=a; echo "f:1:2=|${f:1:2}|"
|
||||||
"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"'
|
f=a; echo "f::2 =|${f::2}|"
|
||||||
"$THIS_SH" -c 'f=a; echo "f:1: =|${f:1:}|"'
|
f=a; echo "f:1: =|${f:1:}|"
|
||||||
"$THIS_SH" -c 'f=a; echo "f:: =|${f::}|"'
|
f=a; echo "f:: =|${f::}|"
|
||||||
|
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f =|$f|"'
|
f=0123456789; echo "f =|$f|"
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"'
|
f=0123456789; echo "f:1 =|${f:1}|"
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"'
|
f=0123456789; echo "f:1:2=|${f:1:2}|"
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"'
|
f=0123456789; echo "f::2 =|${f::2}|"
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f:1: =|${f:1:}|"'
|
f=0123456789; echo "f:1: =|${f:1:}|"
|
||||||
"$THIS_SH" -c 'f=0123456789; echo "f:: =|${f::}|"'
|
f=0123456789; echo "f:: =|${f::}|"
|
||||||
|
|
||||||
|
echo "Substrings with expressions"
|
||||||
|
f=01234567; echo 'f '"=|$f|"
|
||||||
|
f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|"
|
||||||
|
f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|"
|
||||||
|
f=01234567; echo 'f:1:f '"=|${f:1:f}|"
|
||||||
|
f=01234567; echo 'f:1:$f '"=|${f:1:$f}|"
|
||||||
|
f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|"
|
||||||
|
f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
|
||||||
|
f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
|
||||||
|
|
||||||
|
echo Done
|
||||||
|
Reference in New Issue
Block a user