aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/ash_test/ash-vars/var_backslash1.right9
-rwxr-xr-xshell/ash_test/ash-vars/var_backslash1.tests10
-rw-r--r--shell/hush.c168
-rw-r--r--shell/hush_test/hush-vars/var_backslash1.right (renamed from shell/hush_test/hush-bugs/var_backslash1.right)9
-rwxr-xr-xshell/hush_test/hush-vars/var_backslash1.tests (renamed from shell/hush_test/hush-bugs/var_backslash1.tests)10
5 files changed, 138 insertions, 68 deletions
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 *
14${a##$b} removes \: |*bc| - matches \ 14${a##$b} removes \: |*bc| - matches \
15${a##"$b"} removes \: |*bc| - matches \ 15${a##"$b"} removes \: |*bc| - matches \
16 16
17Single quote tests:
18${a##?'*'} removes \*: |bc| - matches one char, then *
19${a##'\'*} removes everything: || - matches \, then all
20${a##'\'\*} removes \*: |bc| - matches \, then *
21${a##'\*'} removes \*: |bc| - matches \, then *
22${a##'\'$c} removes everything: || - matches \, then all
23${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $
24${a##'\'"$c"} removes \*: |bc| - matches \, then *
25
17${a##"$b"?} removes \*: |bc| - matches \, then one char 26${a##"$b"?} removes \*: |bc| - matches \, then one char
18${a##"$b"*} removes everything: || - matches \, then all 27${a##"$b"*} removes everything: || - matches \, then all
19${a##"$b""?"} removes nothing: |\*bc| - second char is not ? 28${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 *'
17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' 17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \'
18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' 18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \'
19echo 19echo
20sq="'"
21echo 'Single quote tests:'
22echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *'
23echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all'
24echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *'
25echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *'
26echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all'
27echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $'
28echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *'
29echo
20## In bash, this isn't working as expected 30## In bash, this isn't working as expected
21#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| 31#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc|
22#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| 32#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 {
559 smallint ended_in_ifs; 559 smallint ended_in_ifs;
560} o_string; 560} o_string;
561enum { 561enum {
562 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ 562 /* Protect all newly added chars against globbing by prepending \ to *, ?, [, -, \ */
563 EXP_FLAG_GLOB = 0x2, 563 EXP_FLAG_GLOBPROTECT_CHARS = 0x1,
564 /* Protect newly added chars against globbing 564 /* Protect quoted vars against globbing */
565 * by prepending \ to *, ?, [, \ */ 565 EXP_FLAG_GLOBPROTECT_VARS = 0x2,
566 EXP_FLAG_ESC_GLOB_CHARS = 0x1, 566 /* On word-split, perform globbing (one word may become many) */
567 EXP_FLAG_DO_GLOBBING = 0x4,
568 /* Do not word-split */
569 EXP_FLAG_SINGLEWORD = 0x80,
570 /* ^^^^ EXP_FLAG_SINGLEWORD must be 0x80 */
567}; 571};
568/* Used for initialization: o_string foo = NULL_O_STRING; */ 572/* Used for initialization: o_string foo = NULL_O_STRING; */
569#define NULL_O_STRING { NULL } 573#define NULL_O_STRING { NULL }
@@ -3193,7 +3197,7 @@ static void o_addqchr(o_string *o, int ch)
3193static void o_addQchr(o_string *o, int ch) 3197static void o_addQchr(o_string *o, int ch)
3194{ 3198{
3195 int sz = 1; 3199 int sz = 1;
3196 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) 3200 if ((o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)
3197 && strchr("*?[-\\" MAYBE_BRACES, ch) 3201 && strchr("*?[-\\" MAYBE_BRACES, ch)
3198 ) { 3202 ) {
3199 sz++; 3203 sz++;
@@ -3236,7 +3240,7 @@ static void o_addqblock(o_string *o, const char *str, int len)
3236 3240
3237static void o_addQblock(o_string *o, const char *str, int len) 3241static void o_addQblock(o_string *o, const char *str, int len)
3238{ 3242{
3239 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) { 3243 if (!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)) {
3240 o_addblock(o, str, len); 3244 o_addblock(o, str, len);
3241 return; 3245 return;
3242 } 3246 }
@@ -3248,6 +3252,11 @@ static void o_addQstr(o_string *o, const char *str)
3248 o_addQblock(o, str, strlen(str)); 3252 o_addQblock(o, str, strlen(str));
3249} 3253}
3250 3254
3255static void o_addqstr(o_string *o, const char *str)
3256{
3257 o_addqblock(o, str, strlen(str));
3258}
3259
3251/* A special kind of o_string for $VAR and `cmd` expansion. 3260/* A special kind of o_string for $VAR and `cmd` expansion.
3252 * It contains char* list[] at the beginning, which is grown in 16 element 3261 * It contains char* list[] at the beginning, which is grown in 16 element
3253 * increments. Actual string data starts at the next multiple of 16 * (char*). 3262 * 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)
3266 int i = 0; 3275 int i = 0;
3267 3276
3268 indent(); 3277 indent();
3269 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n", 3278 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d do_glob:%d has_quoted:%d globprotect:%d\n",
3270 prefix, list, n, string_start, o->length, o->maxlen, 3279 prefix, list, n, string_start, o->length, o->maxlen,
3271 !!(o->o_expflags & EXP_FLAG_GLOB), 3280 !!(o->o_expflags & EXP_FLAG_DO_GLOBBING),
3272 o->has_quoted_part, 3281 o->has_quoted_part,
3273 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 3282 !!(o->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
3274 while (i < n) { 3283 while (i < n) {
3275 indent(); 3284 indent();
3276 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i], 3285 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)
3672 3681
3673#endif /* !HUSH_BRACE_EXPANSION */ 3682#endif /* !HUSH_BRACE_EXPANSION */
3674 3683
3675/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. 3684/* If o->o_expflags & EXP_FLAG_DO_GLOBBING, glob the string so far remembered.
3676 * Otherwise, just finish current list[] and start new */ 3685 * Otherwise, just finish current list[] and start new */
3677static int o_save_ptr(o_string *o, int n) 3686static int o_save_ptr(o_string *o, int n)
3678{ 3687{
3679 if (o->o_expflags & EXP_FLAG_GLOB) { 3688 if (o->o_expflags & EXP_FLAG_DO_GLOBBING) {
3680 /* If o->has_empty_slot, list[n] was already globbed 3689 /* If o->has_empty_slot, list[n] was already globbed
3681 * (if it was requested back then when it was filled) 3690 * (if it was requested back then when it was filled)
3682 * so don't do that again! */ 3691 * so don't do that again! */
@@ -5476,8 +5485,8 @@ static int encode_string(o_string *as_string,
5476 if (ch != '\n') { 5485 if (ch != '\n') {
5477 next = i_peek(input); 5486 next = i_peek(input);
5478 } 5487 }
5479 debug_printf_parse("\" ch=%c (%d) escape=%d\n", 5488 debug_printf_parse("\" ch:%c (%d) globprotect:%d\n",
5480 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 5489 ch, ch, !!(dest->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
5481 if (ch == '\\') { 5490 if (ch == '\\') {
5482 if (next == EOF) { 5491 if (next == EOF) {
5483 /* Testcase: in interactive shell a file with 5492 /* Testcase: in interactive shell a file with
@@ -5581,8 +5590,8 @@ static struct pipe *parse_stream(char **pstring,
5581 redir_type redir_style; 5590 redir_type redir_style;
5582 5591
5583 ch = i_getch(input); 5592 ch = i_getch(input);
5584 debug_printf_parse(": ch=%c (%d) escape=%d\n", 5593 debug_printf_parse(": ch:%c (%d) globprotect:%d\n",
5585 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 5594 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS));
5586 if (ch == EOF) { 5595 if (ch == EOF) {
5587 struct pipe *pi; 5596 struct pipe *pi;
5588 5597
@@ -6011,10 +6020,10 @@ static struct pipe *parse_stream(char **pstring,
6011 continue; /* get next char */ 6020 continue; /* get next char */
6012 } 6021 }
6013 if (ctx.is_assignment == NOT_ASSIGNMENT) 6022 if (ctx.is_assignment == NOT_ASSIGNMENT)
6014 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 6023 ctx.word.o_expflags |= EXP_FLAG_GLOBPROTECT_CHARS;
6015 if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) 6024 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6016 goto parse_error_exitcode1; 6025 goto parse_error_exitcode1;
6017 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; 6026 ctx.word.o_expflags &= ~EXP_FLAG_GLOBPROTECT_CHARS;
6018 continue; /* get next char */ 6027 continue; /* get next char */
6019#if ENABLE_HUSH_TICK 6028#if ENABLE_HUSH_TICK
6020 case '`': { 6029 case '`': {
@@ -6183,10 +6192,6 @@ static struct pipe *parse_stream(char **pstring,
6183 * Execution routines 6192 * Execution routines
6184 */ 6193 */
6185/* Expansion can recurse, need forward decls: */ 6194/* Expansion can recurse, need forward decls: */
6186#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6187#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6188 expand_string_to_string(str)
6189#endif
6190static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); 6195static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6191#if ENABLE_HUSH_TICK 6196#if ENABLE_HUSH_TICK
6192static int process_command_subs(o_string *dest, const char *s); 6197static 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)
6251 word_len = strcspn(str, G.ifs); 6256 word_len = strcspn(str, G.ifs);
6252 if (word_len) { 6257 if (word_len) {
6253 /* We have WORD_LEN leading non-IFS chars */ 6258 /* We have WORD_LEN leading non-IFS chars */
6254 if (!(output->o_expflags & EXP_FLAG_GLOB)) { 6259 if (!(output->o_expflags & EXP_FLAG_DO_GLOBBING)) {
6255 o_addblock(output, str, word_len); 6260 o_addblock(output, str, word_len);
6256 } else { 6261 } else {
6257 /* Protect backslashes against globbing up :) 6262 /* Protect backslashes against globbing up :)
@@ -6344,7 +6349,7 @@ static char *encode_then_expand_string(const char *str)
6344//TODO: error check (encode_string returns 0 on error)? 6349//TODO: error check (encode_string returns 0 on error)?
6345 //bb_error_msg("'%s' -> '%s'", str, dest.data); 6350 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6346 exp_str = expand_string_to_string(dest.data, 6351 exp_str = expand_string_to_string(dest.data,
6347 EXP_FLAG_ESC_GLOB_CHARS, 6352 EXP_FLAG_GLOBPROTECT_CHARS,
6348 /*unbackslash:*/ 1 6353 /*unbackslash:*/ 1
6349 ); 6354 );
6350 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 6355 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
@@ -6379,15 +6384,11 @@ static const char *first_special_char_in_vararg(const char *cp)
6379 * a dquoted string: "${var#"zz"}"), the difference only comes later 6384 * a dquoted string: "${var#"zz"}"), the difference only comes later
6380 * (word splitting and globbing of the ${var...} result). 6385 * (word splitting and globbing of the ${var...} result).
6381 */ 6386 */
6382#if !BASH_PATTERN_SUBST 6387static char *encode_then_expand_vararg(const char *str,
6383#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \ 6388 int handle_squotes, /* 'str' substrings are parsed as literals (unless inside "")*/
6384 encode_then_expand_vararg(str, handle_squotes) 6389 int protect_vars, /* glob-protect double-quoted $VARS */
6385#endif 6390 int do_unbackslash /* run unbackslash on result before returning it */
6386static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash) 6391) {
6387{
6388#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6389 const int do_unbackslash = 0;
6390#endif
6391 char *exp_str; 6392 char *exp_str;
6392 struct in_str input; 6393 struct in_str input;
6393 o_string dest = NULL_O_STRING; 6394 o_string dest = NULL_O_STRING;
@@ -6422,7 +6423,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6422 goto ret; /* error */ 6423 goto ret; /* error */
6423 } 6424 }
6424 if (ch == '"') { 6425 if (ch == '"') {
6425 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; 6426 dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS;
6426 continue; 6427 continue;
6427 } 6428 }
6428 if (ch == '\\') { 6429 if (ch == '\\') {
@@ -6438,7 +6439,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6438 if (ch == '$') { 6439 if (ch == '$') {
6439 if (parse_dollar_squote(NULL, &dest, &input)) 6440 if (parse_dollar_squote(NULL, &dest, &input))
6440 continue; 6441 continue;
6441 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { 6442 if (!parse_dollar(NULL, &dest, &input,
6443 /*quote_mask:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) ? 0x80 : 0
6444 )
6445 ) {
6442 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); 6446 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6443 goto ret; 6447 goto ret;
6444 } 6448 }
@@ -6450,7 +6454,7 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6450 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6454 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6451 o_addchr(&dest, 0x80 | '`'); 6455 o_addchr(&dest, 0x80 | '`');
6452 if (!add_till_backquote(&dest, &input, 6456 if (!add_till_backquote(&dest, &input,
6453 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ 6457 /*in_dquote:*/ (dest.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)
6454 ) 6458 )
6455 ) { 6459 ) {
6456 goto ret; /* error */ 6460 goto ret; /* error */
@@ -6463,9 +6467,12 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
6463 o_addQchr(&dest, ch); 6467 o_addQchr(&dest, ch);
6464 } /* for (;;) */ 6468 } /* for (;;) */
6465 6469
6466 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data); 6470 debug_printf_parse("encode(do_unbackslash:%d): '%s' -> '%s'\n", do_unbackslash, str, dest.data);
6467 exp_str = expand_string_to_string(dest.data, 6471 exp_str = expand_string_to_string(dest.data,
6468 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, 6472 0
6473 | (do_unbackslash ? EXP_FLAG_GLOBPROTECT_CHARS : 0)
6474 | (protect_vars ? EXP_FLAG_GLOBPROTECT_VARS : 0)
6475 ,
6469 do_unbackslash 6476 do_unbackslash
6470 ); 6477 );
6471 ret: 6478 ret:
@@ -6549,7 +6556,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
6549 goto ret; /* error */ 6556 goto ret; /* error */
6550 } 6557 }
6551 if (ch == '"') { 6558 if (ch == '"') {
6552 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; 6559 dest.o_expflags ^= EXP_FLAG_GLOBPROTECT_CHARS;
6553 if (dest.o_expflags) { 6560 if (dest.o_expflags) {
6554 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6561 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6555 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6562 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
@@ -6579,7 +6586,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
6579 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6586 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6580 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); 6587 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6581 if (!add_till_backquote(&dest, &input, 6588 if (!add_till_backquote(&dest, &input,
6582 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ 6589 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_GLOBPROTECT_CHARS set */
6583 ) 6590 )
6584 ) { 6591 ) {
6585 goto ret; /* error */ 6592 goto ret; /* error */
@@ -6707,27 +6714,39 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
6707#endif /* BASH_PATTERN_SUBST */ 6714#endif /* BASH_PATTERN_SUBST */
6708 6715
6709static int append_str_maybe_ifs_split(o_string *output, int n, 6716static int append_str_maybe_ifs_split(o_string *output, int n,
6710 int first_ch, const char *val) 6717 int quoted, const char *val)
6711{ 6718{
6712 if (!(first_ch & 0x80)) { /* unquoted $VAR */ 6719 if (!quoted) { /* unquoted $VAR */
6713 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, 6720 debug_printf_expand("unquoted variable value '%s', o_escape:%d o_varescape:%d, singleword:%d\n", val,
6714 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 6721 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS),
6715 if (val && val[0]) 6722 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS),
6723 !!(output->o_expflags & EXP_FLAG_SINGLEWORD)
6724 );
6725 if (val && val[0]) {
6726 if (output->o_expflags & EXP_FLAG_SINGLEWORD)
6727 goto singleword;
6716 n = expand_on_ifs(output, n, val); 6728 n = expand_on_ifs(output, n, val);
6729 }
6717 } else { /* quoted "$VAR" */ 6730 } else { /* quoted "$VAR" */
6718 output->has_quoted_part = 1; 6731 output->has_quoted_part = 1;
6719 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, 6732 debug_printf_expand("quoted variable value '%s', o_escape:%d o_varescape:%d\n", val,
6720 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); 6733 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS),
6721 if (val && val[0]) 6734 !!(output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS)
6722 o_addQstr(output, val); 6735 );
6736 if (val && val[0]) {
6737 if (output->o_expflags & EXP_FLAG_GLOBPROTECT_VARS)
6738 o_addqstr(output, val);
6739 else
6740 singleword:
6741 o_addQstr(output, val);
6742 }
6723 } 6743 }
6724 return n; 6744 return n;
6725} 6745}
6726 6746
6727/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. 6747/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
6728 */ 6748 */
6729static NOINLINE int expand_one_var(o_string *output, int n, 6749static NOINLINE int expand_one_var(o_string *output, int n, char *arg, char **pp)
6730 int first_ch, char *arg, char **pp)
6731{ 6750{
6732 const char *val; 6751 const char *val;
6733 char *to_be_freed; 6752 char *to_be_freed;
@@ -6863,7 +6882,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6863 if (exp_op == *exp_word) /* ## or %% */ 6882 if (exp_op == *exp_word) /* ## or %% */
6864 exp_word++; 6883 exp_word++;
6865 debug_printf_expand("expand: exp_word:'%s'\n", exp_word); 6884 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
6866 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); 6885 exp_exp_word = encode_then_expand_vararg(exp_word,
6886 /*handle_squotes:*/ 1, /* 'str' are processed (and glob-protected) */
6887 /*globprotect_vars:*/ 1, /* value of "$VAR" is not glob-expanded */
6888 /*unbackslash:*/ 0
6889 );
6867 if (exp_exp_word) 6890 if (exp_exp_word)
6868 exp_word = exp_exp_word; 6891 exp_word = exp_exp_word;
6869 debug_printf_expand("expand: exp_word:'%s'\n", exp_word); 6892 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,
6910 * (note that a*z _pattern_ is never globbed!) 6933 * (note that a*z _pattern_ is never globbed!)
6911 */ 6934 */
6912 char *pattern, *repl, *t; 6935 char *pattern, *repl, *t;
6913 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); 6936 pattern = encode_then_expand_vararg(exp_word,
6937 /*handle_squotes:*/ 1,
6938 /*globprotect_vars:*/ 0,
6939 /*unbackslash:*/ 0
6940 );
6914 if (!pattern) 6941 if (!pattern)
6915 pattern = xstrdup(exp_word); 6942 pattern = xstrdup(exp_word);
6916 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); 6943 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,
6918 exp_word = p; 6945 exp_word = p;
6919 p = strchr(p, SPECIAL_VAR_SYMBOL); 6946 p = strchr(p, SPECIAL_VAR_SYMBOL);
6920 *p = '\0'; 6947 *p = '\0';
6921 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1); 6948 repl = encode_then_expand_vararg(exp_word,
6949 /*handle_squotes:*/ 1,
6950 /*globprotect_vars:*/ 0,
6951 /*unbackslash:*/ 1
6952 );
6922 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); 6953 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
6923 /* HACK ALERT. We depend here on the fact that 6954 /* HACK ALERT. We depend here on the fact that
6924 * G.global_argv and results of utoa and get_local_var_value 6955 * 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,
7071 /* ${var=word} - assign and use default value */ 7102 /* ${var=word} - assign and use default value */
7072 to_be_freed = encode_then_expand_vararg(exp_word, 7103 to_be_freed = encode_then_expand_vararg(exp_word,
7073 /*handle_squotes:*/ !(arg0 & 0x80), 7104 /*handle_squotes:*/ !(arg0 & 0x80),
7105 /*globprotect_vars:*/ 0,
7074 /*unbackslash:*/ 0 7106 /*unbackslash:*/ 0
7075 ); 7107 );
7076 if (to_be_freed) 7108 if (to_be_freed)
@@ -7115,7 +7147,7 @@ static NOINLINE int expand_one_var(o_string *output, int n,
7115 arg[0] = arg0; 7147 arg[0] = arg0;
7116 *pp = p; 7148 *pp = p;
7117 7149
7118 n = append_str_maybe_ifs_split(output, n, first_ch, val); 7150 n = append_str_maybe_ifs_split(output, n, /*quoted:*/ (arg0 & 0x80), val);
7119 7151
7120 free(to_be_freed); 7152 free(to_be_freed);
7121 return n; 7153 return n;
@@ -7243,7 +7275,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7243 G.last_exitcode = process_command_subs(&subst_result, arg); 7275 G.last_exitcode = process_command_subs(&subst_result, arg);
7244 G.expand_exitcode = G.last_exitcode; 7276 G.expand_exitcode = G.last_exitcode;
7245 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); 7277 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7246 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data); 7278 n = append_str_maybe_ifs_split(output, n, /*quoted:*/(first_ch & 0x80), subst_result.data);
7247 o_free(&subst_result); 7279 o_free(&subst_result);
7248 break; 7280 break;
7249 } 7281 }
@@ -7261,7 +7293,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7261 sprintf(arith_buf, ARITH_FMT, res); 7293 sprintf(arith_buf, ARITH_FMT, res);
7262 if (res < 0 7294 if (res < 0
7263 && first_ch == (char)('+'|0x80) 7295 && first_ch == (char)('+'|0x80)
7264 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */ 7296 /* && (output->o_expflags & EXP_FLAG_GLOBPROTECT_CHARS) */
7265 ) { 7297 ) {
7266 /* Quoted negative ariths, like filename[0"$((-9))"], 7298 /* Quoted negative ariths, like filename[0"$((-9))"],
7267 * should not be interpreted as glob ranges. 7299 * 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)
7276#endif 7308#endif
7277 default: 7309 default:
7278 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */ 7310 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7279 n = expand_one_var(output, n, first_ch, arg, &p); 7311 n = expand_one_var(output, n, arg, &p);
7280 break; 7312 break;
7281 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ 7313 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7282 7314
@@ -7344,7 +7376,7 @@ static char **expand_variables(char **argv, unsigned expflags)
7344 7376
7345static char **expand_strvec_to_strvec(char **argv) 7377static char **expand_strvec_to_strvec(char **argv)
7346{ 7378{
7347 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 7379 return expand_variables(argv, EXP_FLAG_DO_GLOBBING | EXP_FLAG_GLOBPROTECT_CHARS);
7348} 7380}
7349 7381
7350#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) 7382#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
@@ -7362,10 +7394,6 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7362 */ 7394 */
7363static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) 7395static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7364{ 7396{
7365#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7366 const int do_unbackslash = 1;
7367 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7368#endif
7369 char *argv[2], **list; 7397 char *argv[2], **list;
7370 7398
7371 debug_printf_expand("string_to_string<='%s'\n", str); 7399 debug_printf_expand("string_to_string<='%s'\n", str);
@@ -7434,7 +7462,7 @@ static char **expand_assignments(char **argv, int count)
7434 for (i = 0; i < count; i++) { 7462 for (i = 0; i < count; i++) {
7435 p = add_string_to_strings(p, 7463 p = add_string_to_strings(p,
7436 expand_string_to_string(argv[i], 7464 expand_string_to_string(argv[i],
7437 EXP_FLAG_ESC_GLOB_CHARS, 7465 EXP_FLAG_GLOBPROTECT_CHARS,
7438 /*unbackslash:*/ 1 7466 /*unbackslash:*/ 1
7439 ) 7467 )
7440 ); 7468 );
@@ -8231,7 +8259,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8231 } 8259 }
8232 mode = redir_table[redir->rd_type].mode; 8260 mode = redir_table[redir->rd_type].mode;
8233 p = expand_string_to_string(redir->rd_filename, 8261 p = expand_string_to_string(redir->rd_filename,
8234 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); 8262 EXP_FLAG_GLOBPROTECT_CHARS,
8263 /*unbackslash:*/ 1
8264 );
8235 newfd = open_or_warn(p, mode); 8265 newfd = open_or_warn(p, mode);
8236 free(p); 8266 free(p);
8237 if (newfd < 0) { 8267 if (newfd < 0) {
@@ -9547,7 +9577,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9547 i = 0; 9577 i = 0;
9548 while (i < command->assignment_cnt) { 9578 while (i < command->assignment_cnt) {
9549 char *p = expand_string_to_string(argv[i], 9579 char *p = expand_string_to_string(argv[i],
9550 EXP_FLAG_ESC_GLOB_CHARS, 9580 EXP_FLAG_GLOBPROTECT_CHARS,
9551 /*unbackslash:*/ 1 9581 /*unbackslash:*/ 1
9552 ); 9582 );
9553#if ENABLE_HUSH_MODE_X 9583#if ENABLE_HUSH_MODE_X
@@ -10068,7 +10098,9 @@ static int run_list(struct pipe *pi)
10068 if (rword == RES_CASE) { 10098 if (rword == RES_CASE) {
10069 debug_printf_exec("CASE cond_code:%d\n", cond_code); 10099 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10070 case_word = expand_string_to_string(pi->cmds->argv[0], 10100 case_word = expand_string_to_string(pi->cmds->argv[0],
10071 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1); 10101 EXP_FLAG_GLOBPROTECT_CHARS,
10102 /*unbackslash:*/ 1
10103 );
10072 debug_printf_exec("CASE word1:'%s'\n", case_word); 10104 debug_printf_exec("CASE word1:'%s'\n", case_word);
10073 //unbackslash(case_word); 10105 //unbackslash(case_word);
10074 //debug_printf_exec("CASE word2:'%s'\n", case_word); 10106 //debug_printf_exec("CASE word2:'%s'\n", case_word);
@@ -10086,7 +10118,7 @@ static int run_list(struct pipe *pi)
10086 char *pattern; 10118 char *pattern;
10087 debug_printf_exec("expand_string_to_string('%s')\n", *argv); 10119 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10088 pattern = expand_string_to_string(*argv, 10120 pattern = expand_string_to_string(*argv,
10089 EXP_FLAG_ESC_GLOB_CHARS, 10121 EXP_FLAG_GLOBPROTECT_CHARS,
10090 /*unbackslash:*/ 0 10122 /*unbackslash:*/ 0
10091 ); 10123 );
10092 /* TODO: which FNM_xxx flags to use? */ 10124 /* TODO: which FNM_xxx flags to use? */
diff --git a/shell/hush_test/hush-bugs/var_backslash1.right b/shell/hush_test/hush-vars/var_backslash1.right
index f384da1f5..3f2c21289 100644
--- a/shell/hush_test/hush-bugs/var_backslash1.right
+++ b/shell/hush_test/hush-vars/var_backslash1.right
@@ -14,6 +14,15 @@ ${a##\\"$c"} removes \*: |bc| - matches \, then *
14${a##$b} removes \: |*bc| - matches \ 14${a##$b} removes \: |*bc| - matches \
15${a##"$b"} removes \: |*bc| - matches \ 15${a##"$b"} removes \: |*bc| - matches \
16 16
17Single quote tests:
18${a##?'*'} removes \*: |bc| - matches one char, then *
19${a##'\'*} removes everything: || - matches \, then all
20${a##'\'\*} removes \*: |bc| - matches \, then *
21${a##'\*'} removes \*: |bc| - matches \, then *
22${a##'\'$c} removes everything: || - matches \, then all
23${a##'\'\$c} removes nothing: |\*bc| - matches \, but then second char is not $
24${a##'\'"$c"} removes \*: |bc| - matches \, then *
25
17${a##"$b"?} removes \*: |bc| - matches \, then one char 26${a##"$b"?} removes \*: |bc| - matches \, then one char
18${a##"$b"*} removes everything: || - matches \, then all 27${a##"$b"*} removes everything: || - matches \, then all
19${a##"$b""?"} removes nothing: |\*bc| - second char is not ? 28${a##"$b""?"} removes nothing: |\*bc| - second char is not ?
diff --git a/shell/hush_test/hush-bugs/var_backslash1.tests b/shell/hush_test/hush-vars/var_backslash1.tests
index 7eea3cc19..015a6107b 100755
--- a/shell/hush_test/hush-bugs/var_backslash1.tests
+++ b/shell/hush_test/hush-vars/var_backslash1.tests
@@ -17,6 +17,16 @@ echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *'
17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' 17echo '${a##$b} removes \: '"|${a##$b}|"' - matches \'
18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' 18echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \'
19echo 19echo
20sq="'"
21echo 'Single quote tests:'
22echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *'
23echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all'
24echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *'
25echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *'
26echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all'
27echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $'
28echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *'
29echo
20## In bash, this isn't working as expected 30## In bash, this isn't working as expected
21#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| 31#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc|
22#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| 32#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc|