From e298ce69baef029f3951dd1d5ed50fdbc6c55c80 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Sep 2010 19:52:44 +0200 Subject: [PATCH] hush: fix handling of backslashes in variable assignment Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/var_bash5.tests | 4 +- shell/hush.c | 43 +++++++++++-------- .../hush_test/hush-vars/var_unbackslash.right | 11 +++++ .../hush_test/hush-vars/var_unbackslash.tests | 23 ++++++++++ shell/match.c | 34 ++++++--------- 5 files changed, 74 insertions(+), 41 deletions(-) create mode 100644 shell/hush_test/hush-vars/var_unbackslash.right create mode 100755 shell/hush_test/hush-vars/var_unbackslash.tests diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests index 3f49321e1..7f482a554 100755 --- a/shell/ash_test/ash-vars/var_bash5.tests +++ b/shell/ash_test/ash-vars/var_bash5.tests @@ -1,5 +1,5 @@ -# This testcase checks whether slashes in ${v/a/b} are parsed before or after expansions -# in a part +# This testcase checks whether slashes in ${v/a/b} are parsed before +# or after expansions v='a/b/c' s='b/c' diff --git a/shell/hush.c b/shell/hush.c index e8dfb2499..d3dab5863 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -390,6 +390,7 @@ typedef struct o_string { smallint o_glob; /* At least some part of the string was inside '' or "", * possibly empty one: word"", wo''rd etc. */ +//TODO: rename to no_empty_expansion? smallint o_quoted; smallint has_empty_slot; smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ @@ -2018,11 +2019,10 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len { while (len) { o_addchr(o, *str); - if (*str++ == '\\' - && (*str != '*' && *str != '?' && *str != '[') - ) { + if (*str == '\\') { o_addchr(o, '\\'); } + str++; len--; } } @@ -2128,8 +2128,8 @@ static void debug_print_list(const char *prefix, o_string *o, int n) int i = 0; indent(); - fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", - prefix, list, n, string_start, o->length, o->maxlen); + fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", + prefix, list, n, string_start, o->length, o->maxlen, o->o_glob, o->o_quoted, o->o_escape); while (i < n) { indent(); fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], @@ -2563,9 +2563,9 @@ static char *expand_pseudo_dquoted(const char *str) struct in_str input; o_string dest = NULL_O_STRING; - if (strchr(str, '$') == NULL + if (!strchr(str, '$') #if ENABLE_HUSH_TICK - && strchr(str, '`') == NULL + && !strchr(str, '`') #endif ) { return NULL; @@ -2740,6 +2740,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } #endif default: { /* varname */ +//TODO: move to a subroutine? char *var; char first_char; char exp_op; @@ -3000,18 +3001,22 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char return n; } -static char **expand_variables(char **argv, int or_mask) +enum { + EXPVAR_FLAG_GLOB = 0x200, + EXPVAR_FLAG_ESCAPE_VARS = 0x100, + EXPVAR_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ +}; +static char **expand_variables(char **argv, unsigned or_mask) { int n; char **list; char **v; o_string output = NULL_O_STRING; - if (or_mask & 0x100) { - output.o_escape = 1; /* protect against globbing for "$var" */ - /* (unquoted $var will temporarily switch it off) */ - output.o_glob = 1; - } + /* protect against globbing for "$var"? */ + /* (unquoted $var will temporarily switch it off) */ + output.o_escape = 1 & (or_mask / EXPVAR_FLAG_ESCAPE_VARS); + output.o_glob = 1 & (or_mask / EXPVAR_FLAG_GLOB); n = 0; v = argv; @@ -3029,13 +3034,13 @@ static char **expand_variables(char **argv, int or_mask) static char **expand_strvec_to_strvec(char **argv) { - return expand_variables(argv, 0x100); + return expand_variables(argv, EXPVAR_FLAG_GLOB | EXPVAR_FLAG_ESCAPE_VARS); } #if ENABLE_HUSH_BASH_COMPAT static char **expand_strvec_to_strvec_singleword_noglob(char **argv) { - return expand_variables(argv, 0x80); + return expand_variables(argv, EXPVAR_FLAG_SINGLEWORD); } #endif @@ -3075,15 +3080,15 @@ static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv) #endif /* Used for expansion of right hand of assignments */ -/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs - * "v=/bin/c*" */ +/* NB: should NOT do globbing! + * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */ static char *expand_string_to_string(const char *str) { char *argv[2], **list; argv[0] = (char*)str; argv[1] = NULL; - list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */ + list = expand_variables(argv, EXPVAR_FLAG_ESCAPE_VARS | EXPVAR_FLAG_SINGLEWORD); if (HUSH_DEBUG) if (!list[0] || list[1]) bb_error_msg_and_die("BUG in varexp2"); @@ -3099,7 +3104,7 @@ static char* expand_strvec_to_string(char **argv) { char **list; - list = expand_variables(argv, 0x80); + list = expand_variables(argv, EXPVAR_FLAG_SINGLEWORD); /* Convert all NULs to spaces */ if (list[0]) { int n = 1; diff --git a/shell/hush_test/hush-vars/var_unbackslash.right b/shell/hush_test/hush-vars/var_unbackslash.right new file mode 100644 index 000000000..20c2ddf55 --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash.right @@ -0,0 +1,11 @@ +b1=-qwerty-t-\-"---z-*-?- +b1=-qwerty-t-\-"---z-*-?- +b2=-qwerty-\t-\-"-\--\z-\*-\?- +b2=-qwerty-\t-\-"-\--\z-\*-\?- +b3=-$a-\t-\\-\"-\--\z-\*-\?- +b3=-$a-\t-\\-\"-\--\z-\*-\?- +c=-$a-\t-\\-\"-\--\z-\*-\?- +c=-$a-\t-\\-\"-\--\z-\*-\?- +c=-$a-\t-\\-\"-\--\z-\*-\?- +c=-$a-\t-\\-\"-\--\z-\*-\?- +Done: 0 diff --git a/shell/hush_test/hush-vars/var_unbackslash.tests b/shell/hush_test/hush-vars/var_unbackslash.tests new file mode 100755 index 000000000..3c35d7df5 --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash.tests @@ -0,0 +1,23 @@ +# Test for correct handling of backslashes +a=qwerty + +b=-$a-\t-\\-\"-\--\z-\*-\?- +echo b1=$b +echo "b1=$b" +b="-$a-\t-\\-\"-\--\z-\*-\?-" +echo b2=$b +echo "b2=$b" +b='-$a-\t-\\-\"-\--\z-\*-\?-' +echo b3=$b +echo "b3=$b" + +c=$b +echo "c=$c" +c=${b} +echo "c=$c" +c="$b" +echo "c=$c" +c="${b}" +echo "c=$c" + +echo "Done: $?" diff --git a/shell/match.c b/shell/match.c index 8b1ddacd5..01b843918 100644 --- a/shell/match.c +++ b/shell/match.c @@ -31,26 +31,23 @@ char *scanleft(char *string, char *pattern, bool match_at_left) char c; char *loc = string; - do { + while (1) { int match; - const char *s; c = *loc; if (match_at_left) { *loc = '\0'; - s = string; - } else - s = loc; - match = pmatch(pattern, s); - *loc = c; - + match = pmatch(pattern, string); + *loc = c; + } else { + match = pmatch(pattern, loc); + } if (match) return loc; - + if (!c) + return NULL; loc++; - } while (c); - - return NULL; + } } char *scanright(char *string, char *pattern, bool match_at_left) @@ -60,20 +57,17 @@ char *scanright(char *string, char *pattern, bool match_at_left) while (loc >= string) { int match; - const char *s; c = *loc; if (match_at_left) { *loc = '\0'; - s = string; - } else - s = loc; - match = pmatch(pattern, s); - *loc = c; - + match = pmatch(pattern, string); + *loc = c; + } else { + match = pmatch(pattern, loc); + } if (match) return loc; - loc--; }