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
|
||||
|
||||
/* 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!
|
||||
/* Helper:
|
||||
* Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
|
||||
*/
|
||||
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;
|
||||
static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp, char first_ch)
|
||||
{
|
||||
const char *val = NULL;
|
||||
#if ENABLE_HUSH_TICK
|
||||
o_string subst_result = NULL_O_STRING;
|
||||
#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 *to_be_freed = NULL;
|
||||
char *p = *pp;
|
||||
char *var;
|
||||
char first_char;
|
||||
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 */
|
||||
if (isdigit(var[0])) {
|
||||
/* parse_dollar() should have vetted var for us */
|
||||
i = xatoi_positive(var);
|
||||
if (i < G.global_argc)
|
||||
val = G.global_argv[i];
|
||||
int n = xatoi_positive(var);
|
||||
if (n < G.global_argc)
|
||||
val = G.global_argv[n];
|
||||
/* else val remains NULL: $N with too big N */
|
||||
} else {
|
||||
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') {
|
||||
debug_printf_expand("expand: length(%s)=", val);
|
||||
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.
|
||||
* Then var's value is matched to it and matching part removed.
|
||||
*/
|
||||
if (val) {
|
||||
if (val && val[0]) {
|
||||
char *exp_exp_word;
|
||||
char *loc;
|
||||
unsigned scan_flags = pick_scan(exp_op, *exp_word);
|
||||
if (exp_op == *exp_word) /* ## or %% */
|
||||
exp_word++;
|
||||
//TODO: avoid xstrdup unless needed
|
||||
// (see HACK ALERT below)
|
||||
val = to_be_freed = xstrdup(val);
|
||||
exp_exp_word = expand_pseudo_dquoted(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) */
|
||||
|
||||
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
|
||||
store_val:
|
||||
o_string subst_result = NULL_O_STRING;
|
||||
#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 */
|
||||
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 */
|
||||
smallint sv = output->o_escape;
|
||||
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 */
|
||||
debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape);
|
||||
}
|
||||
} /* default: */
|
||||
break;
|
||||
|
||||
} /* switch (char after <SPECIAL_VAR_SYMBOL>) */
|
||||
|
||||
if (val) {
|
||||
if (val && val[0]) {
|
||||
o_addQstr(output, val, strlen(val));
|
||||
}
|
||||
free(to_be_freed);
|
||||
|
Loading…
x
Reference in New Issue
Block a user