diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-12-21 10:09:48 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-12-21 10:14:18 +0100 |
commit | 07abc7c6f7dd0875e0aa548bba952174a54c8797 (patch) | |
tree | 88f42363939bfbf5221ed2e1f24eeb511ccc6721 | |
parent | 901d2c3862e0aaf96fc38ab32bdf1aa3f7f509d2 (diff) | |
download | busybox-w32-07abc7c6f7dd0875e0aa548bba952174a54c8797.tar.gz busybox-w32-07abc7c6f7dd0875e0aa548bba952174a54c8797.tar.bz2 busybox-w32-07abc7c6f7dd0875e0aa548bba952174a54c8797.zip |
hush: deal with FIXMEs for corner cases in parameter expansion
function old new delta
expand_one_var 2323 2344 +21
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 52 |
1 files changed, 34 insertions, 18 deletions
diff --git a/shell/hush.c b/shell/hush.c index f29c985ad..a8f7237d5 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -6506,9 +6506,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
6506 | * Word is expanded to produce a glob pattern. | 6506 | * Word is expanded to produce a glob pattern. |
6507 | * Then var's value is matched to it and matching part removed. | 6507 | * Then var's value is matched to it and matching part removed. |
6508 | */ | 6508 | */ |
6509 | //FIXME: ${x#...${...}...} | 6509 | /* bash compat: if x is "" and no shrinking of it is possible, |
6510 | //should evaluate inner ${...} even if x is "" and no shrinking of it is possible - | 6510 | * inner ${...} is not evaluated. Example: |
6511 | //inner ${...} may have side effects! | 6511 | * unset b; : ${a%${b=B}}; echo $b |
6512 | * assignment b=B only happens if $a is not "". | ||
6513 | */ | ||
6512 | if (val && val[0]) { | 6514 | if (val && val[0]) { |
6513 | char *t; | 6515 | char *t; |
6514 | char *exp_exp_word; | 6516 | char *exp_exp_word; |
@@ -6547,7 +6549,12 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
6547 | * and if // is used, it is encoded as \: | 6549 | * and if // is used, it is encoded as \: |
6548 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> | 6550 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> |
6549 | */ | 6551 | */ |
6550 | if (val && val[0]) { | 6552 | /* bash compat: if var is "", both pattern and repl |
6553 | * are still evaluated, if it is unset, then not: | ||
6554 | * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3 | ||
6555 | * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set | ||
6556 | */ | ||
6557 | if (val /*&& val[0]*/) { | ||
6551 | /* pattern uses non-standard expansion. | 6558 | /* pattern uses non-standard expansion. |
6552 | * repl should be unbackslashed and globbed | 6559 | * repl should be unbackslashed and globbed |
6553 | * by the usual expansion rules: | 6560 | * by the usual expansion rules: |
@@ -6583,8 +6590,9 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
6583 | free(pattern); | 6590 | free(pattern); |
6584 | free(repl); | 6591 | free(repl); |
6585 | } else { | 6592 | } else { |
6586 | /* Empty variable always gives nothing */ | 6593 | /* Unset variable always gives nothing */ |
6587 | // "v=''; echo ${v/*/w}" prints "", not "w" | 6594 | // a=; echo ${a/*/w} # "w" |
6595 | // unset a; echo ${a/*/w} # "" | ||
6588 | /* Just skip "replace" part */ | 6596 | /* Just skip "replace" part */ |
6589 | *p++ = SPECIAL_VAR_SYMBOL; | 6597 | *p++ = SPECIAL_VAR_SYMBOL; |
6590 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 6598 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
@@ -6599,40 +6607,48 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
6599 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | 6607 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> |
6600 | */ | 6608 | */ |
6601 | arith_t beg, len; | 6609 | arith_t beg, len; |
6610 | unsigned vallen; | ||
6602 | const char *errmsg; | 6611 | const char *errmsg; |
6603 | 6612 | ||
6604 | beg = expand_and_evaluate_arith(exp_word, &errmsg); | 6613 | beg = expand_and_evaluate_arith(exp_word, &errmsg); |
6605 | if (errmsg) | 6614 | if (errmsg) |
6606 | goto arith_err; | 6615 | goto empty_result; |
6607 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | 6616 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); |
6608 | *p++ = SPECIAL_VAR_SYMBOL; | 6617 | *p++ = SPECIAL_VAR_SYMBOL; |
6609 | exp_word = p; | 6618 | exp_word = p; |
6610 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 6619 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
6611 | *p = '\0'; | 6620 | *p = '\0'; |
6612 | len = expand_and_evaluate_arith(exp_word, &errmsg); | 6621 | vallen = strlen(val); |
6613 | if (errmsg) | ||
6614 | goto arith_err; | ||
6615 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | ||
6616 | if (beg < 0) { | 6622 | if (beg < 0) { |
6617 | /* negative beg counts from the end */ | 6623 | /* negative beg counts from the end */ |
6618 | beg = (arith_t)strlen(val) + beg; | 6624 | beg = (arith_t)vallen + beg; |
6619 | if (beg < 0) /* ${v: -999999} is "" */ | ||
6620 | beg = len = 0; | ||
6621 | } | 6625 | } |
6626 | /* If expansion will be empty, do not even evaluate len */ | ||
6627 | if (!val || beg < 0 || beg > vallen) { | ||
6628 | /* Why > vallen, not >=? bash: | ||
6629 | * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!) | ||
6630 | * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set | ||
6631 | */ | ||
6632 | goto empty_result; | ||
6633 | } | ||
6634 | len = expand_and_evaluate_arith(exp_word, &errmsg); | ||
6635 | if (errmsg) | ||
6636 | goto empty_result; | ||
6637 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | ||
6622 | debug_printf_varexp("from val:'%s'\n", val); | 6638 | debug_printf_varexp("from val:'%s'\n", val); |
6623 | if (len < 0) { | 6639 | if (len < 0) { |
6624 | /* in bash, len=-n means strlen()-n */ | 6640 | /* in bash, len=-n means strlen()-n */ |
6625 | len = (arith_t)strlen(val) - beg + len; | 6641 | len = (arith_t)vallen - beg + len; |
6626 | if (len < 0) /* bash compat */ | 6642 | if (len < 0) /* bash compat */ |
6627 | msg_and_die_if_script("%s: substring expression < 0", var); | 6643 | msg_and_die_if_script("%s: substring expression < 0", var); |
6628 | } | 6644 | } |
6629 | if (len <= 0 || !val || beg >= strlen(val)) { | 6645 | if (len <= 0 || !val /*|| beg >= vallen*/) { |
6630 | arith_err: | 6646 | empty_result: |
6631 | val = NULL; | 6647 | val = NULL; |
6632 | } else { | 6648 | } else { |
6633 | /* Paranoia. What if user entered 9999999999999 | 6649 | /* Paranoia. What if user entered 9999999999999 |
6634 | * which fits in arith_t but not int? */ | 6650 | * which fits in arith_t but not int? */ |
6635 | if (len >= INT_MAX) | 6651 | if (len > INT_MAX) |
6636 | len = INT_MAX; | 6652 | len = INT_MAX; |
6637 | val = to_be_freed = xstrndup(val + beg, len); | 6653 | val = to_be_freed = xstrndup(val + beg, len); |
6638 | } | 6654 | } |