diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 67 |
1 files changed, 53 insertions, 14 deletions
diff --git a/shell/hush.c b/shell/hush.c index 309ed2139..d0225edb9 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -4466,6 +4466,8 @@ static int parse_dollar(o_string *as_string, | |||
4466 | case '@': /* args */ | 4466 | case '@': /* args */ |
4467 | goto make_one_char_var; | 4467 | goto make_one_char_var; |
4468 | case '{': { | 4468 | case '{': { |
4469 | char len_single_ch; | ||
4470 | |||
4469 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4471 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4470 | 4472 | ||
4471 | ch = i_getch(input); /* eat '{' */ | 4473 | ch = i_getch(input); /* eat '{' */ |
@@ -4485,6 +4487,7 @@ static int parse_dollar(o_string *as_string, | |||
4485 | return 0; | 4487 | return 0; |
4486 | } | 4488 | } |
4487 | nommu_addchr(as_string, ch); | 4489 | nommu_addchr(as_string, ch); |
4490 | len_single_ch = ch; | ||
4488 | ch |= quote_mask; | 4491 | ch |= quote_mask; |
4489 | 4492 | ||
4490 | /* It's possible to just call add_till_closing_bracket() at this point. | 4493 | /* It's possible to just call add_till_closing_bracket() at this point. |
@@ -4509,9 +4512,18 @@ static int parse_dollar(o_string *as_string, | |||
4509 | /* handle parameter expansions | 4512 | /* handle parameter expansions |
4510 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | 4513 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 |
4511 | */ | 4514 | */ |
4512 | if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ | 4515 | if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */ |
4513 | goto bad_dollar_syntax; | 4516 | if (len_single_ch != '#' |
4514 | 4517 | /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */ | |
4518 | || i_peek(input) != '}' | ||
4519 | ) { | ||
4520 | goto bad_dollar_syntax; | ||
4521 | } | ||
4522 | /* else: it's "length of C" ${#C} op, | ||
4523 | * where C is a single char | ||
4524 | * special var name, e.g. ${#!}. | ||
4525 | */ | ||
4526 | } | ||
4515 | /* Eat everything until closing '}' (or ':') */ | 4527 | /* Eat everything until closing '}' (or ':') */ |
4516 | end_ch = '}'; | 4528 | end_ch = '}'; |
4517 | if (BASH_SUBSTR | 4529 | if (BASH_SUBSTR |
@@ -4568,6 +4580,7 @@ static int parse_dollar(o_string *as_string, | |||
4568 | } | 4580 | } |
4569 | break; | 4581 | break; |
4570 | } | 4582 | } |
4583 | len_single_ch = 0; /* it can't be ${#C} op */ | ||
4571 | } | 4584 | } |
4572 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4585 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4573 | break; | 4586 | break; |
@@ -5559,8 +5572,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5559 | first_char = arg[0] = arg0 & 0x7f; | 5572 | first_char = arg[0] = arg0 & 0x7f; |
5560 | exp_op = 0; | 5573 | exp_op = 0; |
5561 | 5574 | ||
5562 | if (first_char == '#' /* ${#... */ | 5575 | if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ |
5563 | && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */ | 5576 | && (!exp_saveptr /* and ( not(${#<op_char>...}) */ |
5577 | || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ | ||
5578 | ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ | ||
5564 | ) { | 5579 | ) { |
5565 | /* It must be length operator: ${#var} */ | 5580 | /* It must be length operator: ${#var} */ |
5566 | var++; | 5581 | var++; |
@@ -5797,7 +5812,11 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5797 | /* mimic bash message */ | 5812 | /* mimic bash message */ |
5798 | die_if_script("%s: %s", | 5813 | die_if_script("%s: %s", |
5799 | var, | 5814 | var, |
5800 | exp_word[0] ? exp_word : "parameter null or not set" | 5815 | exp_word[0] |
5816 | ? exp_word | ||
5817 | : "parameter null or not set" | ||
5818 | /* ash has more specific messages, a-la: */ | ||
5819 | /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/ | ||
5801 | ); | 5820 | ); |
5802 | //TODO: how interactive bash aborts expansion mid-command? | 5821 | //TODO: how interactive bash aborts expansion mid-command? |
5803 | } else { | 5822 | } else { |
@@ -6643,8 +6662,18 @@ struct squirrel { | |||
6643 | /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ | 6662 | /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ |
6644 | }; | 6663 | }; |
6645 | 6664 | ||
6665 | static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved) | ||
6666 | { | ||
6667 | sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); | ||
6668 | sq[i].orig_fd = orig; | ||
6669 | sq[i].moved_to = moved; | ||
6670 | sq[i+1].orig_fd = -1; /* end marker */ | ||
6671 | return sq; | ||
6672 | } | ||
6673 | |||
6646 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | 6674 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) |
6647 | { | 6675 | { |
6676 | int moved_to; | ||
6648 | int i = 0; | 6677 | int i = 0; |
6649 | 6678 | ||
6650 | if (sq) while (sq[i].orig_fd >= 0) { | 6679 | if (sq) while (sq[i].orig_fd >= 0) { |
@@ -6664,15 +6693,12 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6664 | i++; | 6693 | i++; |
6665 | } | 6694 | } |
6666 | 6695 | ||
6667 | sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); | ||
6668 | sq[i].orig_fd = fd; | ||
6669 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | 6696 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ |
6670 | sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); | 6697 | moved_to = fcntl_F_DUPFD(fd, avoid_fd); |
6671 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); | 6698 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); |
6672 | if (sq[i].moved_to < 0 && errno != EBADF) | 6699 | if (moved_to < 0 && errno != EBADF) |
6673 | xfunc_die(); | 6700 | xfunc_die(); |
6674 | sq[i+1].orig_fd = -1; /* end marker */ | 6701 | return append_squirrel(sq, i, fd, moved_to); |
6675 | return sq; | ||
6676 | } | 6702 | } |
6677 | 6703 | ||
6678 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6704 | /* fd: redirect wants this fd to be used (e.g. 3>file). |
@@ -6778,6 +6804,19 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6778 | */ | 6804 | */ |
6779 | return 1; | 6805 | return 1; |
6780 | } | 6806 | } |
6807 | if (openfd == redir->rd_fd && sqp) { | ||
6808 | /* open() gave us precisely the fd we wanted. | ||
6809 | * This means that this fd was not busy | ||
6810 | * (not opened to anywhere). | ||
6811 | * Remember to close it on restore: | ||
6812 | */ | ||
6813 | struct squirrel *sq = *sqp; | ||
6814 | int i = 0; | ||
6815 | if (sq) while (sq[i].orig_fd >= 0) | ||
6816 | i++; | ||
6817 | *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */ | ||
6818 | debug_printf_redir("redir to previously closed fd %d\n", openfd); | ||
6819 | } | ||
6781 | } else { | 6820 | } else { |
6782 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ | 6821 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ |
6783 | openfd = redir->rd_dup; | 6822 | openfd = redir->rd_dup; |
@@ -9719,7 +9758,7 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
9719 | sighandler_t handler; | 9758 | sighandler_t handler; |
9720 | 9759 | ||
9721 | sig = get_signum(*argv++); | 9760 | sig = get_signum(*argv++); |
9722 | if (sig < 0 || sig >= NSIG) { | 9761 | if (sig < 0) { |
9723 | ret = EXIT_FAILURE; | 9762 | ret = EXIT_FAILURE; |
9724 | /* Mimic bash message exactly */ | 9763 | /* Mimic bash message exactly */ |
9725 | bb_error_msg("trap: %s: invalid signal specification", argv[-1]); | 9764 | bb_error_msg("trap: %s: invalid signal specification", argv[-1]); |