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 | } |
