aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c695
1 files changed, 393 insertions, 302 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;
2117struct globals_var { 2115struct 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... */
2599static const char *expandstr(const char *ps); 2597static 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 */
2601static void 2611static void
2602setprompt_if(smallint do_set, int whichprompt) 2612setprompt_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
3059static int 3062static 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}
5726static int
5727fcntl_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}
5740static int
5741xdup_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 */
5718struct two_fd_t { 5762struct squirrel {
5719 int orig, copy; 5763 int orig_fd;
5764 int moved_to;
5720}; 5765};
5721struct redirtab { 5766struct 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)
5727enum {
5728 COPYFD_RESTORE = (int)~(INT_MAX),
5729};
5730 5772
5731static int 5773static void
5732need_to_remember(struct redirtab *rp, int fd) 5774add_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 */
5749static int 5799static int
5750is_hidden_fd(struct redirtab *rp, int fd) 5800save_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
5880static int
5881internally_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 */
5794static void 5912static void
5795redirect(union node *redir, int flags) 5913redirect(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
6000static int
6001redirectsafe(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
6022static struct redirtab*
6023pushredir(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 */
5933static void 6053static void
5934popredir(int drop, int restore) 6054popredir(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/* 6084static void
5966 * Undo all redirections. Called on error or interrupt. 6085unwindredir(struct redirtab *stop)
5967 */
5968
5969static int
5970redirectsafe(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 */
10799static char *
10800pfgets(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}
11129static int 11210static int
11130options(int cmdline) 11211options(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 */
11459static union node *andor(void); 11541static union node *andor(void);
11460static union node *pipeline(void); 11542static 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 */
11641static int
11642noexpand(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
11657static void 11719static void
11658parsefname(void) 11720parsefname(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
12129static ALWAYS_INLINE int
12130realeofmark(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 */
12310checkend: { 12383checkend: {
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 */
13000static const char * 13089static const char *
13001expandstr(const char *ps) 13090expandstr(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 */
14136static void 14224static int
14137procargs(char **argv) 14225procargs(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)
14199static void 14291static void
14200read_profile(const char *name) 14292read_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