root/usr/src/cmd/csh/sh.sem.c
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*        All Rights Reserved   */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved. The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#include <unistd.h>
#include <fcntl.h>
#include "sh.h"
#include "sh.proc.h"
#include "sh.tconst.h"

/*
 * C shell
 */

void    doio(struct command *, int *, int *);
void    mypipe(int *);
void    chkclob(tchar *);

/*
 * Return true if there is a back-quote (`) anywhere in the argument list.
 * Its presence would cause glob() to be invoked in the child process
 * and this would cause chaos if the child is created with vfork().
 */
static bool
AnyBquote(struct command *t)
{
        tchar **pp;
        tchar *p;

        if (noexec)
                return (0);
        for (pp = t->t_dcom; p = *pp++;) {
                if (any('`', p))
                        return (1);
        }
        return (0);
}

/*VARARGS 1*/
void
execute(t, wanttty, pipein, pipeout)
        struct command *t;
        int wanttty, *pipein, *pipeout;
{
        bool forked = 0;
        struct biltins *bifunc;
        int pid = 0;
        int pv[2];
        extern int globcnt;
#ifdef TRACE
        tprintf("TRACE- execute()\n");
#endif

        if (t == 0)
                return;
        if ((t->t_dflg & FAND) && wanttty > 0)
                wanttty = 0;
        switch (t->t_dtyp) {

        case TCOM:
                if (t->t_dcom[0][0] == (tchar)S_TOPBIT[0])
                        (void) strcpy_(t->t_dcom[0], t->t_dcom[0] + 1);
                if ((t->t_dflg & FREDO) == 0)
                        Dfix(t);                /* $ " ' \ */
                if (t->t_dcom[0] == 0)
                        return;
                /* fall into... */

        case TPAR:
                if (t->t_dflg & FPOU)
                        mypipe(pipeout);
                /*
                 * Must do << early so parent will know
                 * where input pointer should be.
                 * If noexec then this is all we do.
                 */
                if (t->t_dflg & FHERE) {
                        (void) close(0);
                        unsetfd(0);
                        heredoc(t->t_dlef);
                        if (noexec) {
                                (void) close(0);
                                unsetfd(0);
                        }
                }
                if (noexec)
                        break;

                set(S_status, S_0);

                /*
                 * This mess is the necessary kludge to handle the prefix
                 * builtins: nice, nohup, time.  These commands can also
                 * be used by themselves, and this is not handled here.
                 * This will also work when loops are parsed.
                 */
                while (t->t_dtyp == TCOM)
                        if (eq(t->t_dcom[0], S_nice /*"nice"*/))
                                if (t->t_dcom[1])
                                        /*if (any(t->t_dcom[1][0], "+-"))*/
                                        if (t->t_dcom[1][0] == '+' ||
                                            t->t_dcom[1][0] == '-')
                                                if (t->t_dcom[2]) {
                                                        setname(S_nice /*"nice"*/);
                                                        t->t_nice = getn(t->t_dcom[1]);
                                                        lshift(t->t_dcom, 2);
                                                        t->t_dflg |= FNICE;
                                                } else
                                                        break;
                                        else {
                                                t->t_nice = 4;
                                                lshift(t->t_dcom, 1);
                                                t->t_dflg |= FNICE;
                                        }
                                else
                                        break;
                        else if (eq(t->t_dcom[0], S_nohup /*"nohup"*/))
                                if (t->t_dcom[1]) {
                                        t->t_dflg |= FNOHUP;
                                        lshift(t->t_dcom, 1);
                                } else
                                        break;
                        else if (eq(t->t_dcom[0], S_time /*"time"*/))
                                if (t->t_dcom[1]) {
                                        t->t_dflg |= FTIME;
                                        lshift(t->t_dcom, 1);
                                } else
                                        break;
                        else
                                break;
                /*
                 * Check if we have a builtin function and remember which one.
                 */
                bifunc = t->t_dtyp == TCOM ? isbfunc(t) : (struct biltins *) 0;

                /*
                 * We fork only if we are timed, or are not the end of
                 * a parenthesized list and not a simple builtin function.
                 * Simple meaning one that is not pipedout, niced, nohupped,
                 * or &'d.
                 * It would be nice(?) to not fork in some of these cases.
                 */
                if (((t->t_dflg & FTIME) || (t->t_dflg & FPAR) == 0 &&
                     (!bifunc || t->t_dflg & (FPOU|FAND|FNICE|FNOHUP))))
#ifdef VFORK
                    if (t->t_dtyp == TPAR || t->t_dflg&(FREDO|FAND) ||
                        bifunc || AnyBquote(t))
#endif
                        { forked++; pid = pfork(t, wanttty); }
#ifdef VFORK
                    else {
                        void vffree();
                        struct sv {
                                int mask, child, setintr, haderr, didfds;
                                int SHIN, SHOUT, SHDIAG, OLDSTD, tpgrp;
                                struct sigvec sigv;
                        } sv;

                        /*
                         * Prepare for the vfork by saving everything
                         * that the child corrupts before it exec's.
                         * Note that in some signal implementations
                         * which keep the signal info in user space
                         * (e.g. Sun's) it will also be necessary to
                         * save and restore the current sigvec's for
                         * the signals the child touches before it
                         * exec's.
                         */
                        sv.mask = sigblock(sigmask(SIGCHLD));
                        sv.child = child; sv.setintr = setintr;
                        sv.haderr = haderr; sv.didfds = didfds;
                        sv.SHIN = SHIN; sv.SHOUT = SHOUT;
                        sv.SHDIAG = SHDIAG; sv.OLDSTD = OLDSTD;
                        sv.tpgrp = tpgrp;
                        Vsav = Vdp = 0; Vav = 0;
                        (void) sigvec(SIGINT, (struct sigvec *)0, &sv.sigv);
                        pid = vfork();
                        if (pid < 0) {
                                (void) sigsetmask(sv.mask);
                                error("Vfork failed");
                        }
                        forked++;
                        if (pid) {      /* parent */
                                int ppid;
                                closelog();
                                child = sv.child; setintr = sv.setintr;
                                haderr = sv.haderr; didfds = sv.didfds;
                                SHIN = sv.SHIN;
                                SHOUT = sv.SHOUT; SHDIAG = sv.SHDIAG;
                                OLDSTD = sv.OLDSTD; tpgrp = sv.tpgrp;
                                xfree(Vsav); Vsav = 0;
                                xfree(Vdp); Vdp = 0;
                                xfree( (tchar *)Vav); Vav = 0;
                                /* this is from pfork() */
                                ppid = pcurrjob ? pcurrjob->p_jobid : pid;
                                if (wanttty >= 0 && tpgrp >= 0)
                                        setpgid (ppid, ppid);
                                palloc(pid, t);
                                /*
                                 * Restore SIGINT handler.
                                 */
                                (void) sigvec(SIGINT, &sv.sigv, (struct sigvec *)0);
                                (void) sigsetmask(sv.mask);
                        } else {        /* child */
                                /* this is from pfork() */
                                int pgrp;
                                bool ignint = 0;
                                int sigttou;
                                if (setintr)
                                        ignint =
                                            (tpgrp == -1 && (t->t_dflg&FINT))
                                            || gointr
                                                && eq(gointr, S_MINUS/*"-"*/);
                                pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
                                child++;
                                if (setintr) {
                                        setintr = 0;
#ifdef notdef
                                        (void) signal(SIGCHLD, SIG_DFL);
#endif
                                        (void) signal(SIGINT, ignint ?
                                                SIG_IGN : vffree);
                                        (void) signal(SIGQUIT, ignint ?
                                                SIG_IGN : SIG_DFL);
                                        if (wanttty >= 0) {
                                                (void) signal(SIGTSTP, SIG_DFL);
                                                (void) signal(SIGTTIN, SIG_DFL);
                                                (void) signal(SIGTTOU, SIG_DFL);
                                        }
                                        (void) signal(SIGTERM, parterm);
                                } else if (tpgrp == -1 && (t->t_dflg&FINT)) {
                                        (void) signal(SIGINT, SIG_IGN);
                                        (void) signal(SIGQUIT, SIG_IGN);
                                }
                                if (wanttty >= 0 && tpgrp >= 0)
                                        (void) setpgid(0, pgrp);
                                if (wanttty > 0) {
                                        sigttou = sigblock (
                                                sigmask(SIGTTOU) |
                                                sigmask(SIGTTIN) |
                                                sigmask(SIGTSTP));
                                        (void) ioctl(FSHTTY, TIOCSPGRP,
                                                 (tchar *)&pgrp);
                                        sigsetmask (sigttou);
                                }
                                if (tpgrp > 0)
                                        tpgrp = 0;
                                if (t->t_dflg & FNOHUP)
                                        (void) signal(SIGHUP, SIG_IGN);
                                if (t->t_dflg & FNICE)
                                        (void) setpriority(PRIO_PROCESS,
                                                0, t->t_nice);
                        }

                    }
#endif
                if (pid != 0) {
                        /*
                         * It would be better if we could wait for the
                         * whole job when we knew the last process
                         * had been started.  Pwait, in fact, does
                         * wait for the whole job anyway, but this test
                         * doesn't really express our intentions.
                         */
                        if (didfds==0 && t->t_dflg&FPIN) {
                                (void) close(pipein[0]);
                                unsetfd(pipein[0]);
                                (void) close(pipein[1]);
                                unsetfd(pipein[1]);
                        }
                        if ((t->t_dflg & (FPOU|FAND)) == 0)
                                pwait();
                        break;
                }
                doio(t, pipein, pipeout);
                if (t->t_dflg & FPOU) {
                        (void) close(pipeout[0]);
                        (void) unsetfd(pipeout[0]);
                        (void) close(pipeout[1]);
                        (void) unsetfd(pipeout[1]);
                }

                /*
                 * Perform a builtin function.
                 * If we are not forked, arrange for possible stopping
                 */
                if (bifunc) {
                        func(t, bifunc);
                        if (forked)
                                exitstat();
                        break;
                }
                if (t->t_dtyp != TPAR) {
                        doexec(t);
                        /*NOTREACHED*/
                }
                /*
                 * For () commands must put new 0,1,2 in FSH* and recurse
                 */
                OLDSTD = dcopy(0, FOLDSTD);
                SHOUT = dcopy(1, FSHOUT);
                SHDIAG = dcopy(2, FSHDIAG);
                (void) close(SHIN);
                (void) unsetfd(SHIN);
                SHIN = -1;
                didfds = 0;
                wanttty = -1;
                t->t_dspr->t_dflg |= t->t_dflg & FINT;
                execute(t->t_dspr, wanttty);
                exitstat();

        case TFIL:
                t->t_dcar->t_dflg |= FPOU |
                    (t->t_dflg & (FPIN|FAND|FDIAG|FINT));
                execute(t->t_dcar, wanttty, pipein, pv);
                t->t_dcdr->t_dflg |= FPIN |
                    (t->t_dflg & (FPOU|FAND|FPAR|FINT));
                if (wanttty > 0)
                        wanttty = 0;            /* got tty already */
                execute(t->t_dcdr, wanttty, pv, pipeout);
                break;

        case TLST:
                if (t->t_dcar) {
                        t->t_dcar->t_dflg |= t->t_dflg & FINT;
                        execute(t->t_dcar, wanttty);
                        /*
                         * In strange case of A&B make a new job after A
                         */
                        if (t->t_dcar->t_dflg&FAND && t->t_dcdr &&
                            (t->t_dcdr->t_dflg&FAND) == 0)
                                pendjob();
                }
                if (t->t_dcdr) {
                        t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
                        execute(t->t_dcdr, wanttty);
                }
                break;

        case TOR:
        case TAND:
                if (t->t_dcar) {
                        t->t_dcar->t_dflg |= t->t_dflg & FINT;
                        execute(t->t_dcar, wanttty);
                        if ((getn(value(S_status/*"status"*/)) == 0) != (t->t_dtyp == TAND))
                                return;
                }
                if (t->t_dcdr) {
                        t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
                        execute(t->t_dcdr, wanttty);
                }
                break;
        }
        /*
         * Fall through for all breaks from switch
         *
         * If there will be no more executions of this
         * command, flush all file descriptors.
         * Places that turn on the FREDO bit are responsible
         * for doing donefds after the last re-execution
         */
        if (didfds && !(t->t_dflg & FREDO))
                donefds();

        /*
         * If glob() was called and arguments list is not yet
         * free'ed, free them here.
         */
        if (gargv) {
                blkfree(gargv);
                gargv = 0;
                globcnt = 0;
        }
}

#ifdef VFORK
void
vffree(void)
{
        tchar **v;

#ifdef TRACE
        tprintf("TRACE- vffree()\n");
#endif
        if (v = gargv)
                gargv = 0, xfree( (tchar *)v);
        if (v = pargv)
                pargv = 0, xfree( (tchar *)v);
        _exit(1);
}
#endif

/*
 * Perform io redirection.
 * We may or maynot be forked here.
 */
void
doio(struct command *t, int *pipein, int *pipeout)
{
        tchar *cp, *dp;
        int flags = t->t_dflg;
        int fd;

#ifdef TRACE
        tprintf("TRACE- doio()\n");
#endif
        if (didfds || (flags & FREDO))
                return;
        if ((flags & FHERE) == 0) {     /* FHERE already done */
                (void) close(0);
                (void) unsetfd(0);
                if (cp = t->t_dlef) {
                        dp = Dfix1(cp);
                        cp = globone(dp);
                        xfree(dp);
                        xfree(cp);
                        if (open_(cp, 0) < 0)
                                Perror(cp);
                } else if (flags & FPIN) {
                        fd = dup(pipein[0]);
                        if (fd != -1)
                                setfd(fd);
                        (void) close(pipein[0]);
                        (void) unsetfd(pipein[0]);
                        (void) close(pipein[1]);
                        (void) unsetfd(pipein[1]);
                } else if ((flags & FINT) && tpgrp == -1) {
                        (void) close(0);        /* no need for unsetfd */
                        (void) open("/dev/null", 0); /* no need for setfd */
                } else {
                        fd = dup(OLDSTD);
                        if (fd != -1)
                                setfd(fd);
                }
        }
        (void) close(1);
        (void) unsetfd(1);
        if (cp = t->t_drit) {
                dp = Dfix1(cp);
                cp = globone(dp);
                xfree(dp);
                if ((flags & FCAT) && open_(cp, 1) >= 0)
                        (void) lseek(1, (off_t)0, 2);
                else {
                        if (!(flags & FANY) && adrof(S_noclobber/*"noclobber"*/)) {
                                if (flags & FCAT)
                                        Perror(cp);
                                chkclob(cp);
                        }
                        if (creat_(cp, 0666) < 0)
                                Perror(cp);
                }
                xfree(cp);
        } else if (flags & FPOU) {
                fd = dup(pipeout[1]);
                if (fd != -1)
                        setfd (fd);
        }
        else {
                fd = dup(SHOUT);
                if (fd != -1)
                        setfd(fd);
        }

        (void) close(2);
        (void) unsetfd(2);
        if (flags & FDIAG) {
                fd = dup(1);
                if (fd != -1)
                        setfd(fd);
        }
        else {
                fd = dup(SHDIAG);
                if (fd != -1)
                        setfd(fd);
        }
        didfds = 1;
}

void
mypipe(int *pv)
{

#ifdef TRACE
        tprintf("TRACE- mypipe()\n");
#endif
        if (pipe(pv) < 0)
                goto oops;
        setfd(pv[0]);
        setfd(pv[1]);

        pv[0] = dmove(pv[0], -1);
        pv[1] = dmove(pv[1], -1);
        if (pv[0] >= 0 && pv[1] >= 0)
                return;
oops:
        error("Can't make pipe");
}

void
chkclob(tchar *cp)
{
        struct stat stb;
        unsigned short  type;

#ifdef TRACE
        tprintf("TRACE- chkclob()\n");
#endif
        if (stat_(cp, &stb) < 0)
                return;
        type = stb.st_mode & S_IFMT;
        if (type == S_IFCHR || type == S_IFIFO)
                return;
        error("%t: File exists", cp);
}