diff options
author | Ron Yorston <rmy@pobox.com> | 2020-01-08 12:30:49 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2020-01-08 12:30:49 +0000 |
commit | a9271a8e97e6e7be5285330d5f19352decabf807 (patch) | |
tree | bf3c4464c369a15a46454792dac167505f74769f /shell | |
parent | b0b7ab792bc1f45963f4b84b94faaf05054e1613 (diff) | |
parent | 9ec836c033fc6e55e80f3309b3e05acdf09bb297 (diff) | |
download | busybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.tar.gz busybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.tar.bz2 busybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 14 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_stdin1.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_stdin1.tests | 7 | ||||
-rw-r--r-- | shell/hush.c | 113 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_stdin1.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_stdin1.tests | 7 | ||||
-rw-r--r-- | shell/math.c | 35 | ||||
-rw-r--r-- | shell/shell_common.c | 77 |
8 files changed, 184 insertions, 75 deletions
diff --git a/shell/ash.c b/shell/ash.c index 8a27d5cc3..95d0aebf0 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -638,7 +638,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; | |||
638 | #define random_gen (G_misc.random_gen ) | 638 | #define random_gen (G_misc.random_gen ) |
639 | #define backgndpid (G_misc.backgndpid ) | 639 | #define backgndpid (G_misc.backgndpid ) |
640 | #define INIT_G_misc() do { \ | 640 | #define INIT_G_misc() do { \ |
641 | (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ | 641 | (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \ |
642 | barrier(); \ | 642 | barrier(); \ |
643 | curdir = nullstr; \ | 643 | curdir = nullstr; \ |
644 | physdir = nullstr; \ | 644 | physdir = nullstr; \ |
@@ -1693,7 +1693,7 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack; | |||
1693 | #define g_stacknleft (G_memstack.g_stacknleft) | 1693 | #define g_stacknleft (G_memstack.g_stacknleft) |
1694 | #define stackbase (G_memstack.stackbase ) | 1694 | #define stackbase (G_memstack.stackbase ) |
1695 | #define INIT_G_memstack() do { \ | 1695 | #define INIT_G_memstack() do { \ |
1696 | (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \ | 1696 | (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \ |
1697 | barrier(); \ | 1697 | barrier(); \ |
1698 | g_stackp = &stackbase; \ | 1698 | g_stackp = &stackbase; \ |
1699 | g_stacknxt = stackbase.space; \ | 1699 | g_stacknxt = stackbase.space; \ |
@@ -2316,7 +2316,7 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; | |||
2316 | #endif | 2316 | #endif |
2317 | #define INIT_G_var() do { \ | 2317 | #define INIT_G_var() do { \ |
2318 | unsigned i; \ | 2318 | unsigned i; \ |
2319 | (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ | 2319 | (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \ |
2320 | barrier(); \ | 2320 | barrier(); \ |
2321 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ | 2321 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ |
2322 | varinit[i].flags = varinit_data[i].flags; \ | 2322 | varinit[i].flags = varinit_data[i].flags; \ |
@@ -13112,7 +13112,13 @@ checkend: { | |||
13112 | for (p = eofmark; STPUTC(c, out), *p; p++) { | 13112 | for (p = eofmark; STPUTC(c, out), *p; p++) { |
13113 | if (c != *p) | 13113 | if (c != *p) |
13114 | goto more_heredoc; | 13114 | goto more_heredoc; |
13115 | 13115 | /* FIXME: fails for backslash-newlined terminator: | |
13116 | * cat <<EOF | ||
13117 | * ... | ||
13118 | * EO\ | ||
13119 | * F | ||
13120 | * (see heredoc_bkslash_newline2.tests) | ||
13121 | */ | ||
13116 | c = pgetc_without_PEOA(); | 13122 | c = pgetc_without_PEOA(); |
13117 | } | 13123 | } |
13118 | 13124 | ||
diff --git a/shell/ash_test/ash-redir/redir_stdin1.right b/shell/ash_test/ash-redir/redir_stdin1.right new file mode 100644 index 000000000..1c6217e92 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_stdin1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | #Testing that stdin redirect is restored | ||
2 | read2 | ||
3 | Ok:0 | ||
diff --git a/shell/ash_test/ash-redir/redir_stdin1.tests b/shell/ash_test/ash-redir/redir_stdin1.tests new file mode 100755 index 000000000..f72253f9d --- /dev/null +++ b/shell/ash_test/ash-redir/redir_stdin1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | #Testing that stdin redirect is restored | ||
2 | echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests | ||
3 | echo $r | ||
4 | read r | ||
5 | echo $r | ||
6 | ' | ||
7 | echo Ok:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index 19b97e2a5..97202b953 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -573,7 +573,6 @@ typedef struct HFILE { | |||
573 | char *cur; | 573 | char *cur; |
574 | char *end; | 574 | char *end; |
575 | struct HFILE *next_hfile; | 575 | struct HFILE *next_hfile; |
576 | int is_stdin; | ||
577 | int fd; | 576 | int fd; |
578 | char buf[1024]; | 577 | char buf[1024]; |
579 | } HFILE; | 578 | } HFILE; |
@@ -973,6 +972,7 @@ struct globals { | |||
973 | unsigned execute_lineno; | 972 | unsigned execute_lineno; |
974 | #endif | 973 | #endif |
975 | HFILE *HFILE_list; | 974 | HFILE *HFILE_list; |
975 | HFILE *HFILE_stdin; | ||
976 | /* Which signals have non-DFL handler (even with no traps set)? | 976 | /* Which signals have non-DFL handler (even with no traps set)? |
977 | * Set at the start to: | 977 | * Set at the start to: |
978 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | 978 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
@@ -1603,7 +1603,8 @@ static HFILE *hfopen(const char *name) | |||
1603 | } | 1603 | } |
1604 | 1604 | ||
1605 | fp = xmalloc(sizeof(*fp)); | 1605 | fp = xmalloc(sizeof(*fp)); |
1606 | fp->is_stdin = (name == NULL); | 1606 | if (name == NULL) |
1607 | G.HFILE_stdin = fp; | ||
1607 | fp->fd = fd; | 1608 | fp->fd = fd; |
1608 | fp->cur = fp->end = fp->buf; | 1609 | fp->cur = fp->end = fp->buf; |
1609 | fp->next_hfile = G.HFILE_list; | 1610 | fp->next_hfile = G.HFILE_list; |
@@ -2666,7 +2667,7 @@ static int fgetc_interactive(struct in_str *i) | |||
2666 | { | 2667 | { |
2667 | int ch; | 2668 | int ch; |
2668 | /* If it's interactive stdin, get new line. */ | 2669 | /* If it's interactive stdin, get new line. */ |
2669 | if (G_interactive_fd && i->file->is_stdin) { | 2670 | if (G_interactive_fd && i->file == G.HFILE_stdin) { |
2670 | /* Returns first char (or EOF), the rest is in i->p[] */ | 2671 | /* Returns first char (or EOF), the rest is in i->p[] */ |
2671 | ch = get_user_input(i); | 2672 | ch = get_user_input(i); |
2672 | G.promptmode = 1; /* PS2 */ | 2673 | G.promptmode = 1; /* PS2 */ |
@@ -3652,9 +3653,9 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3652 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", | 3653 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", |
3653 | lvl*2, "", prn, | 3654 | lvl*2, "", prn, |
3654 | command->assignment_cnt); | 3655 | command->assignment_cnt); |
3655 | #if ENABLE_HUSH_LINENO_VAR | 3656 | # if ENABLE_HUSH_LINENO_VAR |
3656 | fdprintf(2, " LINENO:%u", command->lineno); | 3657 | fdprintf(2, " LINENO:%u", command->lineno); |
3657 | #endif | 3658 | # endif |
3658 | if (command->group) { | 3659 | if (command->group) { |
3659 | fdprintf(2, " group %s: (argv=%p)%s%s\n", | 3660 | fdprintf(2, " group %s: (argv=%p)%s%s\n", |
3660 | CMDTYPE[command->cmd_type], | 3661 | CMDTYPE[command->cmd_type], |
@@ -4770,9 +4771,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
4770 | # endif | 4771 | # endif |
4771 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | 4772 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); |
4772 | 4773 | ||
4773 | #if ENABLE_HUSH_INTERACTIVE | 4774 | # if ENABLE_HUSH_INTERACTIVE |
4774 | G.promptmode = 1; /* PS2 */ | 4775 | G.promptmode = 1; /* PS2 */ |
4775 | #endif | 4776 | # endif |
4776 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 4777 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
4777 | 4778 | ||
4778 | while (1) { | 4779 | while (1) { |
@@ -4828,13 +4829,13 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
4828 | syntax_error_unterm_ch(end_ch); | 4829 | syntax_error_unterm_ch(end_ch); |
4829 | return 0; | 4830 | return 0; |
4830 | } | 4831 | } |
4831 | #if 0 | 4832 | # if 0 |
4832 | if (ch == '\n') { | 4833 | if (ch == '\n') { |
4833 | /* "backslash+newline", ignore both */ | 4834 | /* "backslash+newline", ignore both */ |
4834 | o_delchr(dest); /* undo insertion of '\' */ | 4835 | o_delchr(dest); /* undo insertion of '\' */ |
4835 | continue; | 4836 | continue; |
4836 | } | 4837 | } |
4837 | #endif | 4838 | # endif |
4838 | o_addchr(dest, ch); | 4839 | o_addchr(dest, ch); |
4839 | //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch); | 4840 | //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch); |
4840 | continue; | 4841 | continue; |
@@ -4991,7 +4992,7 @@ static int parse_dollar(o_string *as_string, | |||
4991 | if (last_ch == 0) /* error? */ | 4992 | if (last_ch == 0) /* error? */ |
4992 | return 0; | 4993 | return 0; |
4993 | #else | 4994 | #else |
4994 | #error Simple code to only allow ${var} is not implemented | 4995 | # error Simple code to only allow ${var} is not implemented |
4995 | #endif | 4996 | #endif |
4996 | if (as_string) { | 4997 | if (as_string) { |
4997 | o_addstr(as_string, dest->data + pos); | 4998 | o_addstr(as_string, dest->data + pos); |
@@ -7605,7 +7606,9 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
7605 | avoid_fd = 9; | 7606 | avoid_fd = 9; |
7606 | 7607 | ||
7607 | #if ENABLE_HUSH_INTERACTIVE | 7608 | #if ENABLE_HUSH_INTERACTIVE |
7608 | if (fd == G_interactive_fd) { | 7609 | if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */ |
7610 | && fd == G_interactive_fd | ||
7611 | ) { | ||
7609 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ | 7612 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ |
7610 | G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd); | 7613 | G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd); |
7611 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); | 7614 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); |
@@ -7619,7 +7622,7 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
7619 | /* No need to move script fds. | 7622 | /* No need to move script fds. |
7620 | * For NOMMU case, it's actively wrong: we'd change ->fd | 7623 | * For NOMMU case, it's actively wrong: we'd change ->fd |
7621 | * fields in memory for the parent, but parent's fds | 7624 | * fields in memory for the parent, but parent's fds |
7622 | * aren't be moved, it would use wrong fd! | 7625 | * aren't moved, it would use wrong fd! |
7623 | * Reproducer: "cmd 3>FILE" in script. | 7626 | * Reproducer: "cmd 3>FILE" in script. |
7624 | * If we would call move_HFILEs_on_redirect(), child would: | 7627 | * If we would call move_HFILEs_on_redirect(), child would: |
7625 | * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10 | 7628 | * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10 |
@@ -7683,6 +7686,20 @@ static void restore_redirects(struct squirrel *sq) | |||
7683 | } | 7686 | } |
7684 | free(sq); | 7687 | free(sq); |
7685 | } | 7688 | } |
7689 | if (G.HFILE_stdin | ||
7690 | && G.HFILE_stdin->fd != STDIN_FILENO | ||
7691 | ) { | ||
7692 | /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r". | ||
7693 | * Redirect moves ->fd to e.g. 10, | ||
7694 | * and it is not restored above (we do not restore script fds | ||
7695 | * after redirects, we just use new, "moved" fds). | ||
7696 | * However for stdin, get_user_input() -> read_line_input(), | ||
7697 | * and read builtin, depend on fd == STDIN_FILENO. | ||
7698 | */ | ||
7699 | debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd); | ||
7700 | xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO); | ||
7701 | G.HFILE_stdin->fd = STDIN_FILENO; | ||
7702 | } | ||
7686 | 7703 | ||
7687 | /* If moved, G_interactive_fd stays on new fd, not restoring it */ | 7704 | /* If moved, G_interactive_fd stays on new fd, not restoring it */ |
7688 | } | 7705 | } |
@@ -8684,9 +8701,9 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
8684 | pi->cmds[i].pid = 0; | 8701 | pi->cmds[i].pid = 0; |
8685 | pi->alive_cmds--; | 8702 | pi->alive_cmds--; |
8686 | if (!pi->alive_cmds) { | 8703 | if (!pi->alive_cmds) { |
8687 | #if ENABLE_HUSH_BASH_COMPAT | 8704 | # if ENABLE_HUSH_BASH_COMPAT |
8688 | G.dead_job_exitcode = job_exited_or_stopped(pi); | 8705 | G.dead_job_exitcode = job_exited_or_stopped(pi); |
8689 | #endif | 8706 | # endif |
8690 | if (G_interactive_fd) { | 8707 | if (G_interactive_fd) { |
8691 | printf(JOB_STATUS_FORMAT, pi->jobid, | 8708 | printf(JOB_STATUS_FORMAT, pi->jobid, |
8692 | "Done", pi->cmdtext); | 8709 | "Done", pi->cmdtext); |
@@ -9824,9 +9841,12 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
9824 | IF_HUSH_MODE_X(G_x_mode = state;) | 9841 | IF_HUSH_MODE_X(G_x_mode = state;) |
9825 | IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);) | 9842 | IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);) |
9826 | break; | 9843 | break; |
9844 | case 'e': | ||
9845 | G.o_opt[OPT_O_ERREXIT] = state; | ||
9846 | break; | ||
9827 | case 'o': | 9847 | case 'o': |
9828 | if (!o_opt) { | 9848 | if (!o_opt) { |
9829 | /* "set -+o" without parameter. | 9849 | /* "set -o" or "set +o" without parameter. |
9830 | * in bash, set -o produces this output: | 9850 | * in bash, set -o produces this output: |
9831 | * pipefail off | 9851 | * pipefail off |
9832 | * and set +o: | 9852 | * and set +o: |
@@ -9847,9 +9867,7 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
9847 | G.o_opt[idx] = state; | 9867 | G.o_opt[idx] = state; |
9848 | break; | 9868 | break; |
9849 | } | 9869 | } |
9850 | case 'e': | 9870 | /* fall through to error */ |
9851 | G.o_opt[OPT_O_ERREXIT] = state; | ||
9852 | break; | ||
9853 | default: | 9871 | default: |
9854 | return EXIT_FAILURE; | 9872 | return EXIT_FAILURE; |
9855 | } | 9873 | } |
@@ -10213,8 +10231,6 @@ int hush_main(int argc, char **argv) | |||
10213 | G_saved_tty_pgrp = 0; | 10231 | G_saved_tty_pgrp = 0; |
10214 | } | 10232 | } |
10215 | } | 10233 | } |
10216 | // TODO: track & disallow any attempts of user | ||
10217 | // to (inadvertently) close/redirect G_interactive_fd | ||
10218 | } | 10234 | } |
10219 | debug_printf("interactive_fd:%d\n", G_interactive_fd); | 10235 | debug_printf("interactive_fd:%d\n", G_interactive_fd); |
10220 | if (G_interactive_fd) { | 10236 | if (G_interactive_fd) { |
@@ -10536,10 +10552,10 @@ static int FAST_FUNC builtin_type(char **argv) | |||
10536 | if (0) {} /* make conditional compile easier below */ | 10552 | if (0) {} /* make conditional compile easier below */ |
10537 | /*else if (find_alias(*argv)) | 10553 | /*else if (find_alias(*argv)) |
10538 | type = "an alias";*/ | 10554 | type = "an alias";*/ |
10539 | #if ENABLE_HUSH_FUNCTIONS | 10555 | # if ENABLE_HUSH_FUNCTIONS |
10540 | else if (find_function(*argv)) | 10556 | else if (find_function(*argv)) |
10541 | type = "a function"; | 10557 | type = "a function"; |
10542 | #endif | 10558 | # endif |
10543 | else if (find_builtin(*argv)) | 10559 | else if (find_builtin(*argv)) |
10544 | type = "a shell builtin"; | 10560 | type = "a shell builtin"; |
10545 | else if ((path = find_in_path(*argv)) != NULL) | 10561 | else if ((path = find_in_path(*argv)) != NULL) |
@@ -10594,11 +10610,11 @@ static int FAST_FUNC builtin_read(char **argv) | |||
10594 | * Option string must start with "sr" to match BUILTIN_READ_xxx | 10610 | * Option string must start with "sr" to match BUILTIN_READ_xxx |
10595 | */ | 10611 | */ |
10596 | params.read_flags = getopt32(argv, | 10612 | params.read_flags = getopt32(argv, |
10597 | #if BASH_READ_D | 10613 | # if BASH_READ_D |
10598 | "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d | 10614 | "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d |
10599 | #else | 10615 | # else |
10600 | "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u | 10616 | "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u |
10601 | #endif | 10617 | # endif |
10602 | ); | 10618 | ); |
10603 | if ((uint32_t)params.read_flags == (uint32_t)-1) | 10619 | if ((uint32_t)params.read_flags == (uint32_t)-1) |
10604 | return EXIT_FAILURE; | 10620 | return EXIT_FAILURE; |
@@ -10771,24 +10787,24 @@ static int FAST_FUNC builtin_export(char **argv) | |||
10771 | { | 10787 | { |
10772 | unsigned opt_unexport; | 10788 | unsigned opt_unexport; |
10773 | 10789 | ||
10774 | #if ENABLE_HUSH_EXPORT_N | 10790 | # if ENABLE_HUSH_EXPORT_N |
10775 | /* "!": do not abort on errors */ | 10791 | /* "!": do not abort on errors */ |
10776 | opt_unexport = getopt32(argv, "!n"); | 10792 | opt_unexport = getopt32(argv, "!n"); |
10777 | if (opt_unexport == (uint32_t)-1) | 10793 | if (opt_unexport == (uint32_t)-1) |
10778 | return EXIT_FAILURE; | 10794 | return EXIT_FAILURE; |
10779 | argv += optind; | 10795 | argv += optind; |
10780 | #else | 10796 | # else |
10781 | opt_unexport = 0; | 10797 | opt_unexport = 0; |
10782 | argv++; | 10798 | argv++; |
10783 | #endif | 10799 | # endif |
10784 | 10800 | ||
10785 | if (argv[0] == NULL) { | 10801 | if (argv[0] == NULL) { |
10786 | char **e = environ; | 10802 | char **e = environ; |
10787 | if (e) { | 10803 | if (e) { |
10788 | while (*e) { | 10804 | while (*e) { |
10789 | #if 0 | 10805 | # if 0 |
10790 | puts(*e++); | 10806 | puts(*e++); |
10791 | #else | 10807 | # else |
10792 | /* ash emits: export VAR='VAL' | 10808 | /* ash emits: export VAR='VAL' |
10793 | * bash: declare -x VAR="VAL" | 10809 | * bash: declare -x VAR="VAL" |
10794 | * we follow ash example */ | 10810 | * we follow ash example */ |
@@ -10801,7 +10817,7 @@ static int FAST_FUNC builtin_export(char **argv) | |||
10801 | printf("export %.*s", (int)(p - s) + 1, s); | 10817 | printf("export %.*s", (int)(p - s) + 1, s); |
10802 | print_escaped(p + 1); | 10818 | print_escaped(p + 1); |
10803 | putchar('\n'); | 10819 | putchar('\n'); |
10804 | #endif | 10820 | # endif |
10805 | } | 10821 | } |
10806 | /*fflush_all(); - done after each builtin anyway */ | 10822 | /*fflush_all(); - done after each builtin anyway */ |
10807 | } | 10823 | } |
@@ -10931,8 +10947,10 @@ static int FAST_FUNC builtin_set(char **argv) | |||
10931 | if (arg[0] != '+' && arg[0] != '-') | 10947 | if (arg[0] != '+' && arg[0] != '-') |
10932 | break; | 10948 | break; |
10933 | for (n = 1; arg[n]; ++n) { | 10949 | for (n = 1; arg[n]; ++n) { |
10934 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) | 10950 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) { |
10935 | goto error; | 10951 | bb_error_msg("%s: %s: invalid option", "set", arg); |
10952 | return EXIT_FAILURE; | ||
10953 | } | ||
10936 | if (arg[n] == 'o' && argv[1]) | 10954 | if (arg[n] == 'o' && argv[1]) |
10937 | argv++; | 10955 | argv++; |
10938 | } | 10956 | } |
@@ -10962,11 +10980,6 @@ static int FAST_FUNC builtin_set(char **argv) | |||
10962 | G.global_argc = 1 + string_array_len(pp + 1); | 10980 | G.global_argc = 1 + string_array_len(pp + 1); |
10963 | 10981 | ||
10964 | return EXIT_SUCCESS; | 10982 | return EXIT_SUCCESS; |
10965 | |||
10966 | /* Nothing known, so abort */ | ||
10967 | error: | ||
10968 | bb_error_msg("%s: %s: invalid option", "set", arg); | ||
10969 | return EXIT_FAILURE; | ||
10970 | } | 10983 | } |
10971 | #endif | 10984 | #endif |
10972 | 10985 | ||
@@ -11459,9 +11472,9 @@ static int FAST_FUNC builtin_kill(char **argv) | |||
11459 | 11472 | ||
11460 | #if ENABLE_HUSH_WAIT | 11473 | #if ENABLE_HUSH_WAIT |
11461 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 11474 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
11462 | #if !ENABLE_HUSH_JOB | 11475 | # if !ENABLE_HUSH_JOB |
11463 | # define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) | 11476 | # define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) |
11464 | #endif | 11477 | # endif |
11465 | static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid) | 11478 | static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid) |
11466 | { | 11479 | { |
11467 | int ret = 0; | 11480 | int ret = 0; |
@@ -11493,7 +11506,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
11493 | /* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */ | 11506 | /* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */ |
11494 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ | 11507 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ |
11495 | debug_printf_exec("checkjobs:%d\n", ret); | 11508 | debug_printf_exec("checkjobs:%d\n", ret); |
11496 | #if ENABLE_HUSH_JOB | 11509 | # if ENABLE_HUSH_JOB |
11497 | if (waitfor_pipe) { | 11510 | if (waitfor_pipe) { |
11498 | int rcode = job_exited_or_stopped(waitfor_pipe); | 11511 | int rcode = job_exited_or_stopped(waitfor_pipe); |
11499 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); | 11512 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); |
@@ -11503,7 +11516,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
11503 | break; | 11516 | break; |
11504 | } | 11517 | } |
11505 | } | 11518 | } |
11506 | #endif | 11519 | # endif |
11507 | /* if ECHILD, there are no children (ret is -1 or 0) */ | 11520 | /* if ECHILD, there are no children (ret is -1 or 0) */ |
11508 | /* if ret == 0, no children changed state */ | 11521 | /* if ret == 0, no children changed state */ |
11509 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ | 11522 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ |
@@ -11511,12 +11524,12 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
11511 | ret--; | 11524 | ret--; |
11512 | if (ret < 0) /* if ECHILD, may need to fix "ret" */ | 11525 | if (ret < 0) /* if ECHILD, may need to fix "ret" */ |
11513 | ret = 0; | 11526 | ret = 0; |
11514 | #if ENABLE_HUSH_BASH_COMPAT | 11527 | # if ENABLE_HUSH_BASH_COMPAT |
11515 | if (waitfor_pid == -1 && errno == ECHILD) { | 11528 | if (waitfor_pid == -1 && errno == ECHILD) { |
11516 | /* exitcode of "wait -n" with no children is 127, not 0 */ | 11529 | /* exitcode of "wait -n" with no children is 127, not 0 */ |
11517 | ret = 127; | 11530 | ret = 127; |
11518 | } | 11531 | } |
11519 | #endif | 11532 | # endif |
11520 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 11533 | sigprocmask(SIG_SETMASK, &oldset, NULL); |
11521 | break; | 11534 | break; |
11522 | } | 11535 | } |
@@ -11545,14 +11558,14 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
11545 | int status; | 11558 | int status; |
11546 | 11559 | ||
11547 | argv = skip_dash_dash(argv); | 11560 | argv = skip_dash_dash(argv); |
11548 | #if ENABLE_HUSH_BASH_COMPAT | 11561 | # if ENABLE_HUSH_BASH_COMPAT |
11549 | if (argv[0] && strcmp(argv[0], "-n") == 0) { | 11562 | if (argv[0] && strcmp(argv[0], "-n") == 0) { |
11550 | /* wait -n */ | 11563 | /* wait -n */ |
11551 | /* (bash accepts "wait -n PID" too and ignores PID) */ | 11564 | /* (bash accepts "wait -n PID" too and ignores PID) */ |
11552 | G.dead_job_exitcode = -1; | 11565 | G.dead_job_exitcode = -1; |
11553 | return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/); | 11566 | return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/); |
11554 | } | 11567 | } |
11555 | #endif | 11568 | # endif |
11556 | if (argv[0] == NULL) { | 11569 | if (argv[0] == NULL) { |
11557 | /* Don't care about wait results */ | 11570 | /* Don't care about wait results */ |
11558 | /* Note 1: must wait until there are no more children */ | 11571 | /* Note 1: must wait until there are no more children */ |
@@ -11576,7 +11589,7 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
11576 | do { | 11589 | do { |
11577 | pid_t pid = bb_strtou(*argv, NULL, 10); | 11590 | pid_t pid = bb_strtou(*argv, NULL, 10); |
11578 | if (errno || pid <= 0) { | 11591 | if (errno || pid <= 0) { |
11579 | #if ENABLE_HUSH_JOB | 11592 | # if ENABLE_HUSH_JOB |
11580 | if (argv[0][0] == '%') { | 11593 | if (argv[0][0] == '%') { |
11581 | struct pipe *wait_pipe; | 11594 | struct pipe *wait_pipe; |
11582 | ret = 127; /* bash compat for bad jobspecs */ | 11595 | ret = 127; /* bash compat for bad jobspecs */ |
@@ -11593,7 +11606,7 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
11593 | /* else: parse_jobspec() already emitted error msg */ | 11606 | /* else: parse_jobspec() already emitted error msg */ |
11594 | continue; | 11607 | continue; |
11595 | } | 11608 | } |
11596 | #endif | 11609 | # endif |
11597 | /* mimic bash message */ | 11610 | /* mimic bash message */ |
11598 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); | 11611 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); |
11599 | ret = EXIT_FAILURE; | 11612 | ret = EXIT_FAILURE; |
@@ -11615,7 +11628,7 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
11615 | ret = G.last_bg_pid_exitcode; | 11628 | ret = G.last_bg_pid_exitcode; |
11616 | } else { | 11629 | } else { |
11617 | /* Example: "wait 1". mimic bash message */ | 11630 | /* Example: "wait 1". mimic bash message */ |
11618 | bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); | 11631 | bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid); |
11619 | } | 11632 | } |
11620 | } else { | 11633 | } else { |
11621 | /* ??? */ | 11634 | /* ??? */ |
diff --git a/shell/hush_test/hush-redir/redir_stdin1.right b/shell/hush_test/hush-redir/redir_stdin1.right new file mode 100644 index 000000000..1c6217e92 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_stdin1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | #Testing that stdin redirect is restored | ||
2 | read2 | ||
3 | Ok:0 | ||
diff --git a/shell/hush_test/hush-redir/redir_stdin1.tests b/shell/hush_test/hush-redir/redir_stdin1.tests new file mode 100755 index 000000000..f72253f9d --- /dev/null +++ b/shell/hush_test/hush-redir/redir_stdin1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | #Testing that stdin redirect is restored | ||
2 | echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests | ||
3 | echo $r | ||
4 | read r | ||
5 | echo $r | ||
6 | ' | ||
7 | echo Ok:$? | ||
diff --git a/shell/math.c b/shell/math.c index eaf4f2453..aac5017d0 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -537,11 +537,40 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) | |||
537 | base = (unsigned)n; | 537 | base = (unsigned)n; |
538 | n = 0; | 538 | n = 0; |
539 | nptr = *endptr + 1; | 539 | nptr = *endptr + 1; |
540 | /* bash allows "N#" (empty "nnnn" part) */ | 540 | for (;;) { |
541 | while (isdigit(*nptr)) { | 541 | unsigned digit = (unsigned)*nptr - '0'; |
542 | if (digit >= 10 /* not 0..9 */ | ||
543 | && digit <= 'z' - '0' /* needed to reject e.g. $((64#~)) */ | ||
544 | ) { | ||
545 | /* in bases up to 36, case does not matter for a-z */ | ||
546 | digit = (unsigned)(*nptr | 0x20) - ('a' - 10); | ||
547 | if (base > 36 && *nptr <= '_') { | ||
548 | /* otherwise, A-Z,@,_ are 36-61,62,63 */ | ||
549 | if (*nptr == '_') | ||
550 | digit = 63; | ||
551 | else if (*nptr == '@') | ||
552 | digit = 62; | ||
553 | else if (digit < 36) /* A-Z */ | ||
554 | digit += 36 - 10; | ||
555 | else | ||
556 | break; /* error: one of [\]^ */ | ||
557 | } | ||
558 | //bb_error_msg("ch:'%c'%d digit:%u", *nptr, *nptr, digit); | ||
559 | //if (digit < 10) - example where we need this? | ||
560 | // break; | ||
561 | } | ||
562 | if (digit >= base) | ||
563 | break; | ||
542 | /* bash does not check for overflows */ | 564 | /* bash does not check for overflows */ |
543 | n = n * base + (*nptr++ - '0'); | 565 | n = n * base + digit; |
566 | nptr++; | ||
544 | } | 567 | } |
568 | /* Note: we do not set errno on bad chars, we just set a pointer | ||
569 | * to the first invalid char. For example, this allows | ||
570 | * "N#" (empty "nnnn" part): 64#+1 is a valid expression, | ||
571 | * it means 64# + 1, whereas 64#~... is not, since ~ is not a valid | ||
572 | * operator. | ||
573 | */ | ||
545 | *endptr = (char*)nptr; | 574 | *endptr = (char*)nptr; |
546 | return n; | 575 | return n; |
547 | } | 576 | } |
diff --git a/shell/shell_common.c b/shell/shell_common.c index 06a6b6e5f..be69ff249 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -371,52 +371,91 @@ shell_builtin_read(struct builtin_read_params *params) | |||
371 | struct limits { | 371 | struct limits { |
372 | uint8_t cmd; /* RLIMIT_xxx fit into it */ | 372 | uint8_t cmd; /* RLIMIT_xxx fit into it */ |
373 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ | 373 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
374 | const char *name; | ||
375 | }; | 374 | }; |
376 | 375 | ||
377 | static const struct limits limits_tbl[] = { | 376 | static const struct limits limits_tbl[] = { |
378 | { RLIMIT_CORE, 9, "core file size (blocks)" }, // -c | 377 | { RLIMIT_CORE, 9, }, // -c |
379 | { RLIMIT_DATA, 10, "data seg size (kb)" }, // -d | 378 | { RLIMIT_DATA, 10, }, // -d |
380 | { RLIMIT_NICE, 0, "scheduling priority" }, // -e | 379 | { RLIMIT_NICE, 0, }, // -e |
381 | { RLIMIT_FSIZE, 9, "file size (blocks)" }, // -f | 380 | { RLIMIT_FSIZE, 9, }, // -f |
382 | #define LIMIT_F_IDX 3 | 381 | #define LIMIT_F_IDX 3 |
383 | #ifdef RLIMIT_SIGPENDING | 382 | #ifdef RLIMIT_SIGPENDING |
384 | { RLIMIT_SIGPENDING, 0, "pending signals" }, // -i | 383 | { RLIMIT_SIGPENDING, 0, }, // -i |
385 | #endif | 384 | #endif |
386 | #ifdef RLIMIT_MEMLOCK | 385 | #ifdef RLIMIT_MEMLOCK |
387 | { RLIMIT_MEMLOCK, 10, "max locked memory (kb)" }, // -l | 386 | { RLIMIT_MEMLOCK, 10, }, // -l |
388 | #endif | 387 | #endif |
389 | #ifdef RLIMIT_RSS | 388 | #ifdef RLIMIT_RSS |
390 | { RLIMIT_RSS, 10, "max memory size (kb)" }, // -m | 389 | { RLIMIT_RSS, 10, }, // -m |
391 | #endif | 390 | #endif |
392 | #ifdef RLIMIT_NOFILE | 391 | #ifdef RLIMIT_NOFILE |
393 | { RLIMIT_NOFILE, 0, "open files" }, // -n | 392 | { RLIMIT_NOFILE, 0, }, // -n |
394 | #endif | 393 | #endif |
395 | #ifdef RLIMIT_MSGQUEUE | 394 | #ifdef RLIMIT_MSGQUEUE |
396 | { RLIMIT_MSGQUEUE, 0, "POSIX message queues (bytes)" }, // -q | 395 | { RLIMIT_MSGQUEUE, 0, }, // -q |
397 | #endif | 396 | #endif |
398 | #ifdef RLIMIT_RTPRIO | 397 | #ifdef RLIMIT_RTPRIO |
399 | { RLIMIT_RTPRIO, 0, "real-time priority" }, // -r | 398 | { RLIMIT_RTPRIO, 0, }, // -r |
400 | #endif | 399 | #endif |
401 | #ifdef RLIMIT_STACK | 400 | #ifdef RLIMIT_STACK |
402 | { RLIMIT_STACK, 10, "stack size (kb)" }, // -s | 401 | { RLIMIT_STACK, 10, }, // -s |
403 | #endif | 402 | #endif |
404 | #ifdef RLIMIT_CPU | 403 | #ifdef RLIMIT_CPU |
405 | { RLIMIT_CPU, 0, "cpu time (seconds)" }, // -t | 404 | { RLIMIT_CPU, 0, }, // -t |
406 | #endif | 405 | #endif |
407 | #ifdef RLIMIT_NPROC | 406 | #ifdef RLIMIT_NPROC |
408 | { RLIMIT_NPROC, 0, "max user processes" }, // -u | 407 | { RLIMIT_NPROC, 0, }, // -u |
409 | #endif | 408 | #endif |
410 | #ifdef RLIMIT_AS | 409 | #ifdef RLIMIT_AS |
411 | { RLIMIT_AS, 10, "virtual memory (kb)" }, // -v | 410 | { RLIMIT_AS, 10, }, // -v |
412 | #endif | 411 | #endif |
413 | #ifdef RLIMIT_LOCKS | 412 | #ifdef RLIMIT_LOCKS |
414 | { RLIMIT_LOCKS, 0, "file locks" }, // -x | 413 | { RLIMIT_LOCKS, 0, }, // -x |
415 | #endif | 414 | #endif |
416 | }; | 415 | }; |
417 | // bash also shows: | 416 | // bash also shows: |
418 | //pipe size (512 bytes, -p) 8 | 417 | //pipe size (512 bytes, -p) 8 |
419 | 418 | ||
419 | static const char limits_help[] ALIGN1 = | ||
420 | "core file size (blocks)" // -c | ||
421 | "\0""data seg size (kb)" // -d | ||
422 | "\0""scheduling priority" // -e | ||
423 | "\0""file size (blocks)" // -f | ||
424 | #ifdef RLIMIT_SIGPENDING | ||
425 | "\0""pending signals" // -i | ||
426 | #endif | ||
427 | #ifdef RLIMIT_MEMLOCK | ||
428 | "\0""max locked memory (kb)" // -l | ||
429 | #endif | ||
430 | #ifdef RLIMIT_RSS | ||
431 | "\0""max memory size (kb)" // -m | ||
432 | #endif | ||
433 | #ifdef RLIMIT_NOFILE | ||
434 | "\0""open files" // -n | ||
435 | #endif | ||
436 | #ifdef RLIMIT_MSGQUEUE | ||
437 | "\0""POSIX message queues (bytes)" // -q | ||
438 | #endif | ||
439 | #ifdef RLIMIT_RTPRIO | ||
440 | "\0""real-time priority" // -r | ||
441 | #endif | ||
442 | #ifdef RLIMIT_STACK | ||
443 | "\0""stack size (kb)" // -s | ||
444 | #endif | ||
445 | #ifdef RLIMIT_CPU | ||
446 | "\0""cpu time (seconds)" // -t | ||
447 | #endif | ||
448 | #ifdef RLIMIT_NPROC | ||
449 | "\0""max user processes" // -u | ||
450 | #endif | ||
451 | #ifdef RLIMIT_AS | ||
452 | "\0""virtual memory (kb)" // -v | ||
453 | #endif | ||
454 | #ifdef RLIMIT_LOCKS | ||
455 | "\0""file locks" // -x | ||
456 | #endif | ||
457 | ; | ||
458 | |||
420 | static const char limit_chars[] ALIGN1 = | 459 | static const char limit_chars[] ALIGN1 = |
421 | "c" | 460 | "c" |
422 | "d" | 461 | "d" |
@@ -607,10 +646,12 @@ shell_builtin_ulimit(char **argv) | |||
607 | if (!(opts & (OPT_hard | OPT_soft))) | 646 | if (!(opts & (OPT_hard | OPT_soft))) |
608 | opts |= (OPT_hard | OPT_soft); | 647 | opts |= (OPT_hard | OPT_soft); |
609 | if (opts & OPT_all) { | 648 | if (opts & OPT_all) { |
649 | const char *help = limits_help; | ||
610 | for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { | 650 | for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { |
611 | getrlimit(limits_tbl[i].cmd, &limit); | 651 | getrlimit(limits_tbl[i].cmd, &limit); |
612 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); | 652 | printf("%-32s(-%c) ", help, limit_chars[i]); |
613 | printlim(opts, &limit, &limits_tbl[i]); | 653 | printlim(opts, &limit, &limits_tbl[i]); |
654 | help += strlen(help) + 1; | ||
614 | } | 655 | } |
615 | return EXIT_SUCCESS; | 656 | return EXIT_SUCCESS; |
616 | } | 657 | } |
@@ -641,7 +682,7 @@ shell_builtin_ulimit(char **argv) | |||
641 | getrlimit(limits_tbl[i].cmd, &limit); | 682 | getrlimit(limits_tbl[i].cmd, &limit); |
642 | if (!val_str) { | 683 | if (!val_str) { |
643 | if (opt_cnt > 1) | 684 | if (opt_cnt > 1) |
644 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); | 685 | printf("%-32s(-%c) ", nth_string(limits_help, i), limit_chars[i]); |
645 | printlim(opts, &limit, &limits_tbl[i]); | 686 | printlim(opts, &limit, &limits_tbl[i]); |
646 | } else { | 687 | } else { |
647 | rlim_t val = RLIM_INFINITY; | 688 | rlim_t val = RLIM_INFINITY; |