diff options
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash_test/ash-vars/var_backslash1.right | 9 | ||||
| -rwxr-xr-x | shell/ash_test/ash-vars/var_backslash1.tests | 10 | ||||
| -rw-r--r-- | shell/hush.c | 168 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var_backslash1.right (renamed from shell/hush_test/hush-bugs/var_backslash1.right) | 9 | ||||
| -rwxr-xr-x | shell/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 | ||
| 17 | Single 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 *' | |||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' |
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' |
| 19 | echo | 19 | echo |
| 20 | sq="'" | ||
| 21 | echo 'Single quote tests:' | ||
| 22 | echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' | ||
| 23 | echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' | ||
| 24 | echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' | ||
| 25 | echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' | ||
| 26 | echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' | ||
| 27 | echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' | ||
| 28 | echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' | ||
| 29 | echo | ||
| 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; |
| 561 | enum { | 561 | enum { |
| 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) | |||
| 3193 | static void o_addQchr(o_string *o, int ch) | 3197 | static 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 | ||
| 3237 | static void o_addQblock(o_string *o, const char *str, int len) | 3241 | static 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 | ||
| 3255 | static 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 */ |
| 3677 | static int o_save_ptr(o_string *o, int n) | 3686 | static 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 | ||
| 6190 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); | 6195 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash); |
| 6191 | #if ENABLE_HUSH_TICK | 6196 | #if ENABLE_HUSH_TICK |
| 6192 | static int process_command_subs(o_string *dest, const char *s); | 6197 | 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) | |||
| 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 | 6387 | static 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 */ |
| 6386 | static 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 | ||
| 6709 | static int append_str_maybe_ifs_split(o_string *output, int n, | 6716 | static 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 | */ |
| 6729 | static NOINLINE int expand_one_var(o_string *output, int n, | 6749 | static 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 | ||
| 7345 | static char **expand_strvec_to_strvec(char **argv) | 7377 | static 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 | */ |
| 7363 | static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash) | 7395 | static 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 | ||
| 17 | Single 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 *' | |||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' |
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' |
| 19 | echo | 19 | echo |
| 20 | sq="'" | ||
| 21 | echo 'Single quote tests:' | ||
| 22 | echo '${a##?'$sq'*'$sq'} removes \*: '"|${a##?'*'}|"' - matches one char, then *' | ||
| 23 | echo '${a##'$sq'\'$sq'*} removes everything: '"|${a##'\'*}|"' - matches \, then all' | ||
| 24 | echo '${a##'$sq'\'$sq'\*} removes \*: '"|${a##'\'\*}|"' - matches \, then *' | ||
| 25 | echo '${a##'$sq'\*'$sq'} removes \*: '"|${a##'\*'}|"' - matches \, then *' | ||
| 26 | echo '${a##'$sq'\'$sq'$c} removes everything: '"|${a##'\'$c}|"' - matches \, then all' | ||
| 27 | echo '${a##'$sq'\'$sq'\$c} removes nothing: '"|${a##'\'\$c}|"' - matches \, but then second char is not $' | ||
| 28 | echo '${a##'$sq'\'$sq'"$c"} removes \*: '"|${a##'\'"$c"}|"' - matches \, then *' | ||
| 29 | echo | ||
| 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| |
