hush: move variable expansion into a separate function. No logic changes
function old new delta expand_one_var - 1551 +1551 expand_vars_to_list 2833 1175 -1658 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/1 up/down: 1551/-1658) Total: -107 bytes Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
parent
36f774a0cd
commit
f2dc20c2d5
311
shell/hush.c
311
shell/hush.c
@ -2664,147 +2664,14 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Expand all variable references in given string, adding words to list[]
|
/* Helper:
|
||||||
* at n, n+1,... positions. Return updated n (so that list[n] is next one
|
* Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
|
||||||
* to be filled). This routine is extremely tricky: has to deal with
|
|
||||||
* variables/parameters with whitespace, $* and $@, and constructs like
|
|
||||||
* 'echo -$*-'. If you play here, you must run testsuite afterwards! */
|
|
||||||
static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|
||||||
{
|
|
||||||
/* or_mask is either 0 (normal case) or 0x80 -
|
|
||||||
* expansion of right-hand side of assignment == 1-element expand.
|
|
||||||
* It will also do no globbing, and thus we must not backslash-quote!
|
|
||||||
*/
|
*/
|
||||||
char ored_ch;
|
static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp, char first_ch)
|
||||||
char *p;
|
{
|
||||||
|
|
||||||
ored_ch = 0;
|
|
||||||
|
|
||||||
debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
|
|
||||||
debug_print_list("expand_vars_to_list", output, n);
|
|
||||||
n = o_save_ptr(output, n);
|
|
||||||
debug_print_list("expand_vars_to_list[0]", output, n);
|
|
||||||
|
|
||||||
while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
|
|
||||||
char first_ch;
|
|
||||||
int i;
|
|
||||||
char *to_be_freed = NULL;
|
|
||||||
const char *val = NULL;
|
const char *val = NULL;
|
||||||
#if ENABLE_HUSH_TICK
|
char *to_be_freed = NULL;
|
||||||
o_string subst_result = NULL_O_STRING;
|
char *p = *pp;
|
||||||
#endif
|
|
||||||
#if ENABLE_SH_MATH_SUPPORT
|
|
||||||
char arith_buf[sizeof(arith_t)*3 + 2];
|
|
||||||
#endif
|
|
||||||
o_addblock(output, arg, p - arg);
|
|
||||||
debug_print_list("expand_vars_to_list[1]", output, n);
|
|
||||||
arg = ++p;
|
|
||||||
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
|
||||||
|
|
||||||
first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */
|
|
||||||
/* "$@" is special. Even if quoted, it can still
|
|
||||||
* expand to nothing (not even an empty string) */
|
|
||||||
if ((first_ch & 0x7f) != '@')
|
|
||||||
ored_ch |= first_ch;
|
|
||||||
|
|
||||||
switch (first_ch & 0x7f) {
|
|
||||||
/* Highest bit in first_ch indicates that var is double-quoted */
|
|
||||||
case '*':
|
|
||||||
case '@':
|
|
||||||
i = 1;
|
|
||||||
if (!G.global_argv[i])
|
|
||||||
break;
|
|
||||||
ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
|
||||||
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
|
|
||||||
smallint sv = output->o_escape;
|
|
||||||
/* unquoted var's contents should be globbed, so don't escape */
|
|
||||||
output->o_escape = 0;
|
|
||||||
while (G.global_argv[i]) {
|
|
||||||
n = expand_on_ifs(output, n, G.global_argv[i]);
|
|
||||||
debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
|
|
||||||
if (G.global_argv[i++][0] && G.global_argv[i]) {
|
|
||||||
/* this argv[] is not empty and not last:
|
|
||||||
* put terminating NUL, start new word */
|
|
||||||
o_addchr(output, '\0');
|
|
||||||
debug_print_list("expand_vars_to_list[2]", output, n);
|
|
||||||
n = o_save_ptr(output, n);
|
|
||||||
debug_print_list("expand_vars_to_list[3]", output, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output->o_escape = sv;
|
|
||||||
} else
|
|
||||||
/* If or_mask is nonzero, we handle assignment 'a=....$@.....'
|
|
||||||
* and in this case should treat it like '$*' - see 'else...' below */
|
|
||||||
if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */
|
|
||||||
while (1) {
|
|
||||||
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
|
|
||||||
if (++i >= G.global_argc)
|
|
||||||
break;
|
|
||||||
o_addchr(output, '\0');
|
|
||||||
debug_print_list("expand_vars_to_list[4]", output, n);
|
|
||||||
n = o_save_ptr(output, n);
|
|
||||||
}
|
|
||||||
} else { /* quoted $*: add as one word */
|
|
||||||
while (1) {
|
|
||||||
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
|
|
||||||
if (!G.global_argv[++i])
|
|
||||||
break;
|
|
||||||
if (G.ifs[0])
|
|
||||||
o_addchr(output, G.ifs[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
|
|
||||||
/* "Empty variable", used to make "" etc to not disappear */
|
|
||||||
arg++;
|
|
||||||
ored_ch = 0x80;
|
|
||||||
break;
|
|
||||||
#if ENABLE_HUSH_TICK
|
|
||||||
case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
|
|
||||||
*p = '\0';
|
|
||||||
arg++;
|
|
||||||
/* Can't just stuff it into output o_string,
|
|
||||||
* expanded result may need to be globbed
|
|
||||||
* and $IFS-splitted */
|
|
||||||
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
|
|
||||||
G.last_exitcode = process_command_subs(&subst_result, arg);
|
|
||||||
debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
|
|
||||||
val = subst_result.data;
|
|
||||||
goto store_val;
|
|
||||||
#endif
|
|
||||||
#if ENABLE_SH_MATH_SUPPORT
|
|
||||||
case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
|
|
||||||
arith_t res;
|
|
||||||
int errcode;
|
|
||||||
|
|
||||||
arg++; /* skip '+' */
|
|
||||||
*p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
|
|
||||||
debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
|
|
||||||
res = expand_and_evaluate_arith(arg, &errcode);
|
|
||||||
|
|
||||||
if (errcode < 0) {
|
|
||||||
const char *msg = "error in arithmetic";
|
|
||||||
switch (errcode) {
|
|
||||||
case -3:
|
|
||||||
msg = "exponent less than 0";
|
|
||||||
break;
|
|
||||||
case -2:
|
|
||||||
msg = "divide by 0";
|
|
||||||
break;
|
|
||||||
case -5:
|
|
||||||
msg = "expression recursion loop detected";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
die_if_script(msg);
|
|
||||||
}
|
|
||||||
debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
|
|
||||||
sprintf(arith_buf, arith_t_fmt, res);
|
|
||||||
val = arith_buf;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
|
|
||||||
//TODO: move to a subroutine?
|
|
||||||
char *var;
|
char *var;
|
||||||
char first_char;
|
char first_char;
|
||||||
char exp_op;
|
char exp_op;
|
||||||
@ -2853,9 +2720,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
/* lookup the variable in question */
|
/* lookup the variable in question */
|
||||||
if (isdigit(var[0])) {
|
if (isdigit(var[0])) {
|
||||||
/* parse_dollar() should have vetted var for us */
|
/* parse_dollar() should have vetted var for us */
|
||||||
i = xatoi_positive(var);
|
int n = xatoi_positive(var);
|
||||||
if (i < G.global_argc)
|
if (n < G.global_argc)
|
||||||
val = G.global_argv[i];
|
val = G.global_argv[n];
|
||||||
/* else val remains NULL: $N with too big N */
|
/* else val remains NULL: $N with too big N */
|
||||||
} else {
|
} else {
|
||||||
switch (var[0]) {
|
switch (var[0]) {
|
||||||
@ -2876,7 +2743,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle any expansions */
|
/* Handle any expansions */
|
||||||
if (exp_op == 'L') {
|
if (exp_op == 'L') {
|
||||||
debug_printf_expand("expand: length(%s)=", val);
|
debug_printf_expand("expand: length(%s)=", val);
|
||||||
val = utoa(val ? strlen(val) : 0);
|
val = utoa(val ? strlen(val) : 0);
|
||||||
@ -2892,12 +2759,14 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
if (val) {
|
if (val && val[0]) {
|
||||||
char *exp_exp_word;
|
char *exp_exp_word;
|
||||||
char *loc;
|
char *loc;
|
||||||
unsigned scan_flags = pick_scan(exp_op, *exp_word);
|
unsigned scan_flags = pick_scan(exp_op, *exp_word);
|
||||||
if (exp_op == *exp_word) /* ## or %% */
|
if (exp_op == *exp_word) /* ## or %% */
|
||||||
exp_word++;
|
exp_word++;
|
||||||
|
//TODO: avoid xstrdup unless needed
|
||||||
|
// (see HACK ALERT below)
|
||||||
val = to_be_freed = xstrdup(val);
|
val = to_be_freed = xstrdup(val);
|
||||||
exp_exp_word = expand_pseudo_dquoted(exp_word);
|
exp_exp_word = expand_pseudo_dquoted(exp_word);
|
||||||
if (exp_exp_word)
|
if (exp_exp_word)
|
||||||
@ -3046,12 +2915,157 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
} /* if (exp_op) */
|
} /* if (exp_op) */
|
||||||
|
|
||||||
arg[0] = first_ch;
|
arg[0] = first_ch;
|
||||||
|
|
||||||
|
*pp = p;
|
||||||
|
*to_be_freed_pp = to_be_freed;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand all variable references in given string, adding words to list[]
|
||||||
|
* at n, n+1,... positions. Return updated n (so that list[n] is next one
|
||||||
|
* to be filled). This routine is extremely tricky: has to deal with
|
||||||
|
* variables/parameters with whitespace, $* and $@, and constructs like
|
||||||
|
* 'echo -$*-'. If you play here, you must run testsuite afterwards! */
|
||||||
|
static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
||||||
|
{
|
||||||
|
/* or_mask is either 0 (normal case) or 0x80 -
|
||||||
|
* expansion of right-hand side of assignment == 1-element expand.
|
||||||
|
* It will also do no globbing, and thus we must not backslash-quote!
|
||||||
|
*/
|
||||||
|
char ored_ch;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
ored_ch = 0;
|
||||||
|
|
||||||
|
debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
|
||||||
|
debug_print_list("expand_vars_to_list", output, n);
|
||||||
|
n = o_save_ptr(output, n);
|
||||||
|
debug_print_list("expand_vars_to_list[0]", output, n);
|
||||||
|
|
||||||
|
while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
|
||||||
|
char first_ch;
|
||||||
|
int i;
|
||||||
|
char *to_be_freed = NULL;
|
||||||
|
const char *val = NULL;
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
store_val:
|
o_string subst_result = NULL_O_STRING;
|
||||||
#endif
|
#endif
|
||||||
|
#if ENABLE_SH_MATH_SUPPORT
|
||||||
|
char arith_buf[sizeof(arith_t)*3 + 2];
|
||||||
|
#endif
|
||||||
|
o_addblock(output, arg, p - arg);
|
||||||
|
debug_print_list("expand_vars_to_list[1]", output, n);
|
||||||
|
arg = ++p;
|
||||||
|
p = strchr(p, SPECIAL_VAR_SYMBOL);
|
||||||
|
|
||||||
|
first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */
|
||||||
|
/* "$@" is special. Even if quoted, it can still
|
||||||
|
* expand to nothing (not even an empty string) */
|
||||||
|
if ((first_ch & 0x7f) != '@')
|
||||||
|
ored_ch |= first_ch;
|
||||||
|
|
||||||
|
switch (first_ch & 0x7f) {
|
||||||
|
/* Highest bit in first_ch indicates that var is double-quoted */
|
||||||
|
case '*':
|
||||||
|
case '@':
|
||||||
|
i = 1;
|
||||||
|
if (!G.global_argv[i])
|
||||||
|
break;
|
||||||
|
ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
||||||
|
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
|
||||||
|
smallint sv = output->o_escape;
|
||||||
|
/* unquoted var's contents should be globbed, so don't escape */
|
||||||
|
output->o_escape = 0;
|
||||||
|
while (G.global_argv[i]) {
|
||||||
|
n = expand_on_ifs(output, n, G.global_argv[i]);
|
||||||
|
debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
|
||||||
|
if (G.global_argv[i++][0] && G.global_argv[i]) {
|
||||||
|
/* this argv[] is not empty and not last:
|
||||||
|
* put terminating NUL, start new word */
|
||||||
|
o_addchr(output, '\0');
|
||||||
|
debug_print_list("expand_vars_to_list[2]", output, n);
|
||||||
|
n = o_save_ptr(output, n);
|
||||||
|
debug_print_list("expand_vars_to_list[3]", output, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output->o_escape = sv;
|
||||||
|
} else
|
||||||
|
/* If or_mask is nonzero, we handle assignment 'a=....$@.....'
|
||||||
|
* and in this case should treat it like '$*' - see 'else...' below */
|
||||||
|
if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */
|
||||||
|
while (1) {
|
||||||
|
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
|
||||||
|
if (++i >= G.global_argc)
|
||||||
|
break;
|
||||||
|
o_addchr(output, '\0');
|
||||||
|
debug_print_list("expand_vars_to_list[4]", output, n);
|
||||||
|
n = o_save_ptr(output, n);
|
||||||
|
}
|
||||||
|
} else { /* quoted $*: add as one word */
|
||||||
|
while (1) {
|
||||||
|
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
|
||||||
|
if (!G.global_argv[++i])
|
||||||
|
break;
|
||||||
|
if (G.ifs[0])
|
||||||
|
o_addchr(output, G.ifs[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
|
||||||
|
/* "Empty variable", used to make "" etc to not disappear */
|
||||||
|
arg++;
|
||||||
|
ored_ch = 0x80;
|
||||||
|
break;
|
||||||
|
#if ENABLE_HUSH_TICK
|
||||||
|
case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
|
||||||
|
*p = '\0';
|
||||||
|
arg++;
|
||||||
|
/* Can't just stuff it into output o_string,
|
||||||
|
* expanded result may need to be globbed
|
||||||
|
* and $IFS-splitted */
|
||||||
|
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
|
||||||
|
G.last_exitcode = process_command_subs(&subst_result, arg);
|
||||||
|
debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
|
||||||
|
val = subst_result.data;
|
||||||
|
goto store_val;
|
||||||
|
#endif
|
||||||
|
#if ENABLE_SH_MATH_SUPPORT
|
||||||
|
case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
|
||||||
|
arith_t res;
|
||||||
|
int errcode;
|
||||||
|
|
||||||
|
arg++; /* skip '+' */
|
||||||
|
*p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
|
||||||
|
debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
|
||||||
|
res = expand_and_evaluate_arith(arg, &errcode);
|
||||||
|
|
||||||
|
if (errcode < 0) {
|
||||||
|
const char *msg = "error in arithmetic";
|
||||||
|
switch (errcode) {
|
||||||
|
case -3:
|
||||||
|
msg = "exponent less than 0";
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
msg = "divide by 0";
|
||||||
|
break;
|
||||||
|
case -5:
|
||||||
|
msg = "expression recursion loop detected";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
die_if_script(msg);
|
||||||
|
}
|
||||||
|
debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
|
||||||
|
sprintf(arith_buf, arith_t_fmt, res);
|
||||||
|
val = arith_buf;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
val = expand_one_var(&to_be_freed, arg, &p, first_ch);
|
||||||
|
IF_HUSH_TICK(store_val:)
|
||||||
if (!(first_ch & 0x80)) { /* unquoted $VAR */
|
if (!(first_ch & 0x80)) { /* unquoted $VAR */
|
||||||
debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape);
|
debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape);
|
||||||
if (val) {
|
if (val && val[0]) {
|
||||||
/* unquoted var's contents should be globbed, so don't escape */
|
/* unquoted var's contents should be globbed, so don't escape */
|
||||||
smallint sv = output->o_escape;
|
smallint sv = output->o_escape;
|
||||||
output->o_escape = 0;
|
output->o_escape = 0;
|
||||||
@ -3062,10 +3076,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
|
|||||||
} else { /* quoted $VAR, val will be appended below */
|
} else { /* quoted $VAR, val will be appended below */
|
||||||
debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape);
|
debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape);
|
||||||
}
|
}
|
||||||
} /* default: */
|
break;
|
||||||
|
|
||||||
} /* switch (char after <SPECIAL_VAR_SYMBOL>) */
|
} /* switch (char after <SPECIAL_VAR_SYMBOL>) */
|
||||||
|
|
||||||
if (val) {
|
if (val && val[0]) {
|
||||||
o_addQstr(output, val, strlen(val));
|
o_addQstr(output, val, strlen(val));
|
||||||
}
|
}
|
||||||
free(to_be_freed);
|
free(to_be_freed);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user