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; |
