diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-09 14:38:46 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-09 14:38:46 +0200 |
commit | bfc02a76c5c3f0c8a5de518a46951098fef1e3cb (patch) | |
tree | b2c789c55069acf0023cb31ac86737678f5b8c1f | |
parent | 101a4e3e2170e5ffa2bd2b06d7a71088a0ab8958 (diff) | |
download | busybox-w32-bfc02a76c5c3f0c8a5de518a46951098fef1e3cb.tar.gz busybox-w32-bfc02a76c5c3f0c8a5de518a46951098fef1e3cb.tar.bz2 busybox-w32-bfc02a76c5c3f0c8a5de518a46951098fef1e3cb.zip |
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>
-rw-r--r-- | shell/hush.c | 44 |
1 files 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 | |||
4487 | /* Helper: | 4487 | /* Helper: |
4488 | * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. | 4488 | * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. |
4489 | */ | 4489 | */ |
4490 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp, char first_ch) | 4490 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) |
4491 | { | 4491 | { |
4492 | const char *val = NULL; | 4492 | const char *val = NULL; |
4493 | char *to_be_freed = NULL; | 4493 | char *to_be_freed = NULL; |
@@ -4498,11 +4498,13 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4498 | char exp_save = exp_save; /* for compiler */ | 4498 | char exp_save = exp_save; /* for compiler */ |
4499 | char *exp_saveptr; /* points to expansion operator */ | 4499 | char *exp_saveptr; /* points to expansion operator */ |
4500 | char *exp_word = exp_word; /* for compiler */ | 4500 | char *exp_word = exp_word; /* for compiler */ |
4501 | char arg0; | ||
4501 | 4502 | ||
4502 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ | 4503 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ |
4503 | var = arg; | 4504 | var = arg; |
4504 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | 4505 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; |
4505 | first_char = arg[0] = first_ch & 0x7f; | 4506 | arg0 = arg[0]; |
4507 | first_char = arg[0] = arg0 & 0x7f; | ||
4506 | exp_op = 0; | 4508 | exp_op = 0; |
4507 | 4509 | ||
4508 | if (first_char == '#' /* ${#... */ | 4510 | if (first_char == '#' /* ${#... */ |
@@ -4754,7 +4756,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4754 | *exp_saveptr = exp_save; | 4756 | *exp_saveptr = exp_save; |
4755 | } /* if (exp_op) */ | 4757 | } /* if (exp_op) */ |
4756 | 4758 | ||
4757 | arg[0] = first_ch; | 4759 | arg[0] = arg0; |
4758 | 4760 | ||
4759 | *pp = p; | 4761 | *pp = p; |
4760 | *to_be_freed_pp = to_be_freed; | 4762 | *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) | |||
4771 | /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in | 4773 | /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in |
4772 | * expansion of right-hand side of assignment == 1-element expand. | 4774 | * expansion of right-hand side of assignment == 1-element expand. |
4773 | */ | 4775 | */ |
4774 | char ored_ch; | 4776 | char cant_be_null = 0; /* only bit 0x80 matters */ |
4775 | char *p; | 4777 | char *p; |
4776 | 4778 | ||
4777 | ored_ch = 0; | ||
4778 | |||
4779 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, | 4779 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, |
4780 | !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); | 4780 | !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); |
4781 | debug_print_list("expand_vars_to_list", output, n); | 4781 | 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) | |||
4797 | arg = ++p; | 4797 | arg = ++p; |
4798 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 4798 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
4799 | 4799 | ||
4800 | /* Fetch special var name (if it is indeed one of them) | ||
4801 | * and quote bit, force the bit on if singleword expansion - | ||
4802 | * important for not getting v=$@ expand to many words. */ | ||
4800 | first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD); | 4803 | first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD); |
4801 | /* "$@" is special. Even if quoted, it can still | 4804 | |
4802 | * expand to nothing (not even an empty string) */ | 4805 | /* Is this variable quoted and thus expansion can't be null? |
4806 | * "$@" is special. Even if quoted, it can still | ||
4807 | * expand to nothing (not even an empty string), | ||
4808 | * thus it is excluded. */ | ||
4803 | if ((first_ch & 0x7f) != '@') | 4809 | if ((first_ch & 0x7f) != '@') |
4804 | ored_ch |= first_ch; | 4810 | cant_be_null |= first_ch; |
4805 | 4811 | ||
4806 | switch (first_ch & 0x7f) { | 4812 | switch (first_ch & 0x7f) { |
4807 | /* Highest bit in first_ch indicates that var is double-quoted */ | 4813 | /* 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) | |||
4811 | if (!G.global_argv[1]) | 4817 | if (!G.global_argv[1]) |
4812 | break; | 4818 | break; |
4813 | i = 1; | 4819 | i = 1; |
4814 | ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ | 4820 | cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ |
4815 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | 4821 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
4816 | int sv = output->o_expflags; | 4822 | int sv = output->o_expflags; |
4817 | /* unquoted var's contents should be globbed, so don't escape */ | 4823 | /* unquoted var's contents should be globbed, so don't escape */ |
4824 | //TODO: make _caller_ set EXP_FLAG_ESC_GLOB_CHARS properly | ||
4818 | output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; | 4825 | output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; |
4819 | while (G.global_argv[i]) { | 4826 | while (G.global_argv[i]) { |
4820 | n = expand_on_ifs(output, n, G.global_argv[i]); | 4827 | 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) | |||
4833 | /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' | 4840 | /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' |
4834 | * and in this case should treat it like '$*' - see 'else...' below */ | 4841 | * and in this case should treat it like '$*' - see 'else...' below */ |
4835 | if (first_ch == ('@'|0x80) /* quoted $@ */ | 4842 | if (first_ch == ('@'|0x80) /* quoted $@ */ |
4836 | && !(output->o_expflags & EXP_FLAG_SINGLEWORD) | 4843 | && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ |
4837 | ) { | 4844 | ) { |
4838 | while (1) { | 4845 | while (1) { |
4839 | o_addQstr(output, G.global_argv[i]); | 4846 | 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) | |||
4843 | debug_print_list("expand_vars_to_list[4]", output, n); | 4850 | debug_print_list("expand_vars_to_list[4]", output, n); |
4844 | n = o_save_ptr(output, n); | 4851 | n = o_save_ptr(output, n); |
4845 | } | 4852 | } |
4846 | } else { /* quoted $*: add as one word */ | 4853 | } else { /* quoted $* (or v="$@" case): add as one word */ |
4847 | while (1) { | 4854 | while (1) { |
4848 | o_addQstr(output, G.global_argv[i]); | 4855 | o_addQstr(output, G.global_argv[i]); |
4849 | if (!G.global_argv[++i]) | 4856 | if (!G.global_argv[++i]) |
@@ -4857,11 +4864,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4857 | case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ | 4864 | case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ |
4858 | /* "Empty variable", used to make "" etc to not disappear */ | 4865 | /* "Empty variable", used to make "" etc to not disappear */ |
4859 | arg++; | 4866 | arg++; |
4860 | ored_ch = 0x80; | 4867 | cant_be_null = 0x80; |
4861 | break; | 4868 | break; |
4862 | #if ENABLE_HUSH_TICK | 4869 | #if ENABLE_HUSH_TICK |
4863 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 4870 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
4864 | *p = '\0'; | 4871 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
4865 | arg++; | 4872 | arg++; |
4866 | /* Can't just stuff it into output o_string, | 4873 | /* Can't just stuff it into output o_string, |
4867 | * expanded result may need to be globbed | 4874 | * 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) | |||
4904 | } | 4911 | } |
4905 | #endif | 4912 | #endif |
4906 | default: | 4913 | default: |
4907 | val = expand_one_var(&to_be_freed, arg, &p, first_ch); | 4914 | val = expand_one_var(&to_be_freed, arg, &p); |
4908 | IF_HUSH_TICK(store_val:) | 4915 | IF_HUSH_TICK(store_val:) |
4909 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 4916 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
4910 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, | 4917 | 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) | |||
4912 | if (val && val[0]) { | 4919 | if (val && val[0]) { |
4913 | /* unquoted var's contents should be globbed, so don't escape */ | 4920 | /* unquoted var's contents should be globbed, so don't escape */ |
4914 | int sv = output->o_expflags; | 4921 | int sv = output->o_expflags; |
4922 | //TODO: make _caller_ set EXP_FLAG_ESC_GLOB_CHARS properly | ||
4915 | output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; | 4923 | output->o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; |
4916 | n = expand_on_ifs(output, n, val); | 4924 | n = expand_on_ifs(output, n, val); |
4917 | val = NULL; | 4925 | val = NULL; |
@@ -4929,7 +4937,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4929 | o_addQstr(output, val); | 4937 | o_addQstr(output, val); |
4930 | } | 4938 | } |
4931 | free(to_be_freed); | 4939 | free(to_be_freed); |
4932 | /* Do the check to avoid writing to a const string */ | 4940 | |
4941 | /* Restore NULL'ed SPECIAL_VAR_SYMBOL. | ||
4942 | * Do the check to avoid writing to a const string. */ | ||
4933 | if (*p != SPECIAL_VAR_SYMBOL) | 4943 | if (*p != SPECIAL_VAR_SYMBOL) |
4934 | *p = SPECIAL_VAR_SYMBOL; | 4944 | *p = SPECIAL_VAR_SYMBOL; |
4935 | 4945 | ||
@@ -4946,7 +4956,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4946 | o_addstr_with_NUL(output, arg); | 4956 | o_addstr_with_NUL(output, arg); |
4947 | debug_print_list("expand_vars_to_list[b]", output, n); | 4957 | debug_print_list("expand_vars_to_list[b]", output, n); |
4948 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ | 4958 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ |
4949 | && !(ored_ch & 0x80) /* and all vars were not quoted. */ | 4959 | && !(cant_be_null & 0x80) /* and all vars were not quoted. */ |
4950 | ) { | 4960 | ) { |
4951 | n--; | 4961 | n--; |
4952 | /* allow to reuse list[n] later without re-growth */ | 4962 | /* allow to reuse list[n] later without re-growth */ |