aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-12-21 10:09:48 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2020-12-21 10:14:18 +0100
commit07abc7c6f7dd0875e0aa548bba952174a54c8797 (patch)
tree88f42363939bfbf5221ed2e1f24eeb511ccc6721
parent901d2c3862e0aaf96fc38ab32bdf1aa3f7f509d2 (diff)
downloadbusybox-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.c52
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 }