From 0da6c813e13eaf1cddfd7cbee1f3a5a4ffe58ec0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 11 Aug 2025 17:31:36 +0200 Subject: hush: fix var_backslash1.tests function old new delta o_addqblock - 131 +131 append_str_maybe_ifs_split 53 100 +47 expand_one_var 1872 1897 +25 encode_then_expand_vararg 380 399 +19 sig_unblock 41 43 +2 sig_block 41 40 -1 sigprocmask_allsigs 33 31 -2 expand_vars_to_list 1080 1077 -3 wait_for_child_or_signal 202 193 -9 o_addQstr 175 42 -133 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 4/5 up/down: 224/-148) Total: 76 bytes Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/var_backslash1.right | 9 ++ shell/ash_test/ash-vars/var_backslash1.tests | 10 ++ shell/hush.c | 168 +++++++++++++++---------- shell/hush_test/hush-bugs/var_backslash1.right | 26 ---- shell/hush_test/hush-bugs/var_backslash1.tests | 38 ------ shell/hush_test/hush-vars/var_backslash1.right | 35 ++++++ shell/hush_test/hush-vars/var_backslash1.tests | 48 +++++++ 7 files changed, 202 insertions(+), 132 deletions(-) delete mode 100644 shell/hush_test/hush-bugs/var_backslash1.right delete mode 100755 shell/hush_test/hush-bugs/var_backslash1.tests create mode 100644 shell/hush_test/hush-vars/var_backslash1.right create mode 100755 shell/hush_test/hush-vars/var_backslash1.tests (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right index f384da1f5..3f2c21289 100644 --- a/shell/ash_test/ash-vars/var_backslash1.right +++ b/shell/ash_test/ash-vars/var_backslash1.right @@ -14,6 +14,15 @@ ${a##\\"$c"} removes \*: |bc| - matches \, then * ${a##$b} removes \: |*bc| - matches \ ${a##"$b"} removes \: |*bc| - matches \ +Single quote tests: +${a##?'*'} removes \*: |bc| - matches one char, then * +${a##'\'*} removes everything: || - matches \, then all +${a##'\'\*} removes \*: |bc| - matches \, then * +${a##'\*'} removes \*: |bc| - matches \, then * +${a##'\'$c} removes everything: || - matches \, then all +${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $ +${a##'\'"$c"} removes \*: |bc| - matches \, then * + ${a##"$b"?} removes \*: |bc| - matches \, then one char ${a##"$b"*} removes everything: || - matches \, then all ${a##"$b""?"} removes nothing: |\*bc| - second char is not ? diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests index 7eea3cc19..015a6107b 100755 --- a/shell/ash_test/ash-vars/var_backslash1.tests +++ b/shell/ash_test/ash-vars/var_backslash1.tests @@ -17,6 +17,16 @@ echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' echo +sq="'" +echo 'Single quote tests:' +echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' +echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' +echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' +echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' +echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' +echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' +echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' +echo ## In bash, this isn't working as expected #echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| #echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| diff --git a/shell/hush.c b/shell/hush.c index a0601b2a3..73d2ee7f1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -559,11 +559,15 @@ typedef struct o_string { smallint ended_in_ifs; } o_string; enum { - EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ - EXP_FLAG_GLOB = 0x2, - /* Protect newly added chars against globbing - * by prepending \ to *, ?, [, \ */ - EXP_FLAG_ESC_GLOB_CHARS = 0x1, + /* Protect all newly added chars against globbing by prepending \ to *, ?, [, -, \ */ + EXP_FLAG_GLOBPROTECT_CHARS = 0x1, + /* Protect quoted vars against globbing */ + EXP_FLAG_GLOBPROTECT_VARS = 0x2, + /* On word-split, perform globbing (one word may become many) */ + EXP_FLAG_DO_GLOBBING = 0x4, + /* Do not word-split */ + EXP_FLAG_SINGLEWORD = 0x80, + /* ^^^^ EXP_FLAG_SINGLEWORD must be 0x80 */ }; /* Used for initialization: o_string foo = NULL_O_STRING; */ #define NULL_O_STRING { NULL } @@ -3193,7 +3197,7 @@ static void o_addqchr(o_string *o, int ch) static void o_addQchr(o_string *o, int ch) { int sz = 1; - if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) + if ((o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) && strchr("*?[-\\" MAYBE_BRACES, ch) ) { sz++; @@ -3236,7 +3240,7 @@ static void o_addqblock(o_string *o, const char *str, int len) static void o_addQblock(o_string *o, const char *str, int len) { - if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) { + if (!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)) { o_addblock(o, str, len); return; } @@ -3248,6 +3252,11 @@ static void o_addQstr(o_string *o, const char *str) o_addQblock(o, str, strlen(str)); } +static void o_addqstr(o_string *o, const char *str) +{ + o_addqblock(o, str, strlen(str)); +} + /* A special kind of o_string for $VAR and `cmd` expansion. * It contains char* list[] at the beginning, which is grown in 16 element * increments. Actual string data starts at the next multiple of 16 * (char*). @@ -3266,11 +3275,11 @@ static void debug_print_list(const char *prefix, o_string *o, int n) int i = 0; indent(); - fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", + fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d do_glob:%d has_quoted:%d globprotect:%d\n", prefix, list, n, string_start, o->length, o->maxlen, - !!(o->o_expflags & EXP_FLAG_GLOB), + !!(o->o_expflags & EXP_FLAG_DO_GLOBBING), o->has_quoted_part, - !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); + !!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); while (i < n) { indent(); fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], @@ -3672,11 +3681,11 @@ static int perform_glob(o_string *o, int n) #endif /* !HUSH_BRACE_EXPANSION */ -/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. +/* If o->o_expflags & EXP_FLAG_DO_GLOBBING, glob the string so far remembered. * Otherwise, just finish current list[] and start new */ static int o_save_ptr(o_string *o, int n) { - if (o->o_expflags & EXP_FLAG_GLOB) { + if (o->o_expflags & EXP_FLAG_DO_GLOBBING) { /* If o->has_empty_slot, list[n] was already globbed * (if it was requested back then when it was filled) * so don't do that again! */ @@ -5476,8 +5485,8 @@ static int encode_string(o_string *as_string, if (ch != '\n') { next = i_peek(input); } - debug_printf_parse("\" ch=%c (%d) escape=%d\n", - ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); + debug_printf_parse("\" ch:%c (%d) globprotect:%d\n", + ch, ch, !!(dest->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); if (ch == '\\') { if (next == EOF) { /* Testcase: in interactive shell a file with @@ -5581,8 +5590,8 @@ static struct pipe *parse_stream(char **pstring, redir_type redir_style; ch = i_getch(input); - debug_printf_parse(": ch=%c (%d) escape=%d\n", - ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); + debug_printf_parse(": ch:%c (%d) globprotect:%d\n", + ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); if (ch == EOF) { struct pipe *pi; @@ -6011,10 +6020,10 @@ static struct pipe *parse_stream(char **pstring, continue; /* get next char */ } if (ctx.is_assignment == NOT_ASSIGNMENT) - ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; + ctx.word.o_expflags |= EXP_FLAG_GLOBPROTECT_CHARS; if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) goto parse_error_exitcode1; - ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; + ctx.word.o_expflags &= ~EXP_FLAG_GLOBPROTECT_CHARS; continue; /* get next char */ #if ENABLE_HUSH_TICK case '`': { @@ -6183,10 +6192,6 @@ static struct pipe *parse_stream(char **pstring, * Execution routines */ /* Expansion can recurse, need forward decls: */ -#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE -#define expand_string_to_string(str, EXP_flags, do_unbackslash) \ - expand_string_to_string(str) -#endif static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); #if ENABLE_HUSH_TICK static int process_command_subs(o_string *dest, const char *s); @@ -6251,7 +6256,7 @@ static int expand_on_ifs(o_string *output, int n, const char *str) word_len = strcspn(str, G.ifs); if (word_len) { /* We have WORD_LEN leading non-IFS chars */ - if (!(output->o_expflags & EXP_FLAG_GLOB)) { + if (!(output->o_expflags & EXP_FLAG_DO_GLOBBING)) { o_addblock(output, str, word_len); } else { /* Protect backslashes against globbing up :) @@ -6344,7 +6349,7 @@ static char *encode_then_expand_string(const char *str) //TODO: error check (encode_string returns 0 on error)? //bb_error_msg("'%s' -> '%s'", str, dest.data); exp_str = expand_string_to_string(dest.data, - EXP_FLAG_ESC_GLOB_CHARS, + EXP_FLAG_GLOBPROTECT_CHARS, /*unbackslash:*/ 1 ); //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); @@ -6379,15 +6384,11 @@ static const char *first_special_char_in_vararg(const char *cp) * a dquoted string: "${var#"zz"}"), the difference only comes later * (word splitting and globbing of the ${var...} result). */ -#if !BASH_PATTERN_SUBST -#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \ - encode_then_expand_vararg(str, handle_squotes) -#endif -static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash) -{ -#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE - const int do_unbackslash = 0; -#endif +static char *encode_then_expand_vararg(const char *str, + int handle_squotes, /* 'str' substrings are parsed as literals (unless inside "")*/ + int protect_vars, /* glob-protect double-quoted $VARS */ + int do_unbackslash /* run unbackslash on result before returning it */ +) { char *exp_str; struct in_str input; o_string dest = NULL_O_STRING; @@ -6422,7 +6423,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int goto ret; /* error */ } if (ch == '"') { - dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; + dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS; continue; } if (ch == '\\') { @@ -6438,7 +6439,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int if (ch == '$') { if (parse_dollar_squote(NULL, &dest, &input)) continue; - if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { + if (!parse_dollar(NULL, &dest, &input, + /*quote_mask:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) ? 0x80 : 0 + ) + ) { debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); goto ret; } @@ -6450,7 +6454,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, 0x80 | '`'); if (!add_till_backquote(&dest, &input, - /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ + /*in_dquote:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) ) ) { goto ret; /* error */ @@ -6463,9 +6467,12 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int o_addQchr(&dest, ch); } /* for (;;) */ - debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data); + debug_printf_parse("encode(do_unbackslash:%d): '%s' -> '%s'\n", do_unbackslash, str, dest.data); exp_str = expand_string_to_string(dest.data, - do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, + 0 + | (do_unbackslash ? EXP_FLAG_GLOBPROTECT_CHARS : 0) + | (protect_vars ? EXP_FLAG_GLOBPROTECT_VARS : 0) + , do_unbackslash ); ret: @@ -6549,7 +6556,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, goto ret; /* error */ } if (ch == '"') { - dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; + dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS; if (dest.o_expflags) { o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, SPECIAL_VAR_SYMBOL); @@ -6579,7 +6586,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); if (!add_till_backquote(&dest, &input, - /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ + /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_GLOBPROTECT_CHARS set */ ) ) { goto ret; /* error */ @@ -6707,27 +6714,39 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c #endif /* BASH_PATTERN_SUBST */ static int append_str_maybe_ifs_split(o_string *output, int n, - int first_ch, const char *val) + int quoted, const char *val) { - if (!(first_ch & 0x80)) { /* unquoted $VAR */ - debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, - !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); - if (val && val[0]) + if (!quoted) { /* unquoted $VAR */ + debug_printf_expand("unquoted variable value '%s', o_escape:%d o_varescape:%d, singleword:%d\n", val, + !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS), + !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS), + !!(output->o_expflags & EXP_FLAG_SINGLEWORD) + ); + if (val && val[0]) { + if (output->o_expflags & EXP_FLAG_SINGLEWORD) + goto singleword; n = expand_on_ifs(output, n, val); + } } else { /* quoted "$VAR" */ output->has_quoted_part = 1; - debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, - !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); - if (val && val[0]) - o_addQstr(output, val); + debug_printf_expand("quoted variable value '%s', o_escape:%d o_varescape:%d\n", val, + !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS), + !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS) + ); + if (val && val[0]) { + if (output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS) + o_addqstr(output, val); + else + singleword: + o_addQstr(output, val); + } } return n; } /* Handle varname... construct. */ -static NOINLINE int expand_one_var(o_string *output, int n, - int first_ch, char *arg, char **pp) +static NOINLINE int expand_one_var(o_string *output, int n, char *arg, char **pp) { const char *val; char *to_be_freed; @@ -6863,7 +6882,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, if (exp_op == *exp_word) /* ## or %% */ exp_word++; debug_printf_expand("expand: exp_word:'%s'\n", exp_word); - exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); + exp_exp_word = encode_then_expand_vararg(exp_word, + /*handle_squotes:*/ 1, /* 'str' are processed (and glob-protected) */ + /*globprotect_vars:*/ 1, /* value of "$VAR" is not glob-expanded */ + /*unbackslash:*/ 0 + ); if (exp_exp_word) exp_word = exp_exp_word; debug_printf_expand("expand: exp_word:'%s'\n", exp_word); @@ -6910,7 +6933,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, * (note that a*z _pattern_ is never globbed!) */ char *pattern, *repl, *t; - pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); + pattern = encode_then_expand_vararg(exp_word, + /*handle_squotes:*/ 1, + /*globprotect_vars:*/ 0, + /*unbackslash:*/ 0 + ); if (!pattern) pattern = xstrdup(exp_word); debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); @@ -6918,7 +6945,11 @@ static NOINLINE int expand_one_var(o_string *output, int n, exp_word = p; p = strchr(p, SPECIAL_VAR_SYMBOL); *p = '\0'; - repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1); + repl = encode_then_expand_vararg(exp_word, + /*handle_squotes:*/ 1, + /*globprotect_vars:*/ 0, + /*unbackslash:*/ 1 + ); debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); /* HACK ALERT. We depend here on the fact that * G.global_argv and results of utoa and get_local_var_value @@ -7071,6 +7102,7 @@ static NOINLINE int expand_one_var(o_string *output, int n, /* ${var=word} - assign and use default value */ to_be_freed = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ !(arg0 & 0x80), + /*globprotect_vars:*/ 0, /*unbackslash:*/ 0 ); if (to_be_freed) @@ -7115,7 +7147,7 @@ static NOINLINE int expand_one_var(o_string *output, int n, arg[0] = arg0; *pp = p; - n = append_str_maybe_ifs_split(output, n, first_ch, val); + n = append_str_maybe_ifs_split(output, n, /*quoted:*/ (arg0 & 0x80), val); free(to_be_freed); return n; @@ -7243,7 +7275,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) G.last_exitcode = process_command_subs(&subst_result, arg); G.expand_exitcode = G.last_exitcode; debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); - n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data); + n = append_str_maybe_ifs_split(output, n, /*quoted:*/(first_ch & 0x80), subst_result.data); o_free(&subst_result); break; } @@ -7261,7 +7293,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) sprintf(arith_buf, ARITH_FMT, res); if (res < 0 && first_ch == (char)('+'|0x80) - /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */ + /* && (output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) */ ) { /* Quoted negative ariths, like filename[0"$((-9))"], * should not be interpreted as glob ranges. @@ -7276,7 +7308,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) #endif default: /* varname[ops] */ - n = expand_one_var(output, n, first_ch, arg, &p); + n = expand_one_var(output, n, arg, &p); break; } /* switch (char after ) */ @@ -7344,7 +7376,7 @@ static char **expand_variables(char **argv, unsigned expflags) static char **expand_strvec_to_strvec(char **argv) { - return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); + return expand_variables(argv, EXP_FLAG_DO_GLOBBING | EXP_FLAG_GLOBPROTECT_CHARS); } #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) @@ -7362,10 +7394,6 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv) */ static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) { -#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE - const int do_unbackslash = 1; - const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS; -#endif char *argv[2], **list; debug_printf_expand("string_to_string<='%s'\n", str); @@ -7434,7 +7462,7 @@ static char **expand_assignments(char **argv, int count) for (i = 0; i < count; i++) { p = add_string_to_strings(p, expand_string_to_string(argv[i], - EXP_FLAG_ESC_GLOB_CHARS, + EXP_FLAG_GLOBPROTECT_CHARS, /*unbackslash:*/ 1 ) ); @@ -8231,7 +8259,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) } mode = redir_table[redir->rd_type].mode; p = expand_string_to_string(redir->rd_filename, - EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); + EXP_FLAG_GLOBPROTECT_CHARS, + /*unbackslash:*/ 1 + ); newfd = open_or_warn(p, mode); free(p); if (newfd < 0) { @@ -9547,7 +9577,7 @@ static NOINLINE int run_pipe(struct pipe *pi) i = 0; while (i < command->assignment_cnt) { char *p = expand_string_to_string(argv[i], - EXP_FLAG_ESC_GLOB_CHARS, + EXP_FLAG_GLOBPROTECT_CHARS, /*unbackslash:*/ 1 ); #if ENABLE_HUSH_MODE_X @@ -10068,7 +10098,9 @@ static int run_list(struct pipe *pi) if (rword == RES_CASE) { debug_printf_exec("CASE cond_code:%d\n", cond_code); case_word = expand_string_to_string(pi->cmds->argv[0], - EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); + EXP_FLAG_GLOBPROTECT_CHARS, + /*unbackslash:*/ 1 + ); debug_printf_exec("CASE word1:'%s'\n", case_word); //unbackslash(case_word); //debug_printf_exec("CASE word2:'%s'\n", case_word); @@ -10086,7 +10118,7 @@ static int run_list(struct pipe *pi) char *pattern; debug_printf_exec("expand_string_to_string('%s')\n", *argv); pattern = expand_string_to_string(*argv, - EXP_FLAG_ESC_GLOB_CHARS, + EXP_FLAG_GLOBPROTECT_CHARS, /*unbackslash:*/ 0 ); /* TODO: which FNM_xxx flags to use? */ diff --git a/shell/hush_test/hush-bugs/var_backslash1.right b/shell/hush_test/hush-bugs/var_backslash1.right deleted file mode 100644 index f384da1f5..000000000 --- a/shell/hush_test/hush-bugs/var_backslash1.right +++ /dev/null @@ -1,26 +0,0 @@ -a is '\*bc' -b is '\' -c is '*' -${a##?*} removes everything: || - matches one char, then all -${a##?"*"} removes \*: |bc| - matches one char, then * -${a##\*} removes nothing: |\*bc| - first char is not * -${a##\\*} removes everything: || - matches \, then all -${a##\\\*} removes \*: |bc| - matches \, then * -${a##?$c} removes everything: || - matches one char, then all -${a##?"$c"} removes \*: |bc| - matches one char, then * -${a##\\$c} removes everything: || - matches \, then all -${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ -${a##\\"$c"} removes \*: |bc| - matches \, then * -${a##$b} removes \: |*bc| - matches \ -${a##"$b"} removes \: |*bc| - matches \ - -${a##"$b"?} removes \*: |bc| - matches \, then one char -${a##"$b"*} removes everything: || - matches \, then all -${a##"$b""?"} removes nothing: |\*bc| - second char is not ? -${a##"$b""*"} removes \*: |bc| - matches \, then * -${a##"$b"\*} removes \*: |bc| - matches \, then * -${a##"$b"$c} removes everything:|| - matches \, then all -${a##"$b""$c"} removes \*: |bc| - matches \, then * -${a##"$b?"} removes nothing: |\*bc| - second char is not ? -${a##"$b*"} removes \*: |bc| - matches \, then * -${a##"$b$c"} removes \*: |bc| - matches \, then * diff --git a/shell/hush_test/hush-bugs/var_backslash1.tests b/shell/hush_test/hush-bugs/var_backslash1.tests deleted file mode 100755 index 7eea3cc19..000000000 --- a/shell/hush_test/hush-bugs/var_backslash1.tests +++ /dev/null @@ -1,38 +0,0 @@ -a='\*bc' -b='\' -c='*' -echo "a is '$a'" -echo "b is '$b'" -echo "c is '$c'" -echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' -echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' -echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' -echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' -echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' -echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' -echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' -echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' -echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' -echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' -echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' -echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' -echo -## In bash, this isn't working as expected -#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| -#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| -#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| -#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| -## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: -## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) -## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| -#echo -echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' -echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' -echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| -echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' -echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' -echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' -echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' -echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| -echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || -echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' diff --git a/shell/hush_test/hush-vars/var_backslash1.right b/shell/hush_test/hush-vars/var_backslash1.right new file mode 100644 index 000000000..3f2c21289 --- /dev/null +++ b/shell/hush_test/hush-vars/var_backslash1.right @@ -0,0 +1,35 @@ +a is '\*bc' +b is '\' +c is '*' +${a##?*} removes everything: || - matches one char, then all +${a##?"*"} removes \*: |bc| - matches one char, then * +${a##\*} removes nothing: |\*bc| - first char is not * +${a##\\*} removes everything: || - matches \, then all +${a##\\\*} removes \*: |bc| - matches \, then * +${a##?$c} removes everything: || - matches one char, then all +${a##?"$c"} removes \*: |bc| - matches one char, then * +${a##\\$c} removes everything: || - matches \, then all +${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ +${a##\\"$c"} removes \*: |bc| - matches \, then * +${a##$b} removes \: |*bc| - matches \ +${a##"$b"} removes \: |*bc| - matches \ + +Single quote tests: +${a##?'*'} removes \*: |bc| - matches one char, then * +${a##'\'*} removes everything: || - matches \, then all +${a##'\'\*} removes \*: |bc| - matches \, then * +${a##'\*'} removes \*: |bc| - matches \, then * +${a##'\'$c} removes everything: || - matches \, then all +${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $ +${a##'\'"$c"} removes \*: |bc| - matches \, then * + +${a##"$b"?} removes \*: |bc| - matches \, then one char +${a##"$b"*} removes everything: || - matches \, then all +${a##"$b""?"} removes nothing: |\*bc| - second char is not ? +${a##"$b""*"} removes \*: |bc| - matches \, then * +${a##"$b"\*} removes \*: |bc| - matches \, then * +${a##"$b"$c} removes everything:|| - matches \, then all +${a##"$b""$c"} removes \*: |bc| - matches \, then * +${a##"$b?"} removes nothing: |\*bc| - second char is not ? +${a##"$b*"} removes \*: |bc| - matches \, then * +${a##"$b$c"} removes \*: |bc| - matches \, then * diff --git a/shell/hush_test/hush-vars/var_backslash1.tests b/shell/hush_test/hush-vars/var_backslash1.tests new file mode 100755 index 000000000..015a6107b --- /dev/null +++ b/shell/hush_test/hush-vars/var_backslash1.tests @@ -0,0 +1,48 @@ +a='\*bc' +b='\' +c='*' +echo "a is '$a'" +echo "b is '$b'" +echo "c is '$c'" +echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' +echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' +echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' +echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' +echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' +echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' +echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' +echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' +echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' +echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' +echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' +echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' +echo +sq="'" +echo 'Single quote tests:' +echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' +echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' +echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' +echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' +echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' +echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' +echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' +echo +## In bash, this isn't working as expected +#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| +#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| +## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: +## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) +## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| +#echo +echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' +echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' +echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' +echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' +echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' +echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' +echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || +echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' -- cgit v1.2.3-55-g6feb