hush: fix a bug where expand_one_var wasn't restoring 1st char of the encoded $var

function                                             old     new   delta
expand_one_var                                      1515    1513      -2
expand_vars_to_list                                 1133    1122     -11

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
Denys Vlasenko 2010-09-09 14:38:46 +02:00
parent 101a4e3e21
commit bfc02a76c5

View File

@ -4487,7 +4487,7 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
/* Helper: /* Helper:
* Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
*/ */
static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp, char first_ch) static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp)
{ {
const char *val = NULL; const char *val = NULL;
char *to_be_freed = NULL; char *to_be_freed = NULL;
@ -4498,11 +4498,13 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
char exp_save = exp_save; /* for compiler */ char exp_save = exp_save; /* for compiler */
char *exp_saveptr; /* points to expansion operator */ char *exp_saveptr; /* points to expansion operator */
char *exp_word = exp_word; /* for compiler */ char *exp_word = exp_word; /* for compiler */
char arg0;
*p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
var = arg; var = arg;
exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
first_char = arg[0] = first_ch & 0x7f; arg0 = arg[0];
first_char = arg[0] = arg0 & 0x7f;
exp_op = 0; exp_op = 0;
if (first_char == '#' /* ${#... */ if (first_char == '#' /* ${#... */
@ -4754,7 +4756,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
*exp_saveptr = exp_save; *exp_saveptr = exp_save;
} /* if (exp_op) */ } /* if (exp_op) */
arg[0] = first_ch; arg[0] = arg0;
*pp = p; *pp = p;
*to_be_freed_pp = to_be_freed; *to_be_freed_pp = to_be_freed;
@ -4771,11 +4773,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
/* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
* expansion of right-hand side of assignment == 1-element expand. * expansion of right-hand side of assignment == 1-element expand.
*/ */
char ored_ch; char cant_be_null = 0; /* only bit 0x80 matters */
char *p; char *p;
ored_ch = 0;
debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
!!(output->o_expflags & EXP_FLAG_SINGLEWORD)); !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
debug_print_list("expand_vars_to_list", output, n); debug_print_list("expand_vars_to_list", output, n);
@ -4797,11 +4797,17 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
arg = ++p; arg = ++p;
p = strchr(p, SPECIAL_VAR_SYMBOL); p = strchr(p, SPECIAL_VAR_SYMBOL);
/* Fetch special var name (if it is indeed one of them)
* and quote bit, force the bit on if singleword expansion -
* important for not getting v=$@ expand to many words. */
first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD); first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
/* "$@" is special. Even if quoted, it can still
* expand to nothing (not even an empty string) */ /* Is this variable quoted and thus expansion can't be null?
* "$@" is special. Even if quoted, it can still
* expand to nothing (not even an empty string),
* thus it is excluded. */
if ((first_ch & 0x7f) != '@') if ((first_ch & 0x7f) != '@')
ored_ch |= first_ch; cant_be_null |= first_ch;
switch (first_ch & 0x7f) { switch (first_ch & 0x7f) {
/* Highest bit in first_ch indicates that var is double-quoted */ /* Highest bit in first_ch indicates that var is double-quoted */
@ -4811,10 +4817,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
if (!G.global_argv[1]) if (!G.global_argv[1])
break; break;
i = 1; i = 1;
ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
int sv = output->o_expflags; int sv = output->o_expflags;
/* unquoted var's contents should be globbed, so don't escape */ /* unquoted var's contents should be globbed, so don't escape */
//TODO: make _caller_ set EXP_FLAG_ESC_GLOB_CHARS properly
output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
while (G.global_argv[i]) { while (G.global_argv[i]) {
n = expand_on_ifs(output, n, G.global_argv[i]); n = expand_on_ifs(output, n, G.global_argv[i]);
@ -4833,7 +4840,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
/* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
* and in this case should treat it like '$*' - see 'else...' below */ * and in this case should treat it like '$*' - see 'else...' below */
if (first_ch == ('@'|0x80) /* quoted $@ */ if (first_ch == ('@'|0x80) /* quoted $@ */
&& !(output->o_expflags & EXP_FLAG_SINGLEWORD) && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
) { ) {
while (1) { while (1) {
o_addQstr(output, G.global_argv[i]); o_addQstr(output, G.global_argv[i]);
@ -4843,7 +4850,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
debug_print_list("expand_vars_to_list[4]", output, n); debug_print_list("expand_vars_to_list[4]", output, n);
n = o_save_ptr(output, n); n = o_save_ptr(output, n);
} }
} else { /* quoted $*: add as one word */ } else { /* quoted $* (or v="$@" case): add as one word */
while (1) { while (1) {
o_addQstr(output, G.global_argv[i]); o_addQstr(output, G.global_argv[i]);
if (!G.global_argv[++i]) if (!G.global_argv[++i])
@ -4857,11 +4864,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
/* "Empty variable", used to make "" etc to not disappear */ /* "Empty variable", used to make "" etc to not disappear */
arg++; arg++;
ored_ch = 0x80; cant_be_null = 0x80;
break; break;
#if ENABLE_HUSH_TICK #if ENABLE_HUSH_TICK
case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
*p = '\0'; *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
arg++; arg++;
/* Can't just stuff it into output o_string, /* Can't just stuff it into output o_string,
* expanded result may need to be globbed * expanded result may need to be globbed
@ -4904,7 +4911,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
} }
#endif #endif
default: default:
val = expand_one_var(&to_be_freed, arg, &p, first_ch); val = expand_one_var(&to_be_freed, arg, &p);
IF_HUSH_TICK(store_val:) 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, debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
@ -4912,6 +4919,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
if (val && val[0]) { 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 */
int sv = output->o_expflags; int sv = output->o_expflags;
//TODO: make _caller_ set EXP_FLAG_ESC_GLOB_CHARS properly
output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
n = expand_on_ifs(output, n, val); n = expand_on_ifs(output, n, val);
val = NULL; val = NULL;
@ -4929,7 +4937,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
o_addQstr(output, val); o_addQstr(output, val);
} }
free(to_be_freed); free(to_be_freed);
/* Do the check to avoid writing to a const string */
/* Restore NULL'ed SPECIAL_VAR_SYMBOL.
* Do the check to avoid writing to a const string. */
if (*p != SPECIAL_VAR_SYMBOL) if (*p != SPECIAL_VAR_SYMBOL)
*p = SPECIAL_VAR_SYMBOL; *p = SPECIAL_VAR_SYMBOL;
@ -4946,7 +4956,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
o_addstr_with_NUL(output, arg); o_addstr_with_NUL(output, arg);
debug_print_list("expand_vars_to_list[b]", output, n); debug_print_list("expand_vars_to_list[b]", output, n);
} else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
&& !(ored_ch & 0x80) /* and all vars were not quoted. */ && !(cant_be_null & 0x80) /* and all vars were not quoted. */
) { ) {
n--; n--;
/* allow to reuse list[n] later without re-growth */ /* allow to reuse list[n] later without re-growth */