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>
This commit is contained in:
parent
901d2c3862
commit
07abc7c6f7
52
shell/hush.c
52
shell/hush.c
@ -6506,9 +6506,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
|
|||||||
* Word is expanded to produce a glob pattern.
|
* Word is expanded to produce a glob pattern.
|
||||||
* Then var's value is matched to it and matching part removed.
|
* Then var's value is matched to it and matching part removed.
|
||||||
*/
|
*/
|
||||||
//FIXME: ${x#...${...}...}
|
/* bash compat: if x is "" and no shrinking of it is possible,
|
||||||
//should evaluate inner ${...} even if x is "" and no shrinking of it is possible -
|
* inner ${...} is not evaluated. Example:
|
||||||
//inner ${...} may have side effects!
|
* unset b; : ${a%${b=B}}; echo $b
|
||||||
|
* assignment b=B only happens if $a is not "".
|
||||||
|
*/
|
||||||
if (val && val[0]) {
|
if (val && val[0]) {
|
||||||
char *t;
|
char *t;
|
||||||
char *exp_exp_word;
|
char *exp_exp_word;
|
||||||
@ -6547,7 +6549,12 @@ static NOINLINE int expand_one_var(o_string *output, int n,
|
|||||||
* and if // is used, it is encoded as \:
|
* and if // is used, it is encoded as \:
|
||||||
* var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
|
* var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
|
||||||
*/
|
*/
|
||||||
if (val && val[0]) {
|
/* bash compat: if var is "", both pattern and repl
|
||||||
|
* are still evaluated, if it is unset, then not:
|
||||||
|
* unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
|
||||||
|
* unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
|
||||||
|
*/
|
||||||
|
if (val /*&& val[0]*/) {
|
||||||
/* pattern uses non-standard expansion.
|
/* pattern uses non-standard expansion.
|
||||||
* repl should be unbackslashed and globbed
|
* repl should be unbackslashed and globbed
|
||||||
* by the usual expansion rules:
|
* by the usual expansion rules:
|
||||||
@ -6583,8 +6590,9 @@ static NOINLINE int expand_one_var(o_string *output, int n,
|
|||||||
free(pattern);
|
free(pattern);
|
||||||
free(repl);
|
free(repl);
|
||||||
} else {
|
} else {
|
||||||
/* Empty variable always gives nothing */
|
/* Unset variable always gives nothing */
|
||||||
// "v=''; echo ${v/*/w}" prints "", not "w"
|
// a=; echo ${a/*/w} # "w"
|
||||||
|
// unset a; echo ${a/*/w} # ""
|
||||||
/* Just skip "replace" part */
|
/* Just skip "replace" part */
|
||||||
*p++ = SPECIAL_VAR_SYMBOL;
|
*p++ = SPECIAL_VAR_SYMBOL;
|
||||||
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
||||||
@ -6599,40 +6607,48 @@ static NOINLINE int expand_one_var(o_string *output, int n,
|
|||||||
* var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
|
* var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
|
||||||
*/
|
*/
|
||||||
arith_t beg, len;
|
arith_t beg, len;
|
||||||
|
unsigned vallen;
|
||||||
const char *errmsg;
|
const char *errmsg;
|
||||||
|
|
||||||
beg = expand_and_evaluate_arith(exp_word, &errmsg);
|
beg = expand_and_evaluate_arith(exp_word, &errmsg);
|
||||||
if (errmsg)
|
if (errmsg)
|
||||||
goto arith_err;
|
goto empty_result;
|
||||||
debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
|
debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
|
||||||
*p++ = SPECIAL_VAR_SYMBOL;
|
*p++ = SPECIAL_VAR_SYMBOL;
|
||||||
exp_word = p;
|
exp_word = p;
|
||||||
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
len = expand_and_evaluate_arith(exp_word, &errmsg);
|
vallen = strlen(val);
|
||||||
if (errmsg)
|
|
||||||
goto arith_err;
|
|
||||||
debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
|
|
||||||
if (beg < 0) {
|
if (beg < 0) {
|
||||||
/* negative beg counts from the end */
|
/* negative beg counts from the end */
|
||||||
beg = (arith_t)strlen(val) + beg;
|
beg = (arith_t)vallen + beg;
|
||||||
if (beg < 0) /* ${v: -999999} is "" */
|
|
||||||
beg = len = 0;
|
|
||||||
}
|
}
|
||||||
|
/* If expansion will be empty, do not even evaluate len */
|
||||||
|
if (!val || beg < 0 || beg > vallen) {
|
||||||
|
/* Why > vallen, not >=? bash:
|
||||||
|
* unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
|
||||||
|
* unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
|
||||||
|
*/
|
||||||
|
goto empty_result;
|
||||||
|
}
|
||||||
|
len = expand_and_evaluate_arith(exp_word, &errmsg);
|
||||||
|
if (errmsg)
|
||||||
|
goto empty_result;
|
||||||
|
debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
|
||||||
debug_printf_varexp("from val:'%s'\n", val);
|
debug_printf_varexp("from val:'%s'\n", val);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
/* in bash, len=-n means strlen()-n */
|
/* in bash, len=-n means strlen()-n */
|
||||||
len = (arith_t)strlen(val) - beg + len;
|
len = (arith_t)vallen - beg + len;
|
||||||
if (len < 0) /* bash compat */
|
if (len < 0) /* bash compat */
|
||||||
msg_and_die_if_script("%s: substring expression < 0", var);
|
msg_and_die_if_script("%s: substring expression < 0", var);
|
||||||
}
|
}
|
||||||
if (len <= 0 || !val || beg >= strlen(val)) {
|
if (len <= 0 || !val /*|| beg >= vallen*/) {
|
||||||
arith_err:
|
empty_result:
|
||||||
val = NULL;
|
val = NULL;
|
||||||
} else {
|
} else {
|
||||||
/* Paranoia. What if user entered 9999999999999
|
/* Paranoia. What if user entered 9999999999999
|
||||||
* which fits in arith_t but not int? */
|
* which fits in arith_t but not int? */
|
||||||
if (len >= INT_MAX)
|
if (len > INT_MAX)
|
||||||
len = INT_MAX;
|
len = INT_MAX;
|
||||||
val = to_be_freed = xstrndup(val + beg, len);
|
val = to_be_freed = xstrndup(val + beg, len);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user