aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-06 10:26:37 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-06 10:26:37 +0200
commitc49d2d97939d77be3d1f3bbbbf9db30a55771c15 (patch)
tree8cf0a9ad8fd7e0d9762684fef0a7a5a4f7d43859 /shell/hush.c
parentd383b49aefecea99e5bfb2f9eb2956f1c6c013e1 (diff)
downloadbusybox-w32-c49d2d97939d77be3d1f3bbbbf9db30a55771c15.tar.gz
busybox-w32-c49d2d97939d77be3d1f3bbbbf9db30a55771c15.tar.bz2
busybox-w32-c49d2d97939d77be3d1f3bbbbf9db30a55771c15.zip
hush: fix globbing+backslashes in unquoted $var expansion
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c134
1 files changed, 61 insertions, 73 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 2a4e80b6e..ef46372de 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -2007,12 +2007,17 @@ static void o_addstr_with_NUL(o_string *o, const char *str)
2007static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) 2007static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
2008{ 2008{
2009 while (len) { 2009 while (len) {
2010 len--;
2010 o_addchr(o, *str); 2011 o_addchr(o, *str);
2011 if (*str == '\\') { 2012 if (*str++ == '\\') {
2013 /* \z -> \\\z; \<eol> -> \\<eol> */
2012 o_addchr(o, '\\'); 2014 o_addchr(o, '\\');
2015 if (len) {
2016 len--;
2017 o_addchr(o, '\\');
2018 o_addchr(o, *str++);
2019 }
2013 } 2020 }
2014 str++;
2015 len--;
2016 } 2021 }
2017} 2022}
2018 2023
@@ -2067,12 +2072,8 @@ static void o_addQchr(o_string *o, int ch)
2067 o->data[o->length] = '\0'; 2072 o->data[o->length] = '\0';
2068} 2073}
2069 2074
2070static void o_addQblock(o_string *o, const char *str, int len) 2075static void o_addqblock(o_string *o, const char *str, int len)
2071{ 2076{
2072 if (!o->o_escape) {
2073 o_addblock(o, str, len);
2074 return;
2075 }
2076 while (len) { 2077 while (len) {
2077 char ch; 2078 char ch;
2078 int sz; 2079 int sz;
@@ -2099,6 +2100,15 @@ static void o_addQblock(o_string *o, const char *str, int len)
2099 } 2100 }
2100} 2101}
2101 2102
2103static void o_addQblock(o_string *o, const char *str, int len)
2104{
2105 if (!o->o_escape) {
2106 o_addblock(o, str, len);
2107 return;
2108 }
2109 o_addqblock(o, str, len);
2110}
2111
2102static void o_addQstr(o_string *o, const char *str) 2112static void o_addQstr(o_string *o, const char *str)
2103{ 2113{
2104 o_addQblock(o, str, strlen(str)); 2114 o_addQblock(o, str, strlen(str));
@@ -2356,11 +2366,11 @@ static int glob_brace(char *pattern, o_string *o, int n)
2356/* Performs globbing on last list[], 2366/* Performs globbing on last list[],
2357 * saving each result as a new list[]. 2367 * saving each result as a new list[].
2358 */ 2368 */
2359static int o_glob(o_string *o, int n) 2369static int perform_glob(o_string *o, int n)
2360{ 2370{
2361 char *pattern, *copy; 2371 char *pattern, *copy;
2362 2372
2363 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2373 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2364 if (!o->data) 2374 if (!o->data)
2365 return o_save_ptr_helper(o, n); 2375 return o_save_ptr_helper(o, n);
2366 pattern = o->data + o_get_last_ptr(o, n); 2376 pattern = o->data + o_get_last_ptr(o, n);
@@ -2378,7 +2388,7 @@ static int o_glob(o_string *o, int n)
2378 n = glob_brace(copy, o, n); 2388 n = glob_brace(copy, o, n);
2379 free(copy); 2389 free(copy);
2380 if (DEBUG_GLOB) 2390 if (DEBUG_GLOB)
2381 debug_print_list("o_glob returning", o, n); 2391 debug_print_list("perform_glob returning", o, n);
2382 return n; 2392 return n;
2383} 2393}
2384 2394
@@ -2403,13 +2413,13 @@ static int glob_needed(const char *s)
2403/* Performs globbing on last list[], 2413/* Performs globbing on last list[],
2404 * saving each result as a new list[]. 2414 * saving each result as a new list[].
2405 */ 2415 */
2406static int o_glob(o_string *o, int n) 2416static int perform_glob(o_string *o, int n)
2407{ 2417{
2408 glob_t globdata; 2418 glob_t globdata;
2409 int gr; 2419 int gr;
2410 char *pattern; 2420 char *pattern;
2411 2421
2412 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2422 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2413 if (!o->data) 2423 if (!o->data)
2414 return o_save_ptr_helper(o, n); 2424 return o_save_ptr_helper(o, n);
2415 pattern = o->data + o_get_last_ptr(o, n); 2425 pattern = o->data + o_get_last_ptr(o, n);
@@ -2455,7 +2465,7 @@ static int o_glob(o_string *o, int n)
2455 } 2465 }
2456 globfree(&globdata); 2466 globfree(&globdata);
2457 if (DEBUG_GLOB) 2467 if (DEBUG_GLOB)
2458 debug_print_list("o_glob returning", o, n); 2468 debug_print_list("perform_glob returning", o, n);
2459 return n; 2469 return n;
2460} 2470}
2461 2471
@@ -2470,7 +2480,7 @@ static int o_save_ptr(o_string *o, int n)
2470 * (if it was requested back then when it was filled) 2480 * (if it was requested back then when it was filled)
2471 * so don't do that again! */ 2481 * so don't do that again! */
2472 if (!o->has_empty_slot) 2482 if (!o->has_empty_slot)
2473 return o_glob(o, n); /* o_save_ptr_helper is inside */ 2483 return perform_glob(o, n); /* o_save_ptr_helper is inside */
2474 } 2484 }
2475 return o_save_ptr_helper(o, n); 2485 return o_save_ptr_helper(o, n);
2476} 2486}
@@ -2927,15 +2937,6 @@ static int done_word(o_string *word, struct parse_context *ctx)
2927 (ctx->ctx_res_w == RES_SNTX)); 2937 (ctx->ctx_res_w == RES_SNTX));
2928 return (ctx->ctx_res_w == RES_SNTX); 2938 return (ctx->ctx_res_w == RES_SNTX);
2929 } 2939 }
2930# ifdef CMD_SINGLEWORD_NOGLOB_COND
2931 if (strcmp(word->data, "export") == 0
2932# if ENABLE_HUSH_LOCAL
2933 || strcmp(word->data, "local") == 0
2934# endif
2935 ) {
2936 command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND;
2937 } else
2938# endif
2939# if ENABLE_HUSH_BASH_COMPAT 2940# if ENABLE_HUSH_BASH_COMPAT
2940 if (strcmp(word->data, "[[") == 0) { 2941 if (strcmp(word->data, "[[") == 0) {
2941 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 2942 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
@@ -4371,10 +4372,19 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
4371 while (1) { 4372 while (1) {
4372 int word_len = strcspn(str, G.ifs); 4373 int word_len = strcspn(str, G.ifs);
4373 if (word_len) { 4374 if (word_len) {
4374 if (output->o_escape || !output->o_glob) 4375 if (output->o_escape)
4375 o_addQblock(output, str, word_len); 4376 o_addqblock(output, str, word_len);
4376 else /* protect backslashes against globbing up :) */ 4377 else if (!output->o_glob)
4378 o_addblock(output, str, word_len);
4379 else /* if (!escape && glob) */ {
4380 /* Protect backslashes against globbing up :)
4381 * Example: "v='\*'; echo b$v"
4382 */
4377 o_addblock_duplicate_backslash(output, str, word_len); 4383 o_addblock_duplicate_backslash(output, str, word_len);
4384 /*/ Why can't we do it easier? */
4385 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
4386 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
4387 }
4378 str += word_len; 4388 str += word_len;
4379 } 4389 }
4380 if (!*str) /* EOL - do not finalize word */ 4390 if (!*str) /* EOL - do not finalize word */
@@ -4594,8 +4604,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
4594 if (exp_op == *exp_word) /* ## or %% */ 4604 if (exp_op == *exp_word) /* ## or %% */
4595 exp_word++; 4605 exp_word++;
4596//TODO: avoid xstrdup unless needed 4606//TODO: avoid xstrdup unless needed
4597// (see HACK ALERT below) 4607// (see HACK ALERT below for an example)
4598 val = to_be_freed = xstrdup(val); 4608 val = to_be_freed = xstrdup(val);
4609//TODO: fix expansion rules:
4599 exp_exp_word = expand_pseudo_dquoted(exp_word); 4610 exp_exp_word = expand_pseudo_dquoted(exp_word);
4600 if (exp_exp_word) 4611 if (exp_exp_word)
4601 exp_word = exp_exp_word; 4612 exp_word = exp_exp_word;
@@ -4613,10 +4624,26 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
4613 } 4624 }
4614#if ENABLE_HUSH_BASH_COMPAT 4625#if ENABLE_HUSH_BASH_COMPAT
4615 else if (exp_op == '/' || exp_op == '\\') { 4626 else if (exp_op == '/' || exp_op == '\\') {
4627 /* It's ${var/[/]pattern[/repl]} thing.
4628 * Note that in encoded form it has TWO parts:
4629 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
4630 */
4616 /* Empty variable always gives nothing: */ 4631 /* Empty variable always gives nothing: */
4617 // "v=''; echo ${v/*/w}" prints "" 4632 // "v=''; echo ${v/*/w}" prints "", not "w"
4618 if (val && val[0]) { 4633 if (val && val[0]) {
4619 /* It's ${var/[/]pattern[/repl]} thing */ 4634 /* It's ${var/[/]pattern[/repl]} thing */
4635 /*
4636 * Pattern is taken literally, while
4637 * repl should be de-backslased and globbed
4638 * by the usual expansion rules:
4639 * >az; >bz;
4640 * v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
4641 * v='a bz'; echo "${v/a*z/\z}" prints "\z"
4642 * v='a bz'; echo ${v/a*z/a*z} prints "az"
4643 * v='a bz'; echo ${v/a*z/\z} prints "z"
4644 * (note that a*z _pattern_ is never globbed!)
4645 */
4646//TODO: fix expansion rules:
4620 char *pattern, *repl, *t; 4647 char *pattern, *repl, *t;
4621 pattern = expand_pseudo_dquoted(exp_word); 4648 pattern = expand_pseudo_dquoted(exp_word);
4622 if (!pattern) 4649 if (!pattern)
@@ -4772,7 +4799,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
4772 4799
4773 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { 4800 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
4774 char first_ch; 4801 char first_ch;
4775 int i;
4776 char *to_be_freed = NULL; 4802 char *to_be_freed = NULL;
4777 const char *val = NULL; 4803 const char *val = NULL;
4778#if ENABLE_HUSH_TICK 4804#if ENABLE_HUSH_TICK
@@ -4795,10 +4821,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
4795 switch (first_ch & 0x7f) { 4821 switch (first_ch & 0x7f) {
4796 /* Highest bit in first_ch indicates that var is double-quoted */ 4822 /* Highest bit in first_ch indicates that var is double-quoted */
4797 case '*': 4823 case '*':
4798 case '@': 4824 case '@': {
4799 i = 1; 4825 int i;
4800 if (!G.global_argv[i]) 4826 if (!G.global_argv[1])
4801 break; 4827 break;
4828 i = 1;
4802 ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ 4829 ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
4803 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ 4830 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
4804 smallint sv = output->o_escape; 4831 smallint sv = output->o_escape;
@@ -4839,6 +4866,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
4839 } 4866 }
4840 } 4867 }
4841 break; 4868 break;
4869 }
4842 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ 4870 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
4843 /* "Empty variable", used to make "" etc to not disappear */ 4871 /* "Empty variable", used to make "" etc to not disappear */
4844 arg++; 4872 arg++;
@@ -4984,41 +5012,6 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
4984} 5012}
4985#endif 5013#endif
4986 5014
4987#ifdef CMD_SINGLEWORD_NOGLOB_COND
4988static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv)
4989{
4990 int n;
4991 char **list;
4992 char **v;
4993 o_string output = NULL_O_STRING;
4994
4995 n = 0;
4996 v = argv;
4997 while (*v) {
4998 int is_var = is_well_formed_var_name(*v, '=');
4999 /* is_var * 0x80: singleword expansion for vars */
5000 n = expand_vars_to_list(&output, n, *v, is_var * 0x80);
5001
5002 /* Subtle! expand_vars_to_list did not glob last word yet.
5003 * It does this only when fed with further data.
5004 * Therefore we set globbing flags AFTER it, not before:
5005 */
5006
5007 /* if it is not recognizably abc=...; then: */
5008 output.o_escape = !is_var; /* protect against globbing for "$var" */
5009 /* (unquoted $var will temporarily switch it off) */
5010 output.o_glob = !is_var; /* and indeed do globbing */
5011 v++;
5012 }
5013 debug_print_list("expand_cond", &output, n);
5014
5015 /* output.data (malloced in one block) gets returned in "list" */
5016 list = o_finalize_list(&output, n);
5017 debug_print_strings("expand_cond[1]", list);
5018 return list;
5019}
5020#endif
5021
5022/* Used for expansion of right hand of assignments */ 5015/* Used for expansion of right hand of assignments */
5023/* NB: should NOT do globbing! 5016/* NB: should NOT do globbing!
5024 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */ 5017 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */
@@ -6567,11 +6560,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
6567 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 6560 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
6568 } 6561 }
6569#endif 6562#endif
6570#ifdef CMD_SINGLEWORD_NOGLOB_COND
6571 else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB_COND) {
6572 argv_expanded = expand_strvec_to_strvec_singleword_noglob_cond(argv + command->assignment_cnt);
6573 }
6574#endif
6575 else { 6563 else {
6576 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 6564 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
6577 } 6565 }