diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-06 10:26:37 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-06 10:26:37 +0200 |
commit | c49d2d97939d77be3d1f3bbbbf9db30a55771c15 (patch) | |
tree | 8cf0a9ad8fd7e0d9762684fef0a7a5a4f7d43859 | |
parent | d383b49aefecea99e5bfb2f9eb2956f1c6c013e1 (diff) | |
download | busybox-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>
-rw-r--r-- | shell/hush.c | 134 | ||||
-rw-r--r-- | shell/hush_test/hush-glob/glob2.right | 18 | ||||
-rwxr-xr-x | shell/hush_test/hush-glob/glob2.tests | 27 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_bash4.right | 25 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_bash4.tests | 52 |
5 files changed, 170 insertions, 86 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) | |||
2007 | static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) | 2007 | static 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 | ||
2070 | static void o_addQblock(o_string *o, const char *str, int len) | 2075 | static 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 | ||
2103 | static 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 | |||
2102 | static void o_addQstr(o_string *o, const char *str) | 2112 | static 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 | */ |
2359 | static int o_glob(o_string *o, int n) | 2369 | static 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 | */ |
2406 | static int o_glob(o_string *o, int n) | 2416 | static 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 | ||
4988 | static 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 | } |
diff --git a/shell/hush_test/hush-glob/glob2.right b/shell/hush_test/hush-glob/glob2.right new file mode 100644 index 000000000..7a70c2263 --- /dev/null +++ b/shell/hush_test/hush-glob/glob2.right | |||
@@ -0,0 +1,18 @@ | |||
1 | Expected Actual | ||
2 | Z\* : Z\* | ||
3 | Z* : Z* | ||
4 | Z\f : Z\f | ||
5 | Z\* : Z\* | ||
6 | |||
7 | Z\z : Z\z | ||
8 | Zz : Zz | ||
9 | Z\z : Z\z | ||
10 | Z\z : Z\z | ||
11 | |||
12 | Z\ : Z\ | ||
13 | Z\ : Z\ | ||
14 | |||
15 | Z\f Zf : Z\f Zf | ||
16 | Z\f Zf : Z\f Zf | ||
17 | |||
18 | Done: 0 | ||
diff --git a/shell/hush_test/hush-glob/glob2.tests b/shell/hush_test/hush-glob/glob2.tests new file mode 100755 index 000000000..4dbc92599 --- /dev/null +++ b/shell/hush_test/hush-glob/glob2.tests | |||
@@ -0,0 +1,27 @@ | |||
1 | # This test demonstrates that in unquoted $v, backslashes expand by this rule: | ||
2 | # \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not), | ||
3 | # and subsequently globbing converts \\ to \ and treats \z as literal z | ||
4 | # even if it is a special char. | ||
5 | |||
6 | >'Zf' | ||
7 | >'Z\f' | ||
8 | echo 'Expected' 'Actual' | ||
9 | v='\*'; echo 'Z\* :' Z$v | ||
10 | echo 'Z* :' Z\* | ||
11 | echo 'Z\f :' Z\\* | ||
12 | echo 'Z\* :' Z\\\* # NB! only this matches Z$v output | ||
13 | echo | ||
14 | v='\z'; echo 'Z\z :' Z$v | ||
15 | echo 'Zz :' Z\z | ||
16 | echo 'Z\z :' Z\\z | ||
17 | echo 'Z\z :' Z\\\z | ||
18 | echo | ||
19 | v='\'; echo 'Z\ :' Z$v | ||
20 | echo 'Z\ :' Z\\ | ||
21 | echo | ||
22 | v='*'; echo 'Z\f Zf :' Z$v | ||
23 | echo 'Z\f Zf :' Z* | ||
24 | echo | ||
25 | |||
26 | rm 'Z\f' 'Zf' | ||
27 | echo Done: $? | ||
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right index 600e8532f..0ef1bf661 100644 --- a/shell/hush_test/hush-vars/var_bash4.right +++ b/shell/hush_test/hush-vars/var_bash4.right | |||
@@ -1,23 +1,40 @@ | |||
1 | Source: a*b\*c | 1 | Source: a*b\*c |
2 | Replace str: _\\_\z_ | 2 | Replace str: _\\_\z_ |
3 | Pattern: single backslash and star: "replace literal star" | 3 | Pattern: single backslash and star: "replace literal star" |
4 | In assignment: a_\_z_b\*c | ||
5 | Unquoted: a_\_z_b\*c | 4 | Unquoted: a_\_z_b\*c |
5 | Unquoted =: a_\_z_b\*c | ||
6 | Quoted: a_\_\z_b\*c | 6 | Quoted: a_\_\z_b\*c |
7 | Quoted =: a_\_\z_b\*c | ||
7 | Pattern: double backslash and star: "replace backslash and everything after it" | 8 | Pattern: double backslash and star: "replace backslash and everything after it" |
8 | In assignment: a*b_\_z_ | ||
9 | Unquoted: a*b_\_z_ | 9 | Unquoted: a*b_\_z_ |
10 | Unquoted =: a*b_\_z_ | ||
10 | Quoted: a*b_\_\z_ | 11 | Quoted: a*b_\_\z_ |
12 | Quoted =: a*b_\_\z_ | ||
11 | 13 | ||
12 | Source: a\bc | 14 | Source: a\bc |
13 | Replace str: _\\_\z_ | 15 | Replace str: _\\_\z_ |
14 | Pattern: single backslash and b: "replace b" | 16 | Pattern: single backslash and b: "replace b" |
15 | In assignment: a\_\_z_c | ||
16 | Unquoted: a\_\_z_c | 17 | Unquoted: a\_\_z_c |
18 | Unquoted =: a\_\_z_c | ||
17 | Quoted: a\_\_\z_c | 19 | Quoted: a\_\_\z_c |
20 | Quoted =: a\_\_\z_c | ||
18 | Pattern: double backslash and b: "replace backslash and b" | 21 | Pattern: double backslash and b: "replace backslash and b" |
19 | In assignment: a_\_z_c | ||
20 | Unquoted: a_\_z_c | 22 | Unquoted: a_\_z_c |
23 | Unquoted =: a_\_z_c | ||
21 | Quoted: a_\_\z_c | 24 | Quoted: a_\_\z_c |
25 | Quoted =: a_\_\z_c | ||
26 | |||
27 | Source: a\bc | ||
28 | Replace str: _\\_\z_ (as variable $s) | ||
29 | Pattern: single backslash and b: "replace b" | ||
30 | Unquoted: a\_\\_\z_c | ||
31 | Unquoted =: a\_\\_\z_c | ||
32 | Quoted: a\_\\_\z_c | ||
33 | Quoted =: a\_\\_\z_c | ||
34 | Pattern: double backslash and b: "replace backslash and b" | ||
35 | Unquoted: a_\\_\z_c | ||
36 | Unquoted =: a_\\_\z_c | ||
37 | Quoted: a_\\_\z_c | ||
38 | Quoted =: a_\\_\z_c | ||
22 | 39 | ||
23 | Done: 0 | 40 | Done: 0 |
diff --git a/shell/hush_test/hush-vars/var_bash4.tests b/shell/hush_test/hush-vars/var_bash4.tests index d5470614b..32aa2b34c 100755 --- a/shell/hush_test/hush-vars/var_bash4.tests +++ b/shell/hush_test/hush-vars/var_bash4.tests | |||
@@ -6,23 +6,30 @@ | |||
6 | # even in quotes. | 6 | # even in quotes. |
7 | # | 7 | # |
8 | # bash4 (and probably bash3 too): "Quoted:" results are different from | 8 | # bash4 (and probably bash3 too): "Quoted:" results are different from |
9 | # unquoted and assignment expansions - they have a backslash before z. | 9 | # unquoted expansions - they have a backslash before z. |
10 | # | ||
11 | # The difference only exists if repl is a literal. If it is a variable: | ||
12 | # ${v/.../$s}, then all backslashes are preserved in both cases. | ||
10 | 13 | ||
11 | v='a*b\*c' | 14 | v='a*b\*c' |
12 | echo 'Source: ' "$v" | 15 | echo 'Source: ' "$v" |
13 | echo 'Replace str: ' '_\\_\z_' | 16 | echo 'Replace str: ' '_\\_\z_' |
14 | 17 | ||
15 | echo 'Pattern: ' 'single backslash and star: "replace literal star"' | 18 | echo 'Pattern: ' 'single backslash and star: "replace literal star"' |
16 | r=${v/\*/_\\_\z_} | ||
17 | echo 'In assignment:' "$r" | ||
18 | echo 'Unquoted: ' ${v/\*/_\\_\z_} | 19 | echo 'Unquoted: ' ${v/\*/_\\_\z_} |
20 | r=${v/\*/_\\_\z_} | ||
21 | echo 'Unquoted =: ' "$r" | ||
19 | echo 'Quoted: ' "${v/\*/_\\_\z_}" | 22 | echo 'Quoted: ' "${v/\*/_\\_\z_}" |
23 | r="${v/\*/_\\_\z_}" | ||
24 | echo 'Quoted =: ' "$r" | ||
20 | 25 | ||
21 | echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' | 26 | echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"' |
22 | r=${v/\\*/_\\_\z_} | ||
23 | echo 'In assignment:' "$r" | ||
24 | echo 'Unquoted: ' ${v/\\*/_\\_\z_} | 27 | echo 'Unquoted: ' ${v/\\*/_\\_\z_} |
28 | r=${v/\\*/_\\_\z_} | ||
29 | echo 'Unquoted =: ' "$r" | ||
25 | echo 'Quoted: ' "${v/\\*/_\\_\z_}" | 30 | echo 'Quoted: ' "${v/\\*/_\\_\z_}" |
31 | r="${v/\\*/_\\_\z_}" | ||
32 | echo 'Quoted =: ' "$r" | ||
26 | 33 | ||
27 | echo | 34 | echo |
28 | 35 | ||
@@ -31,16 +38,43 @@ echo 'Source: ' "$v" | |||
31 | echo 'Replace str: ' '_\\_\z_' | 38 | echo 'Replace str: ' '_\\_\z_' |
32 | 39 | ||
33 | echo 'Pattern: ' 'single backslash and b: "replace b"' | 40 | echo 'Pattern: ' 'single backslash and b: "replace b"' |
34 | r=${v/\b/_\\_\z_} | ||
35 | echo 'In assignment:' "$r" | ||
36 | echo 'Unquoted: ' ${v/\b/_\\_\z_} | 41 | echo 'Unquoted: ' ${v/\b/_\\_\z_} |
42 | r=${v/\b/_\\_\z_} | ||
43 | echo 'Unquoted =: ' "$r" | ||
37 | echo 'Quoted: ' "${v/\b/_\\_\z_}" | 44 | echo 'Quoted: ' "${v/\b/_\\_\z_}" |
45 | r="${v/\b/_\\_\z_}" | ||
46 | echo 'Quoted =: ' "$r" | ||
38 | 47 | ||
39 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' | 48 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' |
40 | r=${v/\\b/_\\_\z_} | ||
41 | echo 'In assignment:' "$r" | ||
42 | echo 'Unquoted: ' ${v/\\b/_\\_\z_} | 49 | echo 'Unquoted: ' ${v/\\b/_\\_\z_} |
50 | r=${v/\\b/_\\_\z_} | ||
51 | echo 'Unquoted =: ' "$r" | ||
43 | echo 'Quoted: ' "${v/\\b/_\\_\z_}" | 52 | echo 'Quoted: ' "${v/\\b/_\\_\z_}" |
53 | r="${v/\\b/_\\_\z_}" | ||
54 | echo 'Quoted =: ' "$r" | ||
55 | |||
56 | echo | ||
57 | |||
58 | v='a\bc' | ||
59 | s='_\\_\z_' | ||
60 | echo 'Source: ' "$v" | ||
61 | echo 'Replace str: ' "$s" '(as variable $s)' | ||
62 | |||
63 | echo 'Pattern: ' 'single backslash and b: "replace b"' | ||
64 | echo 'Unquoted: ' ${v/\b/$s} | ||
65 | r=${v/\b/$s} | ||
66 | echo 'Unquoted =: ' "$r" | ||
67 | echo 'Quoted: ' "${v/\b/$s}" | ||
68 | r="${v/\b/$s}" | ||
69 | echo 'Quoted =: ' "$r" | ||
70 | |||
71 | echo 'Pattern: ' 'double backslash and b: "replace backslash and b"' | ||
72 | echo 'Unquoted: ' ${v/\\b/$s} | ||
73 | r=${v/\\b/$s} | ||
74 | echo 'Unquoted =: ' "$r" | ||
75 | echo 'Quoted: ' "${v/\\b/$s}" | ||
76 | r="${v/\\b/$s}" | ||
77 | echo 'Quoted =: ' "$r" | ||
44 | 78 | ||
45 | echo | 79 | echo |
46 | 80 | ||