diff options
author | Ron Yorston <rmy@pobox.com> | 2017-08-02 11:33:53 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-08-02 11:33:53 +0100 |
commit | 8e20d4e9264a30dcc6d652943211668b1a6fe85b (patch) | |
tree | 7a7dcd4d55e2eb83374aed059a873486451c82e9 /shell | |
parent | c8b51e5f274ff044ba2f9f350ead478d6c2063aa (diff) | |
parent | ec05df13b0f3bc69074909f078f981f417d95c89 (diff) | |
download | busybox-w32-8e20d4e9264a30dcc6d652943211668b1a6fe85b.tar.gz busybox-w32-8e20d4e9264a30dcc6d652943211668b1a6fe85b.tar.bz2 busybox-w32-8e20d4e9264a30dcc6d652943211668b1a6fe85b.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
30 files changed, 708 insertions, 384 deletions
diff --git a/shell/ash.c b/shell/ash.c index 62f750ca3..18e53a0da 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -409,7 +409,6 @@ struct globals_misc { | |||
409 | #define EXERROR 1 /* a generic error */ | 409 | #define EXERROR 1 /* a generic error */ |
410 | #define EXEXIT 4 /* exit the shell */ | 410 | #define EXEXIT 4 /* exit the shell */ |
411 | 411 | ||
412 | smallint isloginsh; | ||
413 | char nullstr[1]; /* zero length string */ | 412 | char nullstr[1]; /* zero length string */ |
414 | 413 | ||
415 | char optlist[NOPTS]; | 414 | char optlist[NOPTS]; |
@@ -484,7 +483,6 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
484 | #define pending_int (G_misc.pending_int ) | 483 | #define pending_int (G_misc.pending_int ) |
485 | #define got_sigchld (G_misc.got_sigchld ) | 484 | #define got_sigchld (G_misc.got_sigchld ) |
486 | #define pending_sig (G_misc.pending_sig ) | 485 | #define pending_sig (G_misc.pending_sig ) |
487 | #define isloginsh (G_misc.isloginsh ) | ||
488 | #define nullstr (G_misc.nullstr ) | 486 | #define nullstr (G_misc.nullstr ) |
489 | #define optlist (G_misc.optlist ) | 487 | #define optlist (G_misc.optlist ) |
490 | #define sigmode (G_misc.sigmode ) | 488 | #define sigmode (G_misc.sigmode ) |
@@ -2117,7 +2115,7 @@ struct redirtab; | |||
2117 | struct globals_var { | 2115 | struct globals_var { |
2118 | struct shparam shellparam; /* $@ current positional parameters */ | 2116 | struct shparam shellparam; /* $@ current positional parameters */ |
2119 | struct redirtab *redirlist; | 2117 | struct redirtab *redirlist; |
2120 | int preverrout_fd; /* save fd2 before print debug if xflag is set. */ | 2118 | int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */ |
2121 | struct var *vartab[VTABSIZE]; | 2119 | struct var *vartab[VTABSIZE]; |
2122 | struct var varinit[ARRAY_SIZE(varinit_data)]; | 2120 | struct var varinit[ARRAY_SIZE(varinit_data)]; |
2123 | }; | 2121 | }; |
@@ -2596,8 +2594,20 @@ putprompt(const char *s) | |||
2596 | #endif | 2594 | #endif |
2597 | 2595 | ||
2598 | /* expandstr() needs parsing machinery, so it is far away ahead... */ | 2596 | /* expandstr() needs parsing machinery, so it is far away ahead... */ |
2599 | static const char *expandstr(const char *ps); | 2597 | static const char *expandstr(const char *ps, int syntax_type); |
2598 | /* Values for syntax param */ | ||
2599 | #define BASESYNTAX 0 /* not in quotes */ | ||
2600 | #define DQSYNTAX 1 /* in double quotes */ | ||
2601 | #define SQSYNTAX 2 /* in single quotes */ | ||
2602 | #define ARISYNTAX 3 /* in arithmetic */ | ||
2603 | #if ENABLE_ASH_EXPAND_PRMT | ||
2604 | # define PSSYNTAX 4 /* prompt. never passed to SIT() */ | ||
2605 | #endif | ||
2606 | /* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */ | ||
2600 | 2607 | ||
2608 | /* | ||
2609 | * called by editline -- any expansions to the prompt should be added here. | ||
2610 | */ | ||
2601 | static void | 2611 | static void |
2602 | setprompt_if(smallint do_set, int whichprompt) | 2612 | setprompt_if(smallint do_set, int whichprompt) |
2603 | { | 2613 | { |
@@ -2621,7 +2631,7 @@ setprompt_if(smallint do_set, int whichprompt) | |||
2621 | } | 2631 | } |
2622 | #if ENABLE_ASH_EXPAND_PRMT | 2632 | #if ENABLE_ASH_EXPAND_PRMT |
2623 | pushstackmark(&smark, stackblocksize()); | 2633 | pushstackmark(&smark, stackblocksize()); |
2624 | putprompt(expandstr(prompt)); | 2634 | putprompt(expandstr(prompt, PSSYNTAX)); |
2625 | popstackmark(&smark); | 2635 | popstackmark(&smark); |
2626 | #else | 2636 | #else |
2627 | putprompt(prompt); | 2637 | putprompt(prompt); |
@@ -3047,13 +3057,6 @@ enum { | |||
3047 | /* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, | 3057 | /* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, |
3048 | * caller must ensure proper cast on it if c is *char_ptr! | 3058 | * caller must ensure proper cast on it if c is *char_ptr! |
3049 | */ | 3059 | */ |
3050 | /* Values for syntax param */ | ||
3051 | #define BASESYNTAX 0 /* not in quotes */ | ||
3052 | #define DQSYNTAX 1 /* in double quotes */ | ||
3053 | #define SQSYNTAX 2 /* in single quotes */ | ||
3054 | #define ARISYNTAX 3 /* in arithmetic */ | ||
3055 | #define PSSYNTAX 4 /* prompt. never passed to SIT() */ | ||
3056 | |||
3057 | #if USE_SIT_FUNCTION | 3060 | #if USE_SIT_FUNCTION |
3058 | 3061 | ||
3059 | static int | 3062 | static int |
@@ -4953,6 +4956,10 @@ cmdputs(const char *s) | |||
4953 | /* These can only happen inside quotes */ | 4956 | /* These can only happen inside quotes */ |
4954 | cc[0] = c; | 4957 | cc[0] = c; |
4955 | str = cc; | 4958 | str = cc; |
4959 | //FIXME: | ||
4960 | // $ true $$ & | ||
4961 | // $ <cr> | ||
4962 | // [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok) | ||
4956 | c = '\\'; | 4963 | c = '\\'; |
4957 | break; | 4964 | break; |
4958 | default: | 4965 | default: |
@@ -5134,7 +5141,10 @@ cmdtxt(union node *n) | |||
5134 | cmdputs(utoa(n->nfile.fd)); | 5141 | cmdputs(utoa(n->nfile.fd)); |
5135 | cmdputs(p); | 5142 | cmdputs(p); |
5136 | if (n->type == NTOFD || n->type == NFROMFD) { | 5143 | if (n->type == NTOFD || n->type == NFROMFD) { |
5137 | cmdputs(utoa(n->ndup.dupfd)); | 5144 | if (n->ndup.dupfd >= 0) |
5145 | cmdputs(utoa(n->ndup.dupfd)); | ||
5146 | else | ||
5147 | cmdputs("-"); | ||
5138 | break; | 5148 | break; |
5139 | } | 5149 | } |
5140 | n = n->nfile.fname; | 5150 | n = n->nfile.fname; |
@@ -5517,7 +5527,7 @@ stoppedjobs(void) | |||
5517 | #undef EMPTY | 5527 | #undef EMPTY |
5518 | #undef CLOSED | 5528 | #undef CLOSED |
5519 | #define EMPTY -2 /* marks an unused slot in redirtab */ | 5529 | #define EMPTY -2 /* marks an unused slot in redirtab */ |
5520 | #define CLOSED -3 /* marks a slot of previously-closed fd */ | 5530 | #define CLOSED -1 /* marks a slot of previously-closed fd */ |
5521 | 5531 | ||
5522 | /* | 5532 | /* |
5523 | * Handle here documents. Normally we fork off a process to write the | 5533 | * Handle here documents. Normally we fork off a process to write the |
@@ -5713,73 +5723,182 @@ dup2_or_raise(int from, int to) | |||
5713 | } | 5723 | } |
5714 | return newfd; | 5724 | return newfd; |
5715 | } | 5725 | } |
5726 | static int | ||
5727 | fcntl_F_DUPFD(int fd, int avoid_fd) | ||
5728 | { | ||
5729 | int newfd; | ||
5730 | repeat: | ||
5731 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | ||
5732 | if (newfd < 0) { | ||
5733 | if (errno == EBUSY) | ||
5734 | goto repeat; | ||
5735 | if (errno == EINTR) | ||
5736 | goto repeat; | ||
5737 | } | ||
5738 | return newfd; | ||
5739 | } | ||
5740 | static int | ||
5741 | xdup_CLOEXEC_and_close(int fd, int avoid_fd) | ||
5742 | { | ||
5743 | int newfd; | ||
5744 | repeat: | ||
5745 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | ||
5746 | if (newfd < 0) { | ||
5747 | if (errno == EBUSY) | ||
5748 | goto repeat; | ||
5749 | if (errno == EINTR) | ||
5750 | goto repeat; | ||
5751 | /* fd was not open? */ | ||
5752 | if (errno == EBADF) | ||
5753 | return fd; | ||
5754 | ash_msg_and_raise_perror("%d", newfd); | ||
5755 | } | ||
5756 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
5757 | close(fd); | ||
5758 | return newfd; | ||
5759 | } | ||
5716 | 5760 | ||
5717 | /* Struct def and variable are moved down to the first usage site */ | 5761 | /* Struct def and variable are moved down to the first usage site */ |
5718 | struct two_fd_t { | 5762 | struct squirrel { |
5719 | int orig, copy; | 5763 | int orig_fd; |
5764 | int moved_to; | ||
5720 | }; | 5765 | }; |
5721 | struct redirtab { | 5766 | struct redirtab { |
5722 | struct redirtab *next; | 5767 | struct redirtab *next; |
5723 | int pair_count; | 5768 | int pair_count; |
5724 | struct two_fd_t two_fd[]; | 5769 | struct squirrel two_fd[]; |
5725 | }; | 5770 | }; |
5726 | #define redirlist (G_var.redirlist) | 5771 | #define redirlist (G_var.redirlist) |
5727 | enum { | ||
5728 | COPYFD_RESTORE = (int)~(INT_MAX), | ||
5729 | }; | ||
5730 | 5772 | ||
5731 | static int | 5773 | static void |
5732 | need_to_remember(struct redirtab *rp, int fd) | 5774 | add_squirrel_closed(struct redirtab *sq, int fd) |
5733 | { | 5775 | { |
5734 | int i; | 5776 | int i; |
5735 | 5777 | ||
5736 | if (!rp) /* remembering was not requested */ | 5778 | if (!sq) |
5737 | return 0; | 5779 | return; |
5738 | 5780 | ||
5739 | for (i = 0; i < rp->pair_count; i++) { | 5781 | for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) { |
5740 | if (rp->two_fd[i].orig == fd) { | 5782 | /* If we collide with an already moved fd... */ |
5741 | /* already remembered */ | 5783 | if (fd == sq->two_fd[i].orig_fd) { |
5742 | return 0; | 5784 | /* Examples: |
5785 | * "echo 3>FILE 3>&- 3>FILE" | ||
5786 | * "echo 3>&- 3>FILE" | ||
5787 | * No need for last redirect to insert | ||
5788 | * another "need to close 3" indicator. | ||
5789 | */ | ||
5790 | TRACE(("redirect_fd %d: already moved or closed\n", fd)); | ||
5791 | return; | ||
5743 | } | 5792 | } |
5744 | } | 5793 | } |
5745 | return 1; | 5794 | TRACE(("redirect_fd %d: previous fd was closed\n", fd)); |
5795 | sq->two_fd[i].orig_fd = fd; | ||
5796 | sq->two_fd[i].moved_to = CLOSED; | ||
5746 | } | 5797 | } |
5747 | 5798 | ||
5748 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ | ||
5749 | static int | 5799 | static int |
5750 | is_hidden_fd(struct redirtab *rp, int fd) | 5800 | save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) |
5751 | { | 5801 | { |
5752 | int i; | 5802 | int i, new_fd; |
5753 | struct parsefile *pf; | ||
5754 | 5803 | ||
5755 | if (fd == -1) | 5804 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ |
5805 | avoid_fd = 9; | ||
5806 | |||
5807 | #if JOBS | ||
5808 | if (fd == ttyfd) { | ||
5809 | /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */ | ||
5810 | ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd); | ||
5811 | TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd)); | ||
5812 | return 1; /* "we closed fd" */ | ||
5813 | } | ||
5814 | #endif | ||
5815 | /* Are we called from redirect(0)? E.g. redirect | ||
5816 | * in a forked child. No need to save fds, | ||
5817 | * we aren't going to use them anymore, ok to trash. | ||
5818 | */ | ||
5819 | if (!sq) | ||
5756 | return 0; | 5820 | return 0; |
5757 | /* Check open scripts' fds */ | 5821 | |
5758 | pf = g_parsefile; | 5822 | /* If this one of script's fds? */ |
5759 | while (pf) { | 5823 | if (fd != 0) { |
5760 | /* We skip pf_fd == 0 case because of the following case: | 5824 | struct parsefile *pf = g_parsefile; |
5761 | * $ ash # running ash interactively | 5825 | while (pf) { |
5762 | * $ . ./script.sh | 5826 | /* We skip fd == 0 case because of the following: |
5763 | * and in script.sh: "exec 9>&0". | 5827 | * $ ash # running ash interactively |
5764 | * Even though top-level pf_fd _is_ 0, | 5828 | * $ . ./script.sh |
5765 | * it's still ok to use it: "read" builtin uses it, | 5829 | * and in script.sh: "exec 9>&0". |
5766 | * why should we cripple "exec" builtin? | 5830 | * Even though top-level pf_fd _is_ 0, |
5767 | */ | 5831 | * it's still ok to use it: "read" builtin uses it, |
5768 | if (pf->pf_fd > 0 && fd == pf->pf_fd) { | 5832 | * why should we cripple "exec" builtin? |
5769 | return 1; | 5833 | */ |
5834 | if (fd == pf->pf_fd) { | ||
5835 | pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd); | ||
5836 | return 1; /* "we closed fd" */ | ||
5837 | } | ||
5838 | pf = pf->prev; | ||
5770 | } | 5839 | } |
5771 | pf = pf->prev; | ||
5772 | } | 5840 | } |
5773 | 5841 | ||
5774 | if (!rp) | 5842 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ |
5775 | return 0; | 5843 | |
5776 | /* Check saved fds of redirects */ | 5844 | /* First: do we collide with some already moved fds? */ |
5777 | fd |= COPYFD_RESTORE; | 5845 | for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) { |
5778 | for (i = 0; i < rp->pair_count; i++) { | 5846 | /* If we collide with an already moved fd... */ |
5779 | if (rp->two_fd[i].copy == fd) { | 5847 | if (fd == sq->two_fd[i].moved_to) { |
5780 | return 1; | 5848 | new_fd = fcntl_F_DUPFD(fd, avoid_fd); |
5849 | sq->two_fd[i].moved_to = new_fd; | ||
5850 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); | ||
5851 | if (new_fd < 0) /* what? */ | ||
5852 | xfunc_die(); | ||
5853 | return 0; /* "we did not close fd" */ | ||
5854 | } | ||
5855 | if (fd == sq->two_fd[i].orig_fd) { | ||
5856 | /* Example: echo Hello >/dev/null 1>&2 */ | ||
5857 | TRACE(("redirect_fd %d: already moved\n", fd)); | ||
5858 | return 0; /* "we did not close fd" */ | ||
5781 | } | 5859 | } |
5782 | } | 5860 | } |
5861 | |||
5862 | /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */ | ||
5863 | new_fd = fcntl_F_DUPFD(fd, avoid_fd); | ||
5864 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); | ||
5865 | if (new_fd < 0) { | ||
5866 | if (errno != EBADF) | ||
5867 | xfunc_die(); | ||
5868 | /* new_fd = CLOSED; - already is -1 */ | ||
5869 | } | ||
5870 | sq->two_fd[i].moved_to = new_fd; | ||
5871 | sq->two_fd[i].orig_fd = fd; | ||
5872 | |||
5873 | /* if we move stderr, let "set -x" code know */ | ||
5874 | if (fd == preverrout_fd) | ||
5875 | preverrout_fd = new_fd; | ||
5876 | |||
5877 | return 0; /* "we did not close fd" */ | ||
5878 | } | ||
5879 | |||
5880 | static int | ||
5881 | internally_opened_fd(int fd, struct redirtab *sq) | ||
5882 | { | ||
5883 | int i; | ||
5884 | #if JOBS | ||
5885 | if (fd == ttyfd) | ||
5886 | return 1; | ||
5887 | #endif | ||
5888 | /* If this one of script's fds? */ | ||
5889 | if (fd != 0) { | ||
5890 | struct parsefile *pf = g_parsefile; | ||
5891 | while (pf) { | ||
5892 | if (fd == pf->pf_fd) | ||
5893 | return 1; | ||
5894 | pf = pf->prev; | ||
5895 | } | ||
5896 | } | ||
5897 | |||
5898 | if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) { | ||
5899 | if (fd == sq->two_fd[i].moved_to) | ||
5900 | return 1; | ||
5901 | } | ||
5783 | return 0; | 5902 | return 0; |
5784 | } | 5903 | } |
5785 | 5904 | ||
@@ -5790,148 +5909,149 @@ is_hidden_fd(struct redirtab *rp, int fd) | |||
5790 | */ | 5909 | */ |
5791 | /* flags passed to redirect */ | 5910 | /* flags passed to redirect */ |
5792 | #define REDIR_PUSH 01 /* save previous values of file descriptors */ | 5911 | #define REDIR_PUSH 01 /* save previous values of file descriptors */ |
5793 | #define REDIR_SAVEFD2 03 /* set preverrout */ | ||
5794 | static void | 5912 | static void |
5795 | redirect(union node *redir, int flags) | 5913 | redirect(union node *redir, int flags) |
5796 | { | 5914 | { |
5797 | struct redirtab *sv; | 5915 | struct redirtab *sv; |
5798 | int sv_pos; | 5916 | int sv_pos; |
5799 | int i; | ||
5800 | int fd; | ||
5801 | int newfd; | ||
5802 | int copied_fd2 = -1; | ||
5803 | 5917 | ||
5804 | if (!redir) { | 5918 | if (!redir) |
5805 | return; | 5919 | return; |
5806 | } | ||
5807 | 5920 | ||
5808 | sv = NULL; | ||
5809 | sv_pos = 0; | 5921 | sv_pos = 0; |
5922 | sv = NULL; | ||
5810 | INT_OFF; | 5923 | INT_OFF; |
5811 | if (flags & REDIR_PUSH) { | 5924 | if (flags & REDIR_PUSH) |
5812 | union node *tmp = redir; | 5925 | sv = redirlist; |
5813 | do { | ||
5814 | sv_pos++; | ||
5815 | #if BASH_REDIR_OUTPUT | ||
5816 | if (tmp->nfile.type == NTO2) | ||
5817 | sv_pos++; | ||
5818 | #endif | ||
5819 | tmp = tmp->nfile.next; | ||
5820 | } while (tmp); | ||
5821 | sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0])); | ||
5822 | sv->next = redirlist; | ||
5823 | sv->pair_count = sv_pos; | ||
5824 | redirlist = sv; | ||
5825 | while (sv_pos > 0) { | ||
5826 | sv_pos--; | ||
5827 | sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY; | ||
5828 | } | ||
5829 | } | ||
5830 | |||
5831 | do { | 5926 | do { |
5832 | int right_fd = -1; | 5927 | int fd; |
5928 | int newfd; | ||
5929 | int close_fd; | ||
5930 | int closed; | ||
5931 | |||
5833 | fd = redir->nfile.fd; | 5932 | fd = redir->nfile.fd; |
5834 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { | 5933 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { |
5835 | right_fd = redir->ndup.dupfd; | 5934 | //bb_error_msg("doing %d > %d", fd, newfd); |
5836 | //bb_error_msg("doing %d > %d", fd, right_fd); | 5935 | newfd = redir->ndup.dupfd; |
5837 | /* redirect from/to same file descriptor? */ | 5936 | close_fd = -1; |
5838 | if (right_fd == fd) | ||
5839 | continue; | ||
5840 | /* "echo >&10" and 10 is a fd opened to a sh script? */ | ||
5841 | if (is_hidden_fd(sv, right_fd)) { | ||
5842 | errno = EBADF; /* as if it is closed */ | ||
5843 | ash_msg_and_raise_perror("%d", right_fd); | ||
5844 | } | ||
5845 | newfd = -1; | ||
5846 | } else { | 5937 | } else { |
5847 | newfd = openredirect(redir); /* always >= 0 */ | 5938 | newfd = openredirect(redir); /* always >= 0 */ |
5848 | if (fd == newfd) { | 5939 | if (fd == newfd) { |
5849 | /* Descriptor wasn't open before redirect. | 5940 | /* open() gave us precisely the fd we wanted. |
5850 | * Mark it for close in the future */ | 5941 | * This means that this fd was not busy |
5851 | if (need_to_remember(sv, fd)) { | 5942 | * (not opened to anywhere). |
5852 | goto remember_to_close; | 5943 | * Remember to close it on restore: |
5853 | } | 5944 | */ |
5945 | add_squirrel_closed(sv, fd); | ||
5854 | continue; | 5946 | continue; |
5855 | } | 5947 | } |
5948 | close_fd = newfd; | ||
5856 | } | 5949 | } |
5857 | #if BASH_REDIR_OUTPUT | 5950 | |
5858 | redirect_more: | 5951 | if (fd == newfd) |
5859 | #endif | 5952 | continue; |
5860 | if (need_to_remember(sv, fd)) { | 5953 | |
5861 | /* Copy old descriptor */ | 5954 | /* if "N>FILE": move newfd to fd */ |
5862 | /* Careful to not accidentally "save" | 5955 | /* if "N>&M": dup newfd to fd */ |
5863 | * to the same fd as right side fd in N>&M */ | 5956 | /* if "N>&-": close fd (newfd is -1) */ |
5864 | int minfd = right_fd < 10 ? 10 : right_fd + 1; | 5957 | |
5865 | #if defined(F_DUPFD_CLOEXEC) | 5958 | IF_BASH_REDIR_OUTPUT(redirect_more:) |
5866 | i = fcntl(fd, F_DUPFD_CLOEXEC, minfd); | 5959 | |
5867 | #else | 5960 | closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv); |
5868 | i = fcntl(fd, F_DUPFD, minfd); | 5961 | if (newfd == -1) { |
5869 | #endif | 5962 | /* "N>&-" means "close me" */ |
5870 | if (i == -1) { | 5963 | if (!closed) { |
5871 | i = errno; | 5964 | /* ^^^ optimization: saving may already |
5872 | if (i != EBADF) { | 5965 | * have closed it. If not... */ |
5873 | /* Strange error (e.g. "too many files" EMFILE?) */ | 5966 | close(fd); |
5874 | if (newfd >= 0) | 5967 | } |
5875 | close(newfd); | 5968 | } else { |
5876 | errno = i; | 5969 | /* if newfd is a script fd or saved fd, simulate EBADF */ |
5877 | ash_msg_and_raise_perror("%d", fd); | 5970 | if (internally_opened_fd(newfd, sv)) { |
5878 | /* NOTREACHED */ | 5971 | errno = EBADF; |
5879 | } | 5972 | ash_msg_and_raise_perror("%d", newfd); |
5880 | /* EBADF: it is not open - good, remember to close it */ | ||
5881 | remember_to_close: | ||
5882 | i = CLOSED; | ||
5883 | } else { /* fd is open, save its copy */ | ||
5884 | #if !defined(F_DUPFD_CLOEXEC) | ||
5885 | fcntl(i, F_SETFD, FD_CLOEXEC); | ||
5886 | #endif | ||
5887 | /* "exec fd>&-" should not close fds | ||
5888 | * which point to script file(s). | ||
5889 | * Force them to be restored afterwards */ | ||
5890 | if (is_hidden_fd(sv, fd)) | ||
5891 | i |= COPYFD_RESTORE; | ||
5892 | } | ||
5893 | if (fd == 2) | ||
5894 | copied_fd2 = i; | ||
5895 | sv->two_fd[sv_pos].orig = fd; | ||
5896 | sv->two_fd[sv_pos].copy = i; | ||
5897 | sv_pos++; | ||
5898 | } | ||
5899 | if (newfd < 0) { | ||
5900 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ | ||
5901 | if (redir->ndup.dupfd < 0) { /* "fd>&-" */ | ||
5902 | /* Don't want to trigger debugging */ | ||
5903 | if (fd != -1) | ||
5904 | close(fd); | ||
5905 | } else { | ||
5906 | dup2_or_raise(redir->ndup.dupfd, fd); | ||
5907 | } | 5973 | } |
5908 | } else if (fd != newfd) { /* move newfd to fd */ | ||
5909 | dup2_or_raise(newfd, fd); | 5974 | dup2_or_raise(newfd, fd); |
5975 | if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */ | ||
5976 | close(close_fd); | ||
5910 | #if BASH_REDIR_OUTPUT | 5977 | #if BASH_REDIR_OUTPUT |
5911 | if (!(redir->nfile.type == NTO2 && fd == 2)) | 5978 | if (redir->nfile.type == NTO2 && fd == 1) { |
5979 | /* ">&FILE". we already redirected to 1, now copy 1 to 2 */ | ||
5980 | fd = 2; | ||
5981 | newfd = 1; | ||
5982 | close_fd = -1; | ||
5983 | goto redirect_more; | ||
5984 | } | ||
5912 | #endif | 5985 | #endif |
5913 | close(newfd); | ||
5914 | } | 5986 | } |
5987 | } while ((redir = redir->nfile.next) != NULL); | ||
5988 | INT_ON; | ||
5989 | |||
5990 | //dash:#define REDIR_SAVEFD2 03 /* set preverrout */ | ||
5991 | #define REDIR_SAVEFD2 0 | ||
5992 | // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test | ||
5993 | // triggers for pure REDIR_PUSH too. Thus, this is done almost always, | ||
5994 | // not only for calls with flags containing REDIR_SAVEFD2. | ||
5995 | // We do this unconditionally (see save_fd_on_redirect()). | ||
5996 | //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) | ||
5997 | // preverrout_fd = copied_fd2; | ||
5998 | } | ||
5999 | |||
6000 | static int | ||
6001 | redirectsafe(union node *redir, int flags) | ||
6002 | { | ||
6003 | int err; | ||
6004 | volatile int saveint; | ||
6005 | struct jmploc *volatile savehandler = exception_handler; | ||
6006 | struct jmploc jmploc; | ||
6007 | |||
6008 | SAVE_INT(saveint); | ||
6009 | /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ | ||
6010 | err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */ | ||
6011 | if (!err) { | ||
6012 | exception_handler = &jmploc; | ||
6013 | redirect(redir, flags); | ||
6014 | } | ||
6015 | exception_handler = savehandler; | ||
6016 | if (err && exception_type != EXERROR) | ||
6017 | longjmp(exception_handler->loc, 1); | ||
6018 | RESTORE_INT(saveint); | ||
6019 | return err; | ||
6020 | } | ||
6021 | |||
6022 | static struct redirtab* | ||
6023 | pushredir(union node *redir) | ||
6024 | { | ||
6025 | struct redirtab *sv; | ||
6026 | int i; | ||
6027 | |||
6028 | if (!redir) | ||
6029 | return redirlist; | ||
6030 | |||
6031 | i = 0; | ||
6032 | do { | ||
6033 | i++; | ||
5915 | #if BASH_REDIR_OUTPUT | 6034 | #if BASH_REDIR_OUTPUT |
5916 | if (redir->nfile.type == NTO2 && fd == 1) { | 6035 | if (redir->nfile.type == NTO2) |
5917 | /* We already redirected it to fd 1, now copy it to 2 */ | 6036 | i++; |
5918 | newfd = 1; | ||
5919 | fd = 2; | ||
5920 | goto redirect_more; | ||
5921 | } | ||
5922 | #endif | 6037 | #endif |
5923 | } while ((redir = redir->nfile.next) != NULL); | 6038 | redir = redir->nfile.next; |
6039 | } while (redir); | ||
5924 | 6040 | ||
5925 | INT_ON; | 6041 | sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0])); |
5926 | if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) | 6042 | sv->pair_count = i; |
5927 | preverrout_fd = copied_fd2; | 6043 | while (--i >= 0) |
6044 | sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY; | ||
6045 | sv->next = redirlist; | ||
6046 | redirlist = sv; | ||
6047 | return sv->next; | ||
5928 | } | 6048 | } |
5929 | 6049 | ||
5930 | /* | 6050 | /* |
5931 | * Undo the effects of the last redirection. | 6051 | * Undo the effects of the last redirection. |
5932 | */ | 6052 | */ |
5933 | static void | 6053 | static void |
5934 | popredir(int drop, int restore) | 6054 | popredir(int drop) |
5935 | { | 6055 | { |
5936 | struct redirtab *rp; | 6056 | struct redirtab *rp; |
5937 | int i; | 6057 | int i; |
@@ -5941,20 +6061,19 @@ popredir(int drop, int restore) | |||
5941 | INT_OFF; | 6061 | INT_OFF; |
5942 | rp = redirlist; | 6062 | rp = redirlist; |
5943 | for (i = 0; i < rp->pair_count; i++) { | 6063 | for (i = 0; i < rp->pair_count; i++) { |
5944 | int fd = rp->two_fd[i].orig; | 6064 | int fd = rp->two_fd[i].orig_fd; |
5945 | int copy = rp->two_fd[i].copy; | 6065 | int copy = rp->two_fd[i].moved_to; |
5946 | if (copy == CLOSED) { | 6066 | if (copy == CLOSED) { |
5947 | if (!drop) | 6067 | if (!drop) |
5948 | close(fd); | 6068 | close(fd); |
5949 | continue; | 6069 | continue; |
5950 | } | 6070 | } |
5951 | if (copy != EMPTY) { | 6071 | if (copy != EMPTY) { |
5952 | if (!drop || (restore && (copy & COPYFD_RESTORE))) { | 6072 | if (!drop) { |
5953 | copy &= ~COPYFD_RESTORE; | ||
5954 | /*close(fd);*/ | 6073 | /*close(fd);*/ |
5955 | dup2_or_raise(copy, fd); | 6074 | dup2_or_raise(copy, fd); |
5956 | } | 6075 | } |
5957 | close(copy & ~COPYFD_RESTORE); | 6076 | close(copy); |
5958 | } | 6077 | } |
5959 | } | 6078 | } |
5960 | redirlist = rp->next; | 6079 | redirlist = rp->next; |
@@ -5962,30 +6081,11 @@ popredir(int drop, int restore) | |||
5962 | INT_ON; | 6081 | INT_ON; |
5963 | } | 6082 | } |
5964 | 6083 | ||
5965 | /* | 6084 | static void |
5966 | * Undo all redirections. Called on error or interrupt. | 6085 | unwindredir(struct redirtab *stop) |
5967 | */ | ||
5968 | |||
5969 | static int | ||
5970 | redirectsafe(union node *redir, int flags) | ||
5971 | { | 6086 | { |
5972 | int err; | 6087 | while (redirlist != stop) |
5973 | volatile int saveint; | 6088 | popredir(/*drop:*/ 0); |
5974 | struct jmploc *volatile savehandler = exception_handler; | ||
5975 | struct jmploc jmploc; | ||
5976 | |||
5977 | SAVE_INT(saveint); | ||
5978 | /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ | ||
5979 | err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; | ||
5980 | if (!err) { | ||
5981 | exception_handler = &jmploc; | ||
5982 | redirect(redir, flags); | ||
5983 | } | ||
5984 | exception_handler = savehandler; | ||
5985 | if (err && exception_type != EXERROR) | ||
5986 | longjmp(exception_handler->loc, 1); | ||
5987 | RESTORE_INT(saveint); | ||
5988 | return err; | ||
5989 | } | 6089 | } |
5990 | 6090 | ||
5991 | 6091 | ||
@@ -8076,7 +8176,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
8076 | clearenv(); | 8176 | clearenv(); |
8077 | while (*envp) | 8177 | while (*envp) |
8078 | putenv(*envp++); | 8178 | putenv(*envp++); |
8079 | popredir(/*drop:*/ 1, /*restore:*/ 0); | 8179 | popredir(/*drop:*/ 1); |
8080 | run_applet_no_and_exit(applet_no, cmd, argv); | 8180 | run_applet_no_and_exit(applet_no, cmd, argv); |
8081 | } | 8181 | } |
8082 | /* re-exec ourselves with the new arguments */ | 8182 | /* re-exec ourselves with the new arguments */ |
@@ -9159,12 +9259,13 @@ evaltree(union node *n, int flags) | |||
9159 | goto setstatus; | 9259 | goto setstatus; |
9160 | case NREDIR: | 9260 | case NREDIR: |
9161 | expredir(n->nredir.redirect); | 9261 | expredir(n->nredir.redirect); |
9262 | pushredir(n->nredir.redirect); | ||
9162 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | 9263 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); |
9163 | if (!status) { | 9264 | if (!status) { |
9164 | status = evaltree(n->nredir.n, flags & EV_TESTED); | 9265 | status = evaltree(n->nredir.n, flags & EV_TESTED); |
9165 | } | 9266 | } |
9166 | if (n->nredir.redirect) | 9267 | if (n->nredir.redirect) |
9167 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); | 9268 | popredir(/*drop:*/ 0); |
9168 | goto setstatus; | 9269 | goto setstatus; |
9169 | case NCMD: | 9270 | case NCMD: |
9170 | evalfn = evalcommand; | 9271 | evalfn = evalcommand; |
@@ -10079,6 +10180,7 @@ evalcommand(union node *cmd, int flags) | |||
10079 | "\0\0", bltincmd /* why three NULs? */ | 10180 | "\0\0", bltincmd /* why three NULs? */ |
10080 | }; | 10181 | }; |
10081 | struct localvar_list *localvar_stop; | 10182 | struct localvar_list *localvar_stop; |
10183 | struct redirtab *redir_stop; | ||
10082 | struct stackmark smark; | 10184 | struct stackmark smark; |
10083 | union node *argp; | 10185 | union node *argp; |
10084 | struct arglist arglist; | 10186 | struct arglist arglist; |
@@ -10093,9 +10195,7 @@ evalcommand(union node *cmd, int flags) | |||
10093 | int spclbltin; | 10195 | int spclbltin; |
10094 | int status; | 10196 | int status; |
10095 | char **nargv; | 10197 | char **nargv; |
10096 | struct builtincmd *bcmd; | ||
10097 | smallint cmd_is_exec; | 10198 | smallint cmd_is_exec; |
10098 | smallint pseudovarflag = 0; | ||
10099 | 10199 | ||
10100 | /* First expand the arguments. */ | 10200 | /* First expand the arguments. */ |
10101 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | 10201 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); |
@@ -10112,21 +10212,24 @@ evalcommand(union node *cmd, int flags) | |||
10112 | 10212 | ||
10113 | argc = 0; | 10213 | argc = 0; |
10114 | if (cmd->ncmd.args) { | 10214 | if (cmd->ncmd.args) { |
10215 | struct builtincmd *bcmd; | ||
10216 | smallint pseudovarflag; | ||
10217 | |||
10115 | bcmd = find_builtin(cmd->ncmd.args->narg.text); | 10218 | bcmd = find_builtin(cmd->ncmd.args->narg.text); |
10116 | pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); | 10219 | pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); |
10117 | } | ||
10118 | 10220 | ||
10119 | for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { | 10221 | for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { |
10120 | struct strlist **spp; | 10222 | struct strlist **spp; |
10121 | 10223 | ||
10122 | spp = arglist.lastp; | 10224 | spp = arglist.lastp; |
10123 | if (pseudovarflag && isassignment(argp->narg.text)) | 10225 | if (pseudovarflag && isassignment(argp->narg.text)) |
10124 | expandarg(argp, &arglist, EXP_VARTILDE); | 10226 | expandarg(argp, &arglist, EXP_VARTILDE); |
10125 | else | 10227 | else |
10126 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | 10228 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); |
10127 | 10229 | ||
10128 | for (sp = *spp; sp; sp = sp->next) | 10230 | for (sp = *spp; sp; sp = sp->next) |
10129 | argc++; | 10231 | argc++; |
10232 | } | ||
10130 | } | 10233 | } |
10131 | 10234 | ||
10132 | /* Reserve one extra spot at the front for shellexec. */ | 10235 | /* Reserve one extra spot at the front for shellexec. */ |
@@ -10142,8 +10245,9 @@ evalcommand(union node *cmd, int flags) | |||
10142 | if (iflag && funcnest == 0 && argc > 0) | 10245 | if (iflag && funcnest == 0 && argc > 0) |
10143 | lastarg = nargv[-1]; | 10246 | lastarg = nargv[-1]; |
10144 | 10247 | ||
10145 | preverrout_fd = 2; | ||
10146 | expredir(cmd->ncmd.redirect); | 10248 | expredir(cmd->ncmd.redirect); |
10249 | redir_stop = pushredir(cmd->ncmd.redirect); | ||
10250 | preverrout_fd = 2; | ||
10147 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); | 10251 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); |
10148 | 10252 | ||
10149 | path = vpath.var_text; | 10253 | path = vpath.var_text; |
@@ -10169,7 +10273,7 @@ evalcommand(union node *cmd, int flags) | |||
10169 | if (xflag) { | 10273 | if (xflag) { |
10170 | const char *pfx = ""; | 10274 | const char *pfx = ""; |
10171 | 10275 | ||
10172 | fdprintf(preverrout_fd, "%s", expandstr(ps4val())); | 10276 | fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX)); |
10173 | 10277 | ||
10174 | sp = varlist.list; | 10278 | sp = varlist.list; |
10175 | while (sp) { | 10279 | while (sp) { |
@@ -10365,7 +10469,8 @@ evalcommand(union node *cmd, int flags) | |||
10365 | 10469 | ||
10366 | out: | 10470 | out: |
10367 | if (cmd->ncmd.redirect) | 10471 | if (cmd->ncmd.redirect) |
10368 | popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); | 10472 | popredir(/*drop:*/ cmd_is_exec); |
10473 | unwindredir(redir_stop); | ||
10369 | unwindlocalvars(localvar_stop); | 10474 | unwindlocalvars(localvar_stop); |
10370 | if (lastarg) { | 10475 | if (lastarg) { |
10371 | /* dsl: I think this is intended to be used to support | 10476 | /* dsl: I think this is intended to be used to support |
@@ -10482,6 +10587,7 @@ static smallint checkkwd; | |||
10482 | #define CHKALIAS 0x1 | 10587 | #define CHKALIAS 0x1 |
10483 | #define CHKKWD 0x2 | 10588 | #define CHKKWD 0x2 |
10484 | #define CHKNL 0x4 | 10589 | #define CHKNL 0x4 |
10590 | #define CHKEOFMARK 0x8 | ||
10485 | 10591 | ||
10486 | /* | 10592 | /* |
10487 | * Push a string back onto the input at this current parsefile level. | 10593 | * Push a string back onto the input at this current parsefile level. |
@@ -10794,31 +10900,6 @@ pgetc_without_PEOA(void) | |||
10794 | #endif | 10900 | #endif |
10795 | 10901 | ||
10796 | /* | 10902 | /* |
10797 | * Read a line from the script. | ||
10798 | */ | ||
10799 | static char * | ||
10800 | pfgets(char *line, int len) | ||
10801 | { | ||
10802 | char *p = line; | ||
10803 | int nleft = len; | ||
10804 | int c; | ||
10805 | |||
10806 | while (--nleft > 0) { | ||
10807 | c = pgetc_without_PEOA(); | ||
10808 | if (c == PEOF) { | ||
10809 | if (p == line) | ||
10810 | return NULL; | ||
10811 | break; | ||
10812 | } | ||
10813 | *p++ = c; | ||
10814 | if (c == '\n') | ||
10815 | break; | ||
10816 | } | ||
10817 | *p = '\0'; | ||
10818 | return line; | ||
10819 | } | ||
10820 | |||
10821 | /* | ||
10822 | * Undo a call to pgetc. Only two characters may be pushed back. | 10903 | * Undo a call to pgetc. Only two characters may be pushed back. |
10823 | * PEOF may be pushed back. | 10904 | * PEOF may be pushed back. |
10824 | */ | 10905 | */ |
@@ -11127,7 +11208,7 @@ setoption(int flag, int val) | |||
11127 | /* NOTREACHED */ | 11208 | /* NOTREACHED */ |
11128 | } | 11209 | } |
11129 | static int | 11210 | static int |
11130 | options(int cmdline) | 11211 | options(int cmdline, int *login_sh) |
11131 | { | 11212 | { |
11132 | char *p; | 11213 | char *p; |
11133 | int val; | 11214 | int val; |
@@ -11168,11 +11249,14 @@ options(int cmdline) | |||
11168 | if (*argptr) | 11249 | if (*argptr) |
11169 | argptr++; | 11250 | argptr++; |
11170 | } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ | 11251 | } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ |
11171 | isloginsh = 1; | 11252 | if (login_sh) |
11253 | *login_sh = 1; | ||
11172 | /* bash does not accept +-login, we also won't */ | 11254 | /* bash does not accept +-login, we also won't */ |
11173 | } else if (cmdline && val && (c == '-')) { /* long options */ | 11255 | } else if (cmdline && val && (c == '-')) { /* long options */ |
11174 | if (strcmp(p, "login") == 0) | 11256 | if (strcmp(p, "login") == 0) { |
11175 | isloginsh = 1; | 11257 | if (login_sh) |
11258 | *login_sh = 1; | ||
11259 | } | ||
11176 | break; | 11260 | break; |
11177 | } else { | 11261 | } else { |
11178 | setoption(c, val); | 11262 | setoption(c, val); |
@@ -11256,7 +11340,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
11256 | return showvars(nullstr, 0, VUNSET); | 11340 | return showvars(nullstr, 0, VUNSET); |
11257 | 11341 | ||
11258 | INT_OFF; | 11342 | INT_OFF; |
11259 | retval = options(/*cmdline:*/ 0); | 11343 | retval = options(/*cmdline:*/ 0, NULL); |
11260 | if (retval == 0) { /* if no parse error... */ | 11344 | if (retval == 0) { /* if no parse error... */ |
11261 | optschanged(); | 11345 | optschanged(); |
11262 | if (*argptr != NULL) { | 11346 | if (*argptr != NULL) { |
@@ -11453,8 +11537,6 @@ raise_error_unexpected_syntax(int token) | |||
11453 | /* NOTREACHED */ | 11537 | /* NOTREACHED */ |
11454 | } | 11538 | } |
11455 | 11539 | ||
11456 | #define EOFMARKLEN 79 | ||
11457 | |||
11458 | /* parsing is heavily cross-recursive, need these forward decls */ | 11540 | /* parsing is heavily cross-recursive, need these forward decls */ |
11459 | static union node *andor(void); | 11541 | static union node *andor(void); |
11460 | static union node *pipeline(void); | 11542 | static union node *pipeline(void); |
@@ -11634,43 +11716,22 @@ fixredir(union node *n, const char *text, int err) | |||
11634 | } | 11716 | } |
11635 | } | 11717 | } |
11636 | 11718 | ||
11637 | /* | ||
11638 | * Returns true if the text contains nothing to expand (no dollar signs | ||
11639 | * or backquotes). | ||
11640 | */ | ||
11641 | static int | ||
11642 | noexpand(const char *text) | ||
11643 | { | ||
11644 | unsigned char c; | ||
11645 | |||
11646 | while ((c = *text++) != '\0') { | ||
11647 | if (c == CTLQUOTEMARK) | ||
11648 | continue; | ||
11649 | if (c == CTLESC) | ||
11650 | text++; | ||
11651 | else if (SIT(c, BASESYNTAX) == CCTL) | ||
11652 | return 0; | ||
11653 | } | ||
11654 | return 1; | ||
11655 | } | ||
11656 | |||
11657 | static void | 11719 | static void |
11658 | parsefname(void) | 11720 | parsefname(void) |
11659 | { | 11721 | { |
11660 | union node *n = redirnode; | 11722 | union node *n = redirnode; |
11661 | 11723 | ||
11724 | if (n->type == NHERE) | ||
11725 | checkkwd = CHKEOFMARK; | ||
11662 | if (readtoken() != TWORD) | 11726 | if (readtoken() != TWORD) |
11663 | raise_error_unexpected_syntax(-1); | 11727 | raise_error_unexpected_syntax(-1); |
11664 | if (n->type == NHERE) { | 11728 | if (n->type == NHERE) { |
11665 | struct heredoc *here = heredoc; | 11729 | struct heredoc *here = heredoc; |
11666 | struct heredoc *p; | 11730 | struct heredoc *p; |
11667 | int i; | ||
11668 | 11731 | ||
11669 | if (quoteflag == 0) | 11732 | if (quoteflag == 0) |
11670 | n->type = NXHERE; | 11733 | n->type = NXHERE; |
11671 | TRACE(("Here document %d\n", n->type)); | 11734 | TRACE(("Here document %d\n", n->type)); |
11672 | if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) | ||
11673 | raise_error_syntax("illegal eof marker for << redirection"); | ||
11674 | rmescapes(wordtext, 0); | 11735 | rmescapes(wordtext, 0); |
11675 | here->eofmark = wordtext; | 11736 | here->eofmark = wordtext; |
11676 | here->next = NULL; | 11737 | here->next = NULL; |
@@ -12062,6 +12123,15 @@ decode_dollar_squote(void) | |||
12062 | } | 12123 | } |
12063 | #endif | 12124 | #endif |
12064 | 12125 | ||
12126 | /* Used by expandstr to get here-doc like behaviour. */ | ||
12127 | #define FAKEEOFMARK ((char*)(uintptr_t)1) | ||
12128 | |||
12129 | static ALWAYS_INLINE int | ||
12130 | realeofmark(const char *eofmark) | ||
12131 | { | ||
12132 | return eofmark && eofmark != FAKEEOFMARK; | ||
12133 | } | ||
12134 | |||
12065 | /* | 12135 | /* |
12066 | * If eofmark is NULL, read a word or a redirection symbol. If eofmark | 12136 | * If eofmark is NULL, read a word or a redirection symbol. If eofmark |
12067 | * is not NULL, read a here document. In the latter case, eofmark is the | 12137 | * is not NULL, read a here document. In the latter case, eofmark is the |
@@ -12086,7 +12156,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12086 | /* c parameter is an unsigned char or PEOF or PEOA */ | 12156 | /* c parameter is an unsigned char or PEOF or PEOA */ |
12087 | char *out; | 12157 | char *out; |
12088 | size_t len; | 12158 | size_t len; |
12089 | char line[EOFMARKLEN + 1]; | ||
12090 | struct nodelist *bqlist; | 12159 | struct nodelist *bqlist; |
12091 | smallint quotef; | 12160 | smallint quotef; |
12092 | smallint dblquote; | 12161 | smallint dblquote; |
@@ -12104,9 +12173,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12104 | bqlist = NULL; | 12173 | bqlist = NULL; |
12105 | quotef = 0; | 12174 | quotef = 0; |
12106 | IF_FEATURE_SH_MATH(prevsyntax = 0;) | 12175 | IF_FEATURE_SH_MATH(prevsyntax = 0;) |
12176 | #if ENABLE_ASH_EXPAND_PRMT | ||
12107 | pssyntax = (syntax == PSSYNTAX); | 12177 | pssyntax = (syntax == PSSYNTAX); |
12108 | if (pssyntax) | 12178 | if (pssyntax) |
12109 | syntax = DQSYNTAX; | 12179 | syntax = DQSYNTAX; |
12180 | #else | ||
12181 | pssyntax = 0; /* constant */ | ||
12182 | #endif | ||
12110 | dblquote = (syntax == DQSYNTAX); | 12183 | dblquote = (syntax == DQSYNTAX); |
12111 | varnest = 0; | 12184 | varnest = 0; |
12112 | IF_FEATURE_SH_MATH(arinest = 0;) | 12185 | IF_FEATURE_SH_MATH(arinest = 0;) |
@@ -12160,7 +12233,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12160 | } else if (c == '\n') { | 12233 | } else if (c == '\n') { |
12161 | nlprompt(); | 12234 | nlprompt(); |
12162 | } else { | 12235 | } else { |
12163 | if (c == '$' && pssyntax) { | 12236 | if (pssyntax && c == '$') { |
12164 | USTPUTC(CTLESC, out); | 12237 | USTPUTC(CTLESC, out); |
12165 | USTPUTC('\\', out); | 12238 | USTPUTC('\\', out); |
12166 | } | 12239 | } |
@@ -12308,7 +12381,10 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12308 | * we are at the end of the here document, this routine sets the c to PEOF. | 12381 | * we are at the end of the here document, this routine sets the c to PEOF. |
12309 | */ | 12382 | */ |
12310 | checkend: { | 12383 | checkend: { |
12311 | if (eofmark) { | 12384 | if (realeofmark(eofmark)) { |
12385 | int markloc; | ||
12386 | char *p; | ||
12387 | |||
12312 | #if ENABLE_ASH_ALIAS | 12388 | #if ENABLE_ASH_ALIAS |
12313 | if (c == PEOA) | 12389 | if (c == PEOA) |
12314 | c = pgetc_without_PEOA(); | 12390 | c = pgetc_without_PEOA(); |
@@ -12318,27 +12394,42 @@ checkend: { | |||
12318 | c = pgetc_without_PEOA(); | 12394 | c = pgetc_without_PEOA(); |
12319 | } | 12395 | } |
12320 | } | 12396 | } |
12321 | if (c == *eofmark) { | ||
12322 | if (pfgets(line, sizeof(line)) != NULL) { | ||
12323 | char *p, *q; | ||
12324 | int cc; | ||
12325 | 12397 | ||
12326 | p = line; | 12398 | markloc = out - (char *)stackblock(); |
12327 | for (q = eofmark + 1;; p++, q++) { | 12399 | for (p = eofmark; STPUTC(c, out), *p; p++) { |
12328 | cc = *p; | 12400 | if (c != *p) |
12329 | if (cc == '\n') | 12401 | goto more_heredoc; |
12330 | cc = 0; | 12402 | |
12331 | if (!*q || cc != *q) | 12403 | c = pgetc_without_PEOA(); |
12332 | break; | 12404 | } |
12333 | } | 12405 | |
12334 | if (cc == *q) { | 12406 | if (c == '\n' || c == PEOF) { |
12335 | c = PEOF; | 12407 | c = PEOF; |
12336 | nlnoprompt(); | 12408 | g_parsefile->linno++; |
12337 | } else { | 12409 | needprompt = doprompt; |
12338 | pushstring(line, NULL); | 12410 | } else { |
12411 | int len_here; | ||
12412 | |||
12413 | more_heredoc: | ||
12414 | p = (char *)stackblock() + markloc + 1; | ||
12415 | len_here = out - p; | ||
12416 | |||
12417 | if (len_here) { | ||
12418 | len_here -= (c >= PEOF); | ||
12419 | c = p[-1]; | ||
12420 | |||
12421 | if (len_here) { | ||
12422 | char *str; | ||
12423 | |||
12424 | str = alloca(len_here + 1); | ||
12425 | *(char *)mempcpy(str, p, len_here) = '\0'; | ||
12426 | |||
12427 | pushstring(str, NULL); | ||
12339 | } | 12428 | } |
12340 | } | 12429 | } |
12341 | } | 12430 | } |
12431 | |||
12432 | STADJUST((char *)stackblock() + markloc - out, out); | ||
12342 | } | 12433 | } |
12343 | goto checkend_return; | 12434 | goto checkend_return; |
12344 | } | 12435 | } |
@@ -12432,7 +12523,8 @@ parsesub: { | |||
12432 | int typeloc; | 12523 | int typeloc; |
12433 | 12524 | ||
12434 | c = pgetc_eatbnl(); | 12525 | c = pgetc_eatbnl(); |
12435 | if (c > 255 /* PEOA or PEOF */ | 12526 | if ((checkkwd & CHKEOFMARK) |
12527 | || c > 255 /* PEOA or PEOF */ | ||
12436 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) | 12528 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
12437 | ) { | 12529 | ) { |
12438 | #if BASH_DOLLAR_SQUOTE | 12530 | #if BASH_DOLLAR_SQUOTE |
@@ -12994,22 +13086,18 @@ parseheredoc(void) | |||
12994 | } | 13086 | } |
12995 | 13087 | ||
12996 | 13088 | ||
12997 | /* | ||
12998 | * called by editline -- any expansions to the prompt should be added here. | ||
12999 | */ | ||
13000 | static const char * | 13089 | static const char * |
13001 | expandstr(const char *ps) | 13090 | expandstr(const char *ps, int syntax_type) |
13002 | { | 13091 | { |
13003 | union node n; | 13092 | union node n; |
13004 | int saveprompt; | 13093 | int saveprompt; |
13005 | 13094 | ||
13006 | /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value, | 13095 | /* XXX Fix (char *) cast. */ |
13007 | * and token processing _can_ alter it (delete NULs etc). */ | ||
13008 | setinputstring((char *)ps); | 13096 | setinputstring((char *)ps); |
13009 | 13097 | ||
13010 | saveprompt = doprompt; | 13098 | saveprompt = doprompt; |
13011 | doprompt = 0; | 13099 | doprompt = 0; |
13012 | readtoken1(pgetc(), PSSYNTAX, nullstr, 0); | 13100 | readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); |
13013 | doprompt = saveprompt; | 13101 | doprompt = saveprompt; |
13014 | 13102 | ||
13015 | popfile(); | 13103 | popfile(); |
@@ -14133,21 +14221,23 @@ init(void) | |||
14133 | /* | 14221 | /* |
14134 | * Process the shell command line arguments. | 14222 | * Process the shell command line arguments. |
14135 | */ | 14223 | */ |
14136 | static void | 14224 | static int |
14137 | procargs(char **argv) | 14225 | procargs(char **argv) |
14138 | { | 14226 | { |
14139 | int i; | 14227 | int i; |
14140 | const char *xminusc; | 14228 | const char *xminusc; |
14141 | char **xargv; | 14229 | char **xargv; |
14230 | int login_sh; | ||
14142 | 14231 | ||
14143 | xargv = argv; | 14232 | xargv = argv; |
14233 | login_sh = xargv[0] && xargv[0][0] == '-'; | ||
14144 | arg0 = xargv[0]; | 14234 | arg0 = xargv[0]; |
14145 | /* if (xargv[0]) - mmm, this is always true! */ | 14235 | /* if (xargv[0]) - mmm, this is always true! */ |
14146 | xargv++; | 14236 | xargv++; |
14147 | for (i = 0; i < NOPTS; i++) | 14237 | for (i = 0; i < NOPTS; i++) |
14148 | optlist[i] = 2; | 14238 | optlist[i] = 2; |
14149 | argptr = xargv; | 14239 | argptr = xargv; |
14150 | if (options(/*cmdline:*/ 1)) { | 14240 | if (options(/*cmdline:*/ 1, &login_sh)) { |
14151 | /* it already printed err message */ | 14241 | /* it already printed err message */ |
14152 | raise_exception(EXERROR); | 14242 | raise_exception(EXERROR); |
14153 | } | 14243 | } |
@@ -14191,6 +14281,8 @@ procargs(char **argv) | |||
14191 | xargv++; | 14281 | xargv++; |
14192 | } | 14282 | } |
14193 | optschanged(); | 14283 | optschanged(); |
14284 | |||
14285 | return login_sh; | ||
14194 | } | 14286 | } |
14195 | 14287 | ||
14196 | /* | 14288 | /* |
@@ -14199,7 +14291,7 @@ procargs(char **argv) | |||
14199 | static void | 14291 | static void |
14200 | read_profile(const char *name) | 14292 | read_profile(const char *name) |
14201 | { | 14293 | { |
14202 | name = expandstr(name); | 14294 | name = expandstr(name, DQSYNTAX); |
14203 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) | 14295 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) |
14204 | return; | 14296 | return; |
14205 | cmdloop(0); | 14297 | cmdloop(0); |
@@ -14227,8 +14319,7 @@ reset(void) | |||
14227 | popallfiles(); | 14319 | popallfiles(); |
14228 | 14320 | ||
14229 | /* from redir.c: */ | 14321 | /* from redir.c: */ |
14230 | while (redirlist) | 14322 | unwindredir(NULL); |
14231 | popredir(/*drop:*/ 0, /*restore:*/ 0); | ||
14232 | 14323 | ||
14233 | /* from var.c: */ | 14324 | /* from var.c: */ |
14234 | unwindlocalvars(NULL); | 14325 | unwindlocalvars(NULL); |
@@ -14252,6 +14343,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14252 | volatile smallint state; | 14343 | volatile smallint state; |
14253 | struct jmploc jmploc; | 14344 | struct jmploc jmploc; |
14254 | struct stackmark smark; | 14345 | struct stackmark smark; |
14346 | int login_sh; | ||
14255 | 14347 | ||
14256 | /* Initialize global data */ | 14348 | /* Initialize global data */ |
14257 | INIT_G_misc(); | 14349 | INIT_G_misc(); |
@@ -14304,6 +14396,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14304 | #if ENABLE_PLATFORM_MINGW32 | 14396 | #if ENABLE_PLATFORM_MINGW32 |
14305 | hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL); | 14397 | hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL); |
14306 | SetConsoleCtrlHandler(ctrl_handler, TRUE); | 14398 | SetConsoleCtrlHandler(ctrl_handler, TRUE); |
14399 | |||
14307 | if (argc == 3 && !strcmp(argv[1], "--forkshell")) { | 14400 | if (argc == 3 && !strcmp(argv[1], "--forkshell")) { |
14308 | forkshell_init(argv[2]); | 14401 | forkshell_init(argv[2]); |
14309 | 14402 | ||
@@ -14311,7 +14404,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14311 | bb_error_msg_and_die("subshell ended unexpectedly"); | 14404 | bb_error_msg_and_die("subshell ended unexpectedly"); |
14312 | } | 14405 | } |
14313 | #endif | 14406 | #endif |
14314 | procargs(argv); | 14407 | login_sh = procargs(argv); |
14315 | #if DEBUG | 14408 | #if DEBUG |
14316 | TRACE(("Shell args: ")); | 14409 | TRACE(("Shell args: ")); |
14317 | trace_puts_args(argv); | 14410 | trace_puts_args(argv); |
@@ -14327,9 +14420,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14327 | } | 14420 | } |
14328 | #endif | 14421 | #endif |
14329 | 14422 | ||
14330 | if (argv[0] && argv[0][0] == '-') | 14423 | if (login_sh) { |
14331 | isloginsh = 1; | ||
14332 | if (isloginsh) { | ||
14333 | const char *hp; | 14424 | const char *hp; |
14334 | 14425 | ||
14335 | #if ENABLE_PLATFORM_MINGW32 | 14426 | #if ENABLE_PLATFORM_MINGW32 |
@@ -14367,7 +14458,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14367 | * Testcase: ash -c 'exec 1>&0' must not complain. */ | 14458 | * Testcase: ash -c 'exec 1>&0' must not complain. */ |
14368 | // if (!sflag) g_parsefile->pf_fd = -1; | 14459 | // if (!sflag) g_parsefile->pf_fd = -1; |
14369 | // ^^ not necessary since now we special-case fd 0 | 14460 | // ^^ not necessary since now we special-case fd 0 |
14370 | // in is_hidden_fd() to not be considered "hidden fd" | 14461 | // in save_fd_on_redirect() |
14371 | evalstring(minusc, sflag ? 0 : EV_EXIT); | 14462 | evalstring(minusc, sflag ? 0 : EV_EXIT); |
14372 | } | 14463 | } |
14373 | 14464 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty.right b/shell/ash_test/ash-heredoc/heredoc_empty.right new file mode 100644 index 000000000..0eabe3671 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty.right | |||
@@ -0,0 +1,3 @@ | |||
1 | OK | ||
2 | OK | ||
3 | OK | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty.tests b/shell/ash_test/ash-heredoc/heredoc_empty.tests new file mode 100755 index 000000000..3629bc6d1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | unset a | ||
2 | cat <<- $a | ||
3 | OK | ||
4 | $a | ||
5 | cat <<- "" | ||
6 | OK | ||
7 | |||
8 | echo OK | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty2.right b/shell/ash_test/ash-heredoc/heredoc_empty2.right new file mode 100644 index 000000000..e32c6ea58 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | OK1 | ||
2 | Ok:0 | ||
3 | OK2 | ||
4 | Ok:0 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty2.tests b/shell/ash_test/ash-heredoc/heredoc_empty2.tests new file mode 100755 index 000000000..20fc35fe9 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty2.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | unset a | ||
2 | |||
3 | # Heredoc with empty delimiter | ||
4 | cat <<- "" | ||
5 | OK1 | ||
6 | |||
7 | echo Ok:$? | ||
8 | |||
9 | # Heredoc with empty delimiter | ||
10 | cat <<- "" | ||
11 | OK2 | ||
12 | |||
13 | |||
14 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-misc/func_prio_over_builtins.right b/shell/ash_test/ash-misc/func_prio_over_builtins.right new file mode 100644 index 000000000..54e56dff4 --- /dev/null +++ b/shell/ash_test/ash-misc/func_prio_over_builtins.right | |||
@@ -0,0 +1,5 @@ | |||
1 | YES | ||
2 | YES | ||
3 | YES | ||
4 | YES | ||
5 | Ok:YES | ||
diff --git a/shell/ash_test/ash-misc/func_prio_over_builtins.tests b/shell/ash_test/ash-misc/func_prio_over_builtins.tests new file mode 100755 index 000000000..4f71bfda0 --- /dev/null +++ b/shell/ash_test/ash-misc/func_prio_over_builtins.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | true() { echo YES >&2; } | ||
2 | true | ||
3 | true | true | ||
4 | (true) | ||
5 | echo Ok:`true 2>&1` | ||
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func_return1.right index 0ebd8e5a3..0ebd8e5a3 100644 --- a/shell/ash_test/ash-misc/func6.right +++ b/shell/ash_test/ash-misc/func_return1.right | |||
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func_return1.tests index 029c3e85e..029c3e85e 100755 --- a/shell/ash_test/ash-misc/func6.tests +++ b/shell/ash_test/ash-misc/func_return1.tests | |||
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/ash_test/ash-misc/func_return2.right index 0ebd8e5a3..0ebd8e5a3 100644 --- a/shell/hush_test/hush-misc/func6.right +++ b/shell/ash_test/ash-misc/func_return2.right | |||
diff --git a/shell/ash_test/ash-misc/func_return2.tests b/shell/ash_test/ash-misc/func_return2.tests new file mode 100755 index 000000000..a049dd180 --- /dev/null +++ b/shell/ash_test/ash-misc/func_return2.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | f1() { return 2; } | ||
2 | f1 | ||
3 | echo Two:$? | ||
4 | false | ||
5 | true | f1 | ||
6 | echo Two:$? | ||
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests index ccc497d7b..740daa461 100755 --- a/shell/ash_test/ash-redir/redir_script.tests +++ b/shell/ash_test/ash-redir/redir_script.tests | |||
@@ -20,10 +20,15 @@ eval "find_fds $fds" | |||
20 | 20 | ||
21 | # Shell should not lose that fd. Did it? | 21 | # Shell should not lose that fd. Did it? |
22 | find_fds | 22 | find_fds |
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | 23 | test x"$fds1" = x"$fds" \ |
24 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
25 | |||
26 | # One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11: | ||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | ||
28 | test x"$fds" = x" 11>&- 3>&-" \ | ||
29 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | 30 | ||
25 | echo "Bug: script fd is closed" | 31 | echo "Bug: script fd is closed" |
26 | echo "fds1:$fds1" | 32 | echo "fds1:$fds1" |
27 | echo "fds2:$fds" | 33 | echo "fds2:$fds" |
28 | exit 1 | 34 | exit 1 |
29 | |||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.right b/shell/ash_test/ash-redir/redir_to_bad_fd255.right new file mode 100644 index 000000000..9c5e35b36 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor | ||
2 | OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.tests b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests new file mode 100755 index 000000000..2266af6da --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&255 | ||
3 | echo OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.right b/shell/ash_test/ash-redir/redir_to_bad_fd3.right new file mode 100644 index 000000000..895a4a0a6 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor | ||
2 | OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.tests b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests new file mode 100755 index 000000000..98c54cfc6 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&3 | ||
3 | echo OK | ||
diff --git a/shell/hush.c b/shell/hush.c index d0225edb9..9f946d82f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1454,11 +1454,11 @@ static int fcntl_F_DUPFD(int fd, int avoid_fd) | |||
1454 | return newfd; | 1454 | return newfd; |
1455 | } | 1455 | } |
1456 | 1456 | ||
1457 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | 1457 | static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) |
1458 | { | 1458 | { |
1459 | int newfd; | 1459 | int newfd; |
1460 | repeat: | 1460 | repeat: |
1461 | newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1); | 1461 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); |
1462 | if (newfd < 0) { | 1462 | if (newfd < 0) { |
1463 | if (errno == EBUSY) | 1463 | if (errno == EBUSY) |
1464 | goto repeat; | 1464 | goto repeat; |
@@ -1469,6 +1469,8 @@ static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | |||
1469 | return fd; | 1469 | return fd; |
1470 | xfunc_die(); | 1470 | xfunc_die(); |
1471 | } | 1471 | } |
1472 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | ||
1473 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
1472 | close(fd); | 1474 | close(fd); |
1473 | return newfd; | 1475 | return newfd; |
1474 | } | 1476 | } |
@@ -1507,7 +1509,7 @@ static int save_FILEs_on_redirect(int fd, int avoid_fd) | |||
1507 | while (fl) { | 1509 | while (fl) { |
1508 | if (fd == fl->fd) { | 1510 | if (fd == fl->fd) { |
1509 | /* We use it only on script files, they are all CLOEXEC */ | 1511 | /* We use it only on script files, they are all CLOEXEC */ |
1510 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd); | 1512 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); |
1511 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | 1513 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); |
1512 | return 1; | 1514 | return 1; |
1513 | } | 1515 | } |
@@ -1544,6 +1546,16 @@ static void close_all_FILE_list(void) | |||
1544 | } | 1546 | } |
1545 | } | 1547 | } |
1546 | #endif | 1548 | #endif |
1549 | static int fd_in_FILEs(int fd) | ||
1550 | { | ||
1551 | struct FILE_list *fl = G.FILE_list; | ||
1552 | while (fl) { | ||
1553 | if (fl->fd == fd) | ||
1554 | return 1; | ||
1555 | fl = fl->next; | ||
1556 | } | ||
1557 | return 0; | ||
1558 | } | ||
1547 | 1559 | ||
1548 | 1560 | ||
1549 | /* Helpers for setting new $n and restoring them back | 1561 | /* Helpers for setting new $n and restoring them back |
@@ -4001,24 +4013,34 @@ static char *fetch_till_str(o_string *as_string, | |||
4001 | ch = i_getch(input); | 4013 | ch = i_getch(input); |
4002 | if (ch != EOF) | 4014 | if (ch != EOF) |
4003 | nommu_addchr(as_string, ch); | 4015 | nommu_addchr(as_string, ch); |
4004 | if ((ch == '\n' || ch == EOF) | 4016 | if (ch == '\n' || ch == EOF) { |
4005 | && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') | 4017 | check_heredoc_end: |
4006 | ) { | 4018 | if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') { |
4007 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | 4019 | if (strcmp(heredoc.data + past_EOL, word) == 0) { |
4008 | heredoc.data[past_EOL] = '\0'; | 4020 | heredoc.data[past_EOL] = '\0'; |
4009 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | 4021 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); |
4010 | return heredoc.data; | 4022 | return heredoc.data; |
4011 | } | 4023 | } |
4012 | while (ch == '\n') { | 4024 | if (ch == '\n') { |
4013 | o_addchr(&heredoc, ch); | 4025 | /* This is a new line. |
4014 | prev = ch; | 4026 | * Remember position and backslash-escaping status. |
4027 | */ | ||
4028 | o_addchr(&heredoc, ch); | ||
4029 | prev = ch; | ||
4015 | jump_in: | 4030 | jump_in: |
4016 | past_EOL = heredoc.length; | 4031 | past_EOL = heredoc.length; |
4017 | do { | 4032 | /* Get 1st char of next line, possibly skipping leading tabs */ |
4018 | ch = i_getch(input); | 4033 | do { |
4019 | if (ch != EOF) | 4034 | ch = i_getch(input); |
4020 | nommu_addchr(as_string, ch); | 4035 | if (ch != EOF) |
4021 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | 4036 | nommu_addchr(as_string, ch); |
4037 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | ||
4038 | /* If this immediately ended the line, | ||
4039 | * go back to end-of-line checks. | ||
4040 | */ | ||
4041 | if (ch == '\n') | ||
4042 | goto check_heredoc_end; | ||
4043 | } | ||
4022 | } | 4044 | } |
4023 | } | 4045 | } |
4024 | if (ch == EOF) { | 4046 | if (ch == EOF) { |
@@ -6674,9 +6696,9 @@ static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, in | |||
6674 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | 6696 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) |
6675 | { | 6697 | { |
6676 | int moved_to; | 6698 | int moved_to; |
6677 | int i = 0; | 6699 | int i; |
6678 | 6700 | ||
6679 | if (sq) while (sq[i].orig_fd >= 0) { | 6701 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { |
6680 | /* If we collide with an already moved fd... */ | 6702 | /* If we collide with an already moved fd... */ |
6681 | if (fd == sq[i].moved_to) { | 6703 | if (fd == sq[i].moved_to) { |
6682 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); | 6704 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); |
@@ -6690,7 +6712,6 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6690 | debug_printf_redir("redirect_fd %d: already moved\n", fd); | 6712 | debug_printf_redir("redirect_fd %d: already moved\n", fd); |
6691 | return sq; | 6713 | return sq; |
6692 | } | 6714 | } |
6693 | i++; | ||
6694 | } | 6715 | } |
6695 | 6716 | ||
6696 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | 6717 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ |
@@ -6701,18 +6722,41 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6701 | return append_squirrel(sq, i, fd, moved_to); | 6722 | return append_squirrel(sq, i, fd, moved_to); |
6702 | } | 6723 | } |
6703 | 6724 | ||
6725 | static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | ||
6726 | { | ||
6727 | int i; | ||
6728 | |||
6729 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | ||
6730 | /* If we collide with an already moved fd... */ | ||
6731 | if (fd == sq[i].orig_fd) { | ||
6732 | /* Examples: | ||
6733 | * "echo 3>FILE 3>&- 3>FILE" | ||
6734 | * "echo 3>&- 3>FILE" | ||
6735 | * No need for last redirect to insert | ||
6736 | * another "need to close 3" indicator. | ||
6737 | */ | ||
6738 | debug_printf_redir("redirect_fd %d: already moved or closed\n", fd); | ||
6739 | return sq; | ||
6740 | } | ||
6741 | } | ||
6742 | |||
6743 | debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd); | ||
6744 | return append_squirrel(sq, i, fd, -1); | ||
6745 | } | ||
6746 | |||
6704 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6747 | /* fd: redirect wants this fd to be used (e.g. 3>file). |
6705 | * Move all conflicting internally used fds, | 6748 | * Move all conflicting internally used fds, |
6706 | * and remember them so that we can restore them later. | 6749 | * and remember them so that we can restore them later. |
6707 | */ | 6750 | */ |
6708 | static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | 6751 | static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) |
6709 | { | 6752 | { |
6710 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | 6753 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ |
6711 | avoid_fd = 9; | 6754 | avoid_fd = 9; |
6712 | 6755 | ||
6713 | #if ENABLE_HUSH_INTERACTIVE | 6756 | #if ENABLE_HUSH_INTERACTIVE |
6714 | if (fd != 0 && fd == G.interactive_fd) { | 6757 | if (fd == G.interactive_fd) { |
6715 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd); | 6758 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ |
6759 | G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); | ||
6716 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); | 6760 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); |
6717 | return 1; /* "we closed fd" */ | 6761 | return 1; /* "we closed fd" */ |
6718 | } | 6762 | } |
@@ -6738,10 +6782,9 @@ static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
6738 | 6782 | ||
6739 | static void restore_redirects(struct squirrel *sq) | 6783 | static void restore_redirects(struct squirrel *sq) |
6740 | { | 6784 | { |
6741 | |||
6742 | if (sq) { | 6785 | if (sq) { |
6743 | int i = 0; | 6786 | int i; |
6744 | while (sq[i].orig_fd >= 0) { | 6787 | for (i = 0; sq[i].orig_fd >= 0; i++) { |
6745 | if (sq[i].moved_to >= 0) { | 6788 | if (sq[i].moved_to >= 0) { |
6746 | /* We simply die on error */ | 6789 | /* We simply die on error */ |
6747 | debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); | 6790 | debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); |
@@ -6751,7 +6794,6 @@ static void restore_redirects(struct squirrel *sq) | |||
6751 | debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); | 6794 | debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); |
6752 | close(sq[i].orig_fd); | 6795 | close(sq[i].orig_fd); |
6753 | } | 6796 | } |
6754 | i++; | ||
6755 | } | 6797 | } |
6756 | free(sq); | 6798 | free(sq); |
6757 | } | 6799 | } |
@@ -6761,17 +6803,47 @@ static void restore_redirects(struct squirrel *sq) | |||
6761 | restore_redirected_FILEs(); | 6803 | restore_redirected_FILEs(); |
6762 | } | 6804 | } |
6763 | 6805 | ||
6806 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | ||
6807 | static void close_saved_fds_and_FILE_fds(void) | ||
6808 | { | ||
6809 | if (G_interactive_fd) | ||
6810 | close(G_interactive_fd); | ||
6811 | close_all_FILE_list(); | ||
6812 | } | ||
6813 | #endif | ||
6814 | |||
6815 | static int internally_opened_fd(int fd, struct squirrel *sq) | ||
6816 | { | ||
6817 | int i; | ||
6818 | |||
6819 | #if ENABLE_HUSH_INTERACTIVE | ||
6820 | if (fd == G.interactive_fd) | ||
6821 | return 1; | ||
6822 | #endif | ||
6823 | /* If this one of script's fds? */ | ||
6824 | if (fd_in_FILEs(fd)) | ||
6825 | return 1; | ||
6826 | |||
6827 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | ||
6828 | if (fd == sq[i].moved_to) | ||
6829 | return 1; | ||
6830 | } | ||
6831 | return 0; | ||
6832 | } | ||
6833 | |||
6764 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 6834 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
6765 | * and stderr if they are redirected. */ | 6835 | * and stderr if they are redirected. */ |
6766 | static int setup_redirects(struct command *prog, struct squirrel **sqp) | 6836 | static int setup_redirects(struct command *prog, struct squirrel **sqp) |
6767 | { | 6837 | { |
6768 | int openfd, mode; | ||
6769 | struct redir_struct *redir; | 6838 | struct redir_struct *redir; |
6770 | 6839 | ||
6771 | for (redir = prog->redirects; redir; redir = redir->next) { | 6840 | for (redir = prog->redirects; redir; redir = redir->next) { |
6841 | int newfd; | ||
6842 | int closed; | ||
6843 | |||
6772 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6844 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6773 | /* "rd_fd<<HERE" case */ | 6845 | /* "rd_fd<<HERE" case */ |
6774 | save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 6846 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
6775 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6847 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6776 | * of the heredoc */ | 6848 | * of the heredoc */ |
6777 | debug_printf_parse("set heredoc '%s'\n", | 6849 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6783,6 +6855,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6783 | if (redir->rd_dup == REDIRFD_TO_FILE) { | 6855 | if (redir->rd_dup == REDIRFD_TO_FILE) { |
6784 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ | 6856 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ |
6785 | char *p; | 6857 | char *p; |
6858 | int mode; | ||
6859 | |||
6786 | if (redir->rd_filename == NULL) { | 6860 | if (redir->rd_filename == NULL) { |
6787 | /* | 6861 | /* |
6788 | * Examples: | 6862 | * Examples: |
@@ -6794,9 +6868,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6794 | } | 6868 | } |
6795 | mode = redir_table[redir->rd_type].mode; | 6869 | mode = redir_table[redir->rd_type].mode; |
6796 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); | 6870 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); |
6797 | openfd = open_or_warn(p, mode); | 6871 | newfd = open_or_warn(p, mode); |
6798 | free(p); | 6872 | free(p); |
6799 | if (openfd < 0) { | 6873 | if (newfd < 0) { |
6800 | /* Error message from open_or_warn can be lost | 6874 | /* Error message from open_or_warn can be lost |
6801 | * if stderr has been redirected, but bash | 6875 | * if stderr has been redirected, but bash |
6802 | * and ash both lose it as well | 6876 | * and ash both lose it as well |
@@ -6804,40 +6878,52 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6804 | */ | 6878 | */ |
6805 | return 1; | 6879 | return 1; |
6806 | } | 6880 | } |
6807 | if (openfd == redir->rd_fd && sqp) { | 6881 | if (newfd == redir->rd_fd && sqp) { |
6808 | /* open() gave us precisely the fd we wanted. | 6882 | /* open() gave us precisely the fd we wanted. |
6809 | * This means that this fd was not busy | 6883 | * This means that this fd was not busy |
6810 | * (not opened to anywhere). | 6884 | * (not opened to anywhere). |
6811 | * Remember to close it on restore: | 6885 | * Remember to close it on restore: |
6812 | */ | 6886 | */ |
6813 | struct squirrel *sq = *sqp; | 6887 | *sqp = add_squirrel_closed(*sqp, newfd); |
6814 | int i = 0; | 6888 | debug_printf_redir("redir to previously closed fd %d\n", newfd); |
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 | } | 6889 | } |
6820 | } else { | 6890 | } else { |
6821 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ | 6891 | /* "rd_fd>&rd_dup" or "rd_fd>&-" case */ |
6822 | openfd = redir->rd_dup; | 6892 | newfd = redir->rd_dup; |
6823 | } | 6893 | } |
6824 | 6894 | ||
6825 | if (openfd != redir->rd_fd) { | 6895 | if (newfd == redir->rd_fd) |
6826 | int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp); | 6896 | continue; |
6827 | if (openfd == REDIRFD_CLOSE) { | 6897 | |
6828 | /* "rd_fd >&-" means "close me" */ | 6898 | /* if "N>FILE": move newfd to redir->rd_fd */ |
6829 | if (!closed) { | 6899 | /* if "N>&M": dup newfd to redir->rd_fd */ |
6830 | /* ^^^ optimization: saving may already | 6900 | /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ |
6831 | * have closed it. If not... */ | 6901 | |
6832 | close(redir->rd_fd); | 6902 | closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); |
6833 | } | 6903 | if (newfd == REDIRFD_CLOSE) { |
6834 | } else { | 6904 | /* "N>&-" means "close me" */ |
6835 | xdup2(openfd, redir->rd_fd); | 6905 | if (!closed) { |
6836 | if (redir->rd_dup == REDIRFD_TO_FILE) | 6906 | /* ^^^ optimization: saving may already |
6837 | /* "rd_fd > FILE" */ | 6907 | * have closed it. If not... */ |
6838 | close(openfd); | 6908 | close(redir->rd_fd); |
6839 | /* else: "rd_fd > rd_dup" */ | 6909 | } |
6910 | /* Sometimes we do another close on restore, getting EBADF. | ||
6911 | * Consider "echo 3>FILE 3>&-" | ||
6912 | * first redirect remembers "need to close 3", | ||
6913 | * and second redirect closes 3! Restore code then closes 3 again. | ||
6914 | */ | ||
6915 | } else { | ||
6916 | /* if newfd is a script fd or saved fd, simulate EBADF */ | ||
6917 | if (internally_opened_fd(newfd, sqp ? *sqp : NULL)) { | ||
6918 | //errno = EBADF; | ||
6919 | //bb_perror_msg_and_die("can't duplicate file descriptor"); | ||
6920 | newfd = -1; /* same effect as code above */ | ||
6840 | } | 6921 | } |
6922 | xdup2(newfd, redir->rd_fd); | ||
6923 | if (redir->rd_dup == REDIRFD_TO_FILE) | ||
6924 | /* "rd_fd > FILE" */ | ||
6925 | close(newfd); | ||
6926 | /* else: "rd_fd > rd_dup" */ | ||
6841 | } | 6927 | } |
6842 | } | 6928 | } |
6843 | return 0; | 6929 | return 0; |
@@ -7004,11 +7090,34 @@ static void exec_function(char ***to_free, | |||
7004 | argv[0] = G.global_argv[0]; | 7090 | argv[0] = G.global_argv[0]; |
7005 | G.global_argv = argv; | 7091 | G.global_argv = argv; |
7006 | G.global_argc = n = 1 + string_array_len(argv + 1); | 7092 | G.global_argc = n = 1 + string_array_len(argv + 1); |
7093 | |||
7094 | // Example when we are here: "cmd | func" | ||
7095 | // func will run with saved-redirect fds open. | ||
7096 | // $ f() { echo /proc/self/fd/*; } | ||
7097 | // $ true | f | ||
7098 | // /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 | ||
7099 | // stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob | ||
7100 | // Same in script: | ||
7101 | // $ . ./SCRIPT | ||
7102 | // /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4 | ||
7103 | // stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob | ||
7104 | // They are CLOEXEC so external programs won't see them, but | ||
7105 | // for "more correctness" we might want to close those extra fds here: | ||
7106 | //? close_saved_fds_and_FILE_fds(); | ||
7107 | |||
7108 | /* "we are in function, ok to use return" */ | ||
7109 | G_flag_return_in_progress = -1; | ||
7110 | IF_HUSH_LOCAL(G.func_nest_level++;) | ||
7111 | |||
7007 | /* On MMU, funcp->body is always non-NULL */ | 7112 | /* On MMU, funcp->body is always non-NULL */ |
7008 | n = run_list(funcp->body); | 7113 | n = run_list(funcp->body); |
7009 | fflush_all(); | 7114 | fflush_all(); |
7010 | _exit(n); | 7115 | _exit(n); |
7011 | # else | 7116 | # else |
7117 | //? close_saved_fds_and_FILE_fds(); | ||
7118 | |||
7119 | //TODO: check whether "true | func_with_return" works | ||
7120 | |||
7012 | re_execute_shell(to_free, | 7121 | re_execute_shell(to_free, |
7013 | funcp->body_as_string, | 7122 | funcp->body_as_string, |
7014 | G.global_argv[0], | 7123 | G.global_argv[0], |
@@ -7028,9 +7137,7 @@ static int run_function(const struct function *funcp, char **argv) | |||
7028 | /* "we are in function, ok to use return" */ | 7137 | /* "we are in function, ok to use return" */ |
7029 | sv_flg = G_flag_return_in_progress; | 7138 | sv_flg = G_flag_return_in_progress; |
7030 | G_flag_return_in_progress = -1; | 7139 | G_flag_return_in_progress = -1; |
7031 | # if ENABLE_HUSH_LOCAL | 7140 | IF_HUSH_LOCAL(G.func_nest_level++;) |
7032 | G.func_nest_level++; | ||
7033 | # endif | ||
7034 | 7141 | ||
7035 | /* On MMU, funcp->body is always non-NULL */ | 7142 | /* On MMU, funcp->body is always non-NULL */ |
7036 | # if !BB_MMU | 7143 | # if !BB_MMU |
@@ -7094,6 +7201,7 @@ static void exec_builtin(char ***to_free, | |||
7094 | #if BB_MMU | 7201 | #if BB_MMU |
7095 | int rcode; | 7202 | int rcode; |
7096 | fflush_all(); | 7203 | fflush_all(); |
7204 | //? close_saved_fds_and_FILE_fds(); | ||
7097 | rcode = x->b_function(argv); | 7205 | rcode = x->b_function(argv); |
7098 | fflush_all(); | 7206 | fflush_all(); |
7099 | _exit(rcode); | 7207 | _exit(rcode); |
@@ -7215,6 +7323,16 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7215 | goto skip; | 7323 | goto skip; |
7216 | #endif | 7324 | #endif |
7217 | 7325 | ||
7326 | #if ENABLE_HUSH_FUNCTIONS | ||
7327 | /* Check if the command matches any functions (this goes before bltins) */ | ||
7328 | { | ||
7329 | const struct function *funcp = find_function(argv[0]); | ||
7330 | if (funcp) { | ||
7331 | exec_function(&nommu_save->argv_from_re_execing, funcp, argv); | ||
7332 | } | ||
7333 | } | ||
7334 | #endif | ||
7335 | |||
7218 | /* Check if the command matches any of the builtins. | 7336 | /* Check if the command matches any of the builtins. |
7219 | * Depending on context, this might be redundant. But it's | 7337 | * Depending on context, this might be redundant. But it's |
7220 | * easier to waste a few CPU cycles than it is to figure out | 7338 | * easier to waste a few CPU cycles than it is to figure out |
@@ -7231,15 +7349,6 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7231 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); | 7349 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); |
7232 | } | 7350 | } |
7233 | } | 7351 | } |
7234 | #if ENABLE_HUSH_FUNCTIONS | ||
7235 | /* Check if the command matches any functions */ | ||
7236 | { | ||
7237 | const struct function *funcp = find_function(argv[0]); | ||
7238 | if (funcp) { | ||
7239 | exec_function(&nommu_save->argv_from_re_execing, funcp, argv); | ||
7240 | } | ||
7241 | } | ||
7242 | #endif | ||
7243 | 7352 | ||
7244 | #if ENABLE_FEATURE_SH_STANDALONE | 7353 | #if ENABLE_FEATURE_SH_STANDALONE |
7245 | /* Check if the command matches any busybox applets */ | 7354 | /* Check if the command matches any busybox applets */ |
@@ -7248,8 +7357,12 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7248 | if (a >= 0) { | 7357 | if (a >= 0) { |
7249 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 7358 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
7250 | if (APPLET_IS_NOEXEC(a)) { | 7359 | if (APPLET_IS_NOEXEC(a)) { |
7251 | /* Do not leak open fds from opened script files etc */ | 7360 | /* Do not leak open fds from opened script files etc. |
7252 | close_all_FILE_list(); | 7361 | * Testcase: interactive "ls -l /proc/self/fd" |
7362 | * should not show tty fd open. | ||
7363 | */ | ||
7364 | close_saved_fds_and_FILE_fds(); | ||
7365 | //FIXME: should also close saved redir fds | ||
7253 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7366 | debug_printf_exec("running applet '%s'\n", argv[0]); |
7254 | run_applet_no_and_exit(a, argv[0], argv); | 7367 | run_applet_no_and_exit(a, argv[0], argv); |
7255 | } | 7368 | } |
@@ -7886,12 +7999,13 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7886 | return G.last_exitcode; | 7999 | return G.last_exitcode; |
7887 | } | 8000 | } |
7888 | 8001 | ||
7889 | x = find_builtin(argv_expanded[0]); | ||
7890 | #if ENABLE_HUSH_FUNCTIONS | 8002 | #if ENABLE_HUSH_FUNCTIONS |
7891 | funcp = NULL; | 8003 | /* Check if argv[0] matches any functions (this goes before bltins) */ |
7892 | if (!x) | 8004 | funcp = find_function(argv_expanded[0]); |
7893 | funcp = find_function(argv_expanded[0]); | ||
7894 | #endif | 8005 | #endif |
8006 | x = NULL; | ||
8007 | if (!funcp) | ||
8008 | x = find_builtin(argv_expanded[0]); | ||
7895 | if (x || funcp) { | 8009 | if (x || funcp) { |
7896 | if (!funcp) { | 8010 | if (!funcp) { |
7897 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { | 8011 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { |
@@ -9158,6 +9272,14 @@ static int FAST_FUNC builtin_exec(char **argv) | |||
9158 | if (G_saved_tty_pgrp && getpid() == G.root_pid) | 9272 | if (G_saved_tty_pgrp && getpid() == G.root_pid) |
9159 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 9273 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
9160 | 9274 | ||
9275 | /* Saved-redirect fds, script fds and G_interactive_fd are still | ||
9276 | * open here. However, they are all CLOEXEC, and execv below | ||
9277 | * closes them. Try interactive "exec ls -l /proc/self/fd", | ||
9278 | * it should show no extra open fds in the "ls" process. | ||
9279 | * If we'd try to run builtins/NOEXECs, this would need improving. | ||
9280 | */ | ||
9281 | //close_saved_fds_and_FILE_fds(); | ||
9282 | |||
9161 | /* TODO: if exec fails, bash does NOT exit! We do. | 9283 | /* TODO: if exec fails, bash does NOT exit! We do. |
9162 | * We'll need to undo trap cleanup (it's inside execvp_or_die) | 9284 | * We'll need to undo trap cleanup (it's inside execvp_or_die) |
9163 | * and tcsetpgrp, and this is inherently racy. | 9285 | * and tcsetpgrp, and this is inherently racy. |
diff --git a/shell/hush_test/hush-heredoc/heredoc_empty2.right b/shell/hush_test/hush-heredoc/heredoc_empty2.right new file mode 100644 index 000000000..e32c6ea58 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_empty2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | OK1 | ||
2 | Ok:0 | ||
3 | OK2 | ||
4 | Ok:0 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_empty2.tests b/shell/hush_test/hush-heredoc/heredoc_empty2.tests new file mode 100755 index 000000000..20fc35fe9 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_empty2.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | unset a | ||
2 | |||
3 | # Heredoc with empty delimiter | ||
4 | cat <<- "" | ||
5 | OK1 | ||
6 | |||
7 | echo Ok:$? | ||
8 | |||
9 | # Heredoc with empty delimiter | ||
10 | cat <<- "" | ||
11 | OK2 | ||
12 | |||
13 | |||
14 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-misc/func_prio_over_builtins.right b/shell/hush_test/hush-misc/func_prio_over_builtins.right new file mode 100644 index 000000000..54e56dff4 --- /dev/null +++ b/shell/hush_test/hush-misc/func_prio_over_builtins.right | |||
@@ -0,0 +1,5 @@ | |||
1 | YES | ||
2 | YES | ||
3 | YES | ||
4 | YES | ||
5 | Ok:YES | ||
diff --git a/shell/hush_test/hush-misc/func_prio_over_builtins.tests b/shell/hush_test/hush-misc/func_prio_over_builtins.tests new file mode 100755 index 000000000..4f71bfda0 --- /dev/null +++ b/shell/hush_test/hush-misc/func_prio_over_builtins.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | true() { echo YES >&2; } | ||
2 | true | ||
3 | true | true | ||
4 | (true) | ||
5 | echo Ok:`true 2>&1` | ||
diff --git a/shell/hush_test/hush-misc/func_return1.right b/shell/hush_test/hush-misc/func_return1.right new file mode 100644 index 000000000..0ebd8e5a3 --- /dev/null +++ b/shell/hush_test/hush-misc/func_return1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Two:2 | ||
2 | Two:2 | ||
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func_return1.tests index 029c3e85e..029c3e85e 100755 --- a/shell/hush_test/hush-misc/func6.tests +++ b/shell/hush_test/hush-misc/func_return1.tests | |||
diff --git a/shell/hush_test/hush-misc/func_return2.right b/shell/hush_test/hush-misc/func_return2.right new file mode 100644 index 000000000..0ebd8e5a3 --- /dev/null +++ b/shell/hush_test/hush-misc/func_return2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Two:2 | ||
2 | Two:2 | ||
diff --git a/shell/hush_test/hush-misc/func_return2.tests b/shell/hush_test/hush-misc/func_return2.tests new file mode 100755 index 000000000..a049dd180 --- /dev/null +++ b/shell/hush_test/hush-misc/func_return2.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | f1() { return 2; } | ||
2 | f1 | ||
3 | echo Two:$? | ||
4 | false | ||
5 | true | f1 | ||
6 | echo Two:$? | ||
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests index ccc497d7b..740daa461 100755 --- a/shell/hush_test/hush-redir/redir_script.tests +++ b/shell/hush_test/hush-redir/redir_script.tests | |||
@@ -20,10 +20,15 @@ eval "find_fds $fds" | |||
20 | 20 | ||
21 | # Shell should not lose that fd. Did it? | 21 | # Shell should not lose that fd. Did it? |
22 | find_fds | 22 | find_fds |
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | 23 | test x"$fds1" = x"$fds" \ |
24 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
25 | |||
26 | # One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11: | ||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | ||
28 | test x"$fds" = x" 11>&- 3>&-" \ | ||
29 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | 30 | ||
25 | echo "Bug: script fd is closed" | 31 | echo "Bug: script fd is closed" |
26 | echo "fds1:$fds1" | 32 | echo "fds1:$fds1" |
27 | echo "fds2:$fds" | 33 | echo "fds2:$fds" |
28 | exit 1 | 34 | exit 1 |
29 | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.right b/shell/hush_test/hush-redir/redir_to_bad_fd255.right new file mode 100644 index 000000000..936911ce5 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.right | |||
@@ -0,0 +1 @@ | |||
hush: can't duplicate file descriptor: Bad file descriptor | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.tests b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests new file mode 100755 index 000000000..2266af6da --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&255 | ||
3 | echo OK | ||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.right b/shell/hush_test/hush-redir/redir_to_bad_fd3.right new file mode 100644 index 000000000..936911ce5 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.right | |||
@@ -0,0 +1 @@ | |||
hush: can't duplicate file descriptor: Bad file descriptor | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.tests b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests new file mode 100755 index 000000000..98c54cfc6 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&3 | ||
3 | echo OK | ||