From bfc02a76c5c3f0c8a5de518a46951098fef1e3cb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2010 14:38:46 +0200 Subject: [PATCH] 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 --- shell/hush.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index d58f526b8..9c615275c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4487,7 +4487,7 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c /* Helper: * Handles varname... 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; 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_saveptr; /* points to expansion operator */ char *exp_word = exp_word; /* for compiler */ + char arg0; *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ var = arg; 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; 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; } /* if (exp_op) */ - arg[0] = first_ch; + arg[0] = arg0; *pp = p; *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 * expansion of right-hand side of assignment == 1-element expand. */ - char ored_ch; + char cant_be_null = 0; /* only bit 0x80 matters */ char *p; - ored_ch = 0; - debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); 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; 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); - /* "$@" 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) != '@') - ored_ch |= first_ch; + cant_be_null |= first_ch; switch (first_ch & 0x7f) { /* 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]) break; 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 $@ */ int sv = output->o_expflags; /* 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; while (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=....$@.....' * and in this case should treat it like '$*' - see 'else...' below */ if (first_ch == ('@'|0x80) /* quoted $@ */ - && !(output->o_expflags & EXP_FLAG_SINGLEWORD) + && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ ) { while (1) { 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); n = o_save_ptr(output, n); } - } else { /* quoted $*: add as one word */ + } else { /* quoted $* (or v="$@" case): add as one word */ while (1) { o_addQstr(output, 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: /* */ /* "Empty variable", used to make "" etc to not disappear */ arg++; - ored_ch = 0x80; + cant_be_null = 0x80; break; #if ENABLE_HUSH_TICK case '`': /* `cmd */ - *p = '\0'; + *p = '\0'; /* replace trailing */ arg++; /* Can't just stuff it into output o_string, * 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 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 (!(first_ch & 0x80)) { /* unquoted $VAR */ 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]) { /* unquoted var's contents should be globbed, so don't escape */ int sv = output->o_expflags; +//TODO: make _caller_ set EXP_FLAG_ESC_GLOB_CHARS properly output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; n = expand_on_ifs(output, n, val); val = NULL; @@ -4929,7 +4937,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) o_addQstr(output, val); } 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) *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); debug_print_list("expand_vars_to_list[b]", output, n); } 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--; /* allow to reuse list[n] later without re-growth */ -- 2.25.1