BSD 4_4 release
[unix-history] / usr / src / bin / csh / sem.c
index 55d3914..584d3fb 100644 (file)
-/*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved.  The Berkeley Software License Agreement
- * specifies the terms and conditions for redistribution.
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char *sccsid = "@(#)sem.c       5.8 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)sem.c      8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
 
 
-#include "sh.h"
-#include "sh.proc.h"
-#include <sys/file.h>
+#include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/ioctl.h>
-#include "pathnames.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
 
 
-/*
- * C shell
- */
+#include "csh.h"
+#include "proc.h"
+#include "extern.h"
+
+static void     vffree __P((int));
+static Char    *splicepipe __P((struct command *t, Char *));
+static void     doio __P((struct command *t, int *, int *));
+static void     chkclob __P((char *));
 
 
-static int nosigchld = 0, osigmask;
-static int onosigchld = 0, oosigmask;
-/*VARARGS 1*/
+void
 execute(t, wanttty, pipein, pipeout)
 execute(t, wanttty, pipein, pipeout)
-       register struct command *t;
-       int wanttty, *pipein, *pipeout;
+    register struct command *t;
+    int     wanttty, *pipein, *pipeout;
 {
 {
-       bool forked = 0;
-       struct biltins *bifunc;
-       int pid = 0;
-       int pv[2];
+    bool    forked = 0;
+    struct biltins *bifunc;
+    int     pid = 0;
+    int     pv[2];
 
 
-       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] & (QUOTE|TRIM)) == QUOTE)
-                       (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);
-                       heredoc(t->t_dlef);
-                       if (noexec)
-                               (void) close(0);
-               }
-               if (noexec)
-                       break;
+    static sigset_t csigmask;
 
 
-               set("status", "0");
+    static sigset_t ocsigmask;
+    static int onosigchld = 0;
+    static int nosigchld = 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], "nice"))
-                               if (t->t_dcom[1])
-                                       if (any(t->t_dcom[1][0], "+-"))
-                                               if (t->t_dcom[2]) {
-                                                       setname("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], "nohup"))
-                               if (t->t_dcom[1]) {
-                                       t->t_dflg |= FNOHUP;
-                                       lshift(t->t_dcom, 1);
-                               } else
-                                       break;
-                       else if (eq(t->t_dcom[0], "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;
+    UNREGISTER(forked);
+    UNREGISTER(bifunc);
+    UNREGISTER(wanttty);
 
 
-               /*
-                * 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)
-#endif
-                       { forked++; 
-                         if (wanttty >= 0 && !nosigchld) {
-                               osigmask = sigblock(sigmask(SIGCHLD));
-                               nosigchld = 1;
-                         }
-
-                         pid = pfork(t, wanttty);
-                         if (pid == 0 && nosigchld) {
-                               sigsetmask(osigmask);
-                               nosigchld = 0;
-                         }
+    if (t == 0)
+       return;
+
+    if (t->t_dflg & F_AMPERSAND)
+       wanttty = 0;
+    switch (t->t_dtyp) {
+
+    case NODE_COMMAND:
+       if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE)
+           (void) Strcpy(t->t_dcom[0], t->t_dcom[0] + 1);
+       if ((t->t_dflg & F_REPEAT) == 0)
+           Dfix(t);            /* $ " ' \ */
+       if (t->t_dcom[0] == 0)
+           return;
+       /* fall into... */
+
+    case NODE_PAREN:
+       if (t->t_dflg & F_PIPEOUT)
+           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 & F_READ) {
+           (void) close(0);
+           heredoc(t->t_dlef);
+           if (noexec)
+               (void) close(0);
+       }
+
+       set(STRstatus, Strsave(STR0));
+
+       /*
+        * 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 == NODE_COMMAND)
+           if (eq(t->t_dcom[0], STRnice))
+               if (t->t_dcom[1])
+                   if (strchr("+-", t->t_dcom[1][0]))
+                       if (t->t_dcom[2]) {
+                           setname("nice");
+                           t->t_nice =
+                               getn(t->t_dcom[1]);
+                           lshift(t->t_dcom, 2);
+                           t->t_dflg |= F_NICE;
                        }
                        }
-#ifdef VFORK
+                       else
+                           break;
                    else {
                    else {
-                       sig_t vffree;
-                       int ochild, osetintr, ohaderr, odidfds;
-                       int oSHIN, oSHOUT, oSHDIAG, oOLDSTD, otpgrp;
-                       long omask;
-
-                       /* 
-                        * 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.
-                        */
-                       if (wanttty >= 0 && !nosigchld && !noexec) {
-                               osigmask = sigblock(sigmask(SIGCHLD));
-                               nosigchld = 1;
-                       }
-                       omask = sigblock(sigmask(SIGCHLD));
-                       ochild = child; osetintr = setintr;
-                       ohaderr = haderr; odidfds = didfds;
-                       oSHIN = SHIN; oSHOUT = SHOUT;
-                       oSHDIAG = SHDIAG; oOLDSTD = OLDSTD; otpgrp = tpgrp;
-                       oosigmask = osigmask; onosigchld = nosigchld;
-                       Vsav = Vdp = 0; Vav = 0;
-                       pid = vfork();
-                       if (pid < 0) {
-                               (void) sigsetmask(omask);
-                               error("No more processes");
-                       }
-                       forked++;
-                       if (pid) {      /* parent */
-                               child = ochild; setintr = osetintr;
-                               haderr = ohaderr; didfds = odidfds;
-                               SHIN = oSHIN;
-                               SHOUT = oSHOUT; SHDIAG = oSHDIAG;
-                               OLDSTD = oOLDSTD; tpgrp = otpgrp;
-                               osigmask = oosigmask; nosigchld = onosigchld;
-                               xfree(Vsav); Vsav = 0;
-                               xfree(Vdp); Vdp = 0;
-                               xfree((char *)Vav); Vav = 0;
-                               /* this is from pfork() */
-                               palloc(pid, t);
-                               (void) sigsetmask(omask);
-                       } else {        /* child */
-                               /* this is from pfork() */
-                               int pgrp;
-                               bool ignint = 0;
-
-                               if (nosigchld) {
-                                       sigsetmask(osigmask);
-                                       nosigchld = 0;
-                               }
-                               if (setintr)
-                                       ignint =
-                                           (tpgrp == -1 && (t->t_dflg&FINT))
-                                           || gointr && eq(gointr, "-");
-                               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) setpgrp(0, pgrp);
-                               if (wanttty > 0)
-                                       (void) ioctl(FSHTTY, TIOCSPGRP,
-                                               (char *)&pgrp);
-                               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);
-                       }
-
+                       t->t_nice = 4;
+                       lshift(t->t_dcom, 1);
+                       t->t_dflg |= F_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]);
-                               (void) close(pipein[1]);
-                       }
-                       if ((t->t_dflg & FPOU) == 0) {
-                               if (nosigchld) {
-#ifdef foobarbaz
-                                       printf("DID\n");
-#endif
-                                       sigsetmask(osigmask);
-                                       nosigchld = 0;
-                               }
-                               if ((t->t_dflg & FAND) == 0)
-                                       pwait();
-                       }
-                       break;
+               else
+                   break;
+           else if (eq(t->t_dcom[0], STRnohup))
+               if (t->t_dcom[1]) {
+                   t->t_dflg |= F_NOHUP;
+                   lshift(t->t_dcom, 1);
                }
                }
-               doio(t, pipein, pipeout);
-               if (t->t_dflg & FPOU) {
-                       (void) close(pipeout[0]);
-                       (void) close(pipeout[1]);
+               else
+                   break;
+           else if (eq(t->t_dcom[0], STRtime))
+               if (t->t_dcom[1]) {
+                   t->t_dflg |= F_TIME;
+                   lshift(t->t_dcom, 1);
                }
                }
+               else
+                   break;
+           else
+               break;
 
 
+       /* is it a command */
+       if (t->t_dtyp == NODE_COMMAND) {
+           /*
+            * Check if we have a builtin function and remember which one.
+            */
+           bifunc = isbfunc(t);
+           if (noexec) {
                /*
                /*
-                * Perform a builtin function.
-                * If we are not forked, arrange for possible stopping
+                * Continue for builtins that are part of the scripting language
                 */
                 */
-               if (bifunc) {
-                       func(t, bifunc);
-                       if (forked)
-                               exitstat();
-                       break;
+               if (bifunc->bfunct != dobreak   && bifunc->bfunct != docontin &&
+                   bifunc->bfunct != doelse    && bifunc->bfunct != doend    &&
+                   bifunc->bfunct != doforeach && bifunc->bfunct != dogoto   &&
+                   bifunc->bfunct != doif      && bifunc->bfunct != dorepeat &&
+                   bifunc->bfunct != doswbrk   && bifunc->bfunct != doswitch &&
+                   bifunc->bfunct != dowhile   && bifunc->bfunct != dozip)
+                   break;
+           }
+       }
+       else {                  /* not a command */
+           bifunc = NULL;
+           if (noexec)
+               break;
+       }
+
+       /*
+        * 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.
+        */
+       /*
+        * Prevent forking cd, pushd, popd, chdir cause this will cause the
+        * shell not to change dir!
+        */
+       if (bifunc && (bifunc->bfunct == dochngd ||
+                      bifunc->bfunct == dopushd ||
+                      bifunc->bfunct == dopopd))
+           t->t_dflg &= ~(F_NICE);
+       if (((t->t_dflg & F_TIME) || ((t->t_dflg & F_NOFORK) == 0 &&
+            (!bifunc || t->t_dflg &
+             (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP)))) ||
+       /*
+        * We have to fork for eval too.
+        */
+           (bifunc && (t->t_dflg & (F_PIPEIN | F_PIPEOUT)) != 0 &&
+            bifunc->bfunct == doeval))
+           if (t->t_dtyp == NODE_PAREN ||
+               t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) {
+               forked++;
+               /*
+                * We need to block SIGCHLD here, so that if the process does
+                * not die before we can set the process group
+                */
+               if (wanttty >= 0 && !nosigchld) {
+                   csigmask = sigblock(sigmask(SIGCHLD));
+                   nosigchld = 1;
                }
                }
-               if (t->t_dtyp != TPAR) {
-                       doexec(t);
-                       /*NOTREACHED*/
+
+               pid = pfork(t, wanttty);
+               if (pid == 0 && nosigchld) {
+                   (void) sigsetmask(csigmask);
+                   nosigchld = 0;
                }
                }
+               else if (pid != 0 && (t->t_dflg & F_AMPERSAND))
+                   backpid = pid;
+
+           }
+           else {
+               int     ochild, osetintr, ohaderr, odidfds;
+               int     oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp;
+               sigset_t omask;
+
                /*
                /*
-                * For () commands must put new 0,1,2 in FSH* and recurse
+                * 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.
                 */
                 */
-               OLDSTD = dcopy(0, FOLDSTD);
-               SHOUT = dcopy(1, FSHOUT);
-               SHDIAG = dcopy(2, FSHDIAG);
-               (void) close(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;
+               if (wanttty >= 0 && !nosigchld && !noexec) {
+                   csigmask = sigblock(sigmask(SIGCHLD));
+                   nosigchld = 1;
+               }
+               omask = sigblock(sigmask(SIGCHLD) | sigmask(SIGINT));
+               ochild = child;
+               osetintr = setintr;
+               ohaderr = haderr;
+               odidfds = didfds;
+               oSHIN = SHIN;
+               oSHOUT = SHOUT;
+               oSHERR = SHERR;
+               oOLDSTD = OLDSTD;
+               otpgrp = tpgrp;
+               ocsigmask = csigmask;
+               onosigchld = nosigchld;
+               Vsav = Vdp = 0;
+               Vexpath = 0;
+               Vt = 0;
+               pid = vfork();
 
 
-       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 (pid < 0) {
+                   (void) sigsetmask(omask);
+                   stderror(ERR_NOPROC);
                }
                }
-               if (t->t_dcdr) {
-                       t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
-                       execute(t->t_dcdr, wanttty);
+               forked++;
+               if (pid) {      /* parent */
+                   child = ochild;
+                   setintr = osetintr;
+                   haderr = ohaderr;
+                   didfds = odidfds;
+                   SHIN = oSHIN;
+                   SHOUT = oSHOUT;
+                   SHERR = oSHERR;
+                   OLDSTD = oOLDSTD;
+                   tpgrp = otpgrp;
+                   csigmask = ocsigmask;
+                   nosigchld = onosigchld;
+
+                   xfree((ptr_t) Vsav);
+                   Vsav = 0;
+                   xfree((ptr_t) Vdp);
+                   Vdp = 0;
+                   xfree((ptr_t) Vexpath);
+                   Vexpath = 0;
+                   blkfree((Char **) Vt);
+                   Vt = 0;
+                   /* this is from pfork() */
+                   palloc(pid, t);
+                   (void) sigsetmask(omask);
                }
                }
-               break;
+               else {          /* child */
+                   /* this is from pfork() */
+                   int     pgrp;
+                   bool    ignint = 0;
+
+                   if (nosigchld) {
+                       (void) sigsetmask(csigmask);
+                       nosigchld = 0;
+                   }
+
+                   if (setintr)
+                       ignint =
+                           (tpgrp == -1 &&
+                            (t->t_dflg & F_NOINTERRUPT))
+                           || (gointr && eq(gointr, STRminus));
+                   pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
+                   child++;
+                   if (setintr) {
+                       setintr = 0;
+                       if (ignint) {
+                           (void) signal(SIGINT, SIG_IGN);
+                           (void) signal(SIGQUIT, SIG_IGN);
+                       }
+                       else {
+                           (void) signal(SIGINT, vffree);
+                           (void) signal(SIGQUIT, SIG_DFL);
+                       }
 
 
-       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("status")) == 0) != (t->t_dtyp == TAND))
-                               return;
+                       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 & F_NOINTERRUPT)) {
+                       (void) signal(SIGINT, SIG_IGN);
+                       (void) signal(SIGQUIT, SIG_IGN);
+                   }
+
+                   pgetty(wanttty, pgrp);
+                   if (t->t_dflg & F_NOHUP)
+                       (void) signal(SIGHUP, SIG_IGN);
+                   if (t->t_dflg & F_NICE)
+                       (void) setpriority(PRIO_PROCESS, 0, t->t_nice);
                }
                }
-               if (t->t_dcdr) {
-                       t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
-                       execute(t->t_dcdr, wanttty);
+
+           }
+       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 & F_PIPEIN) {
+               (void) close(pipein[0]);
+               (void) close(pipein[1]);
+           }
+           if ((t->t_dflg & F_PIPEOUT) == 0) {
+               if (nosigchld) {
+                   (void) sigsetmask(csigmask);
+                   nosigchld = 0;
                }
                }
-               break;
+               if ((t->t_dflg & F_AMPERSAND) == 0)
+                   pwait();
+           }
+           break;
+       }
+       doio(t, pipein, pipeout);
+       if (t->t_dflg & F_PIPEOUT) {
+           (void) close(pipeout[0]);
+           (void) close(pipeout[1]);
        }
        /*
        }
        /*
-        * 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
+        * Perform a builtin function. If we are not forked, arrange for
+        * possible stopping
         */
         */
-       if (didfds && !(t->t_dflg & FREDO))
-               donefds();
+       if (bifunc) {
+           func(t, bifunc);
+           if (forked)
+               exitstat();
+           break;
+       }
+       if (t->t_dtyp != NODE_PAREN) {
+           doexec(NULL, t);
+           /* NOTREACHED */
+       }
+       /*
+        * For () commands must put new 0,1,2 in FSH* and recurse
+        */
+       OLDSTD = dcopy(0, FOLDSTD);
+       SHOUT = dcopy(1, FSHOUT);
+       SHERR = dcopy(2, FSHERR);
+       (void) close(SHIN);
+       SHIN = -1;
+       didfds = 0;
+       wanttty = -1;
+       t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+       execute(t->t_dspr, wanttty, NULL, NULL);
+       exitstat();
+
+    case NODE_PIPE:
+       t->t_dcar->t_dflg |= F_PIPEOUT |
+           (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT));
+       execute(t->t_dcar, wanttty, pipein, pv);
+       t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg &
+                       (F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT));
+       if (wanttty > 0)
+           wanttty = 0;        /* got tty already */
+       execute(t->t_dcdr, wanttty, pv, pipeout);
+       break;
+
+    case NODE_LIST:
+       if (t->t_dcar) {
+           t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+           execute(t->t_dcar, wanttty, NULL, NULL);
+           /*
+            * In strange case of A&B make a new job after A
+            */
+           if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr &&
+               (t->t_dcdr->t_dflg & F_AMPERSAND) == 0)
+               pendjob();
+       }
+       if (t->t_dcdr) {
+           t->t_dcdr->t_dflg |= t->t_dflg &
+               (F_NOFORK | F_NOINTERRUPT);
+           execute(t->t_dcdr, wanttty, NULL, NULL);
+       }
+       break;
+
+    case NODE_OR:
+    case NODE_AND:
+       if (t->t_dcar) {
+           t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+           execute(t->t_dcar, wanttty, NULL, NULL);
+           if ((getn(value(STRstatus)) == 0) !=
+               (t->t_dtyp == NODE_AND))
+               return;
+       }
+       if (t->t_dcdr) {
+           t->t_dcdr->t_dflg |= t->t_dflg &
+               (F_NOFORK | F_NOINTERRUPT);
+           execute(t->t_dcdr, wanttty, NULL, NULL);
+       }
+       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 F_REPEAT bit are responsible for
+     * doing donefds after the last re-execution
+     */
+    if (didfds && !(t->t_dflg & F_REPEAT))
+       donefds();
 }
 
 }
 
-#ifdef VFORK
-vffree()
+static void
+vffree(i)
+int i;
 {
 {
-       register char **v;
+    register Char **v;
 
 
-       if (v = gargv)
-               gargv = 0, xfree((char *)v);
-       if (v = pargv)
-               pargv = 0, xfree((char *)v);
-       _exit(1);
+    if ((v = gargv) != NULL) {
+       gargv = 0;
+       xfree((ptr_t) v);
+    }
+    if ((v = pargv) != NULL) {
+       pargv = 0;
+       xfree((ptr_t) v);
+    }
+    _exit(i);
+}
+
+/*
+ * Expand and glob the words after an i/o redirection.
+ * If more than one word is generated, then update the command vector.
+ *
+ * This is done differently in all the shells:
+ * 1. in the bourne shell and ksh globbing is not performed
+ * 2. Bash/csh say ambiguous
+ * 3. zsh does i/o to/from all the files
+ * 4. itcsh concatenates the words.
+ *
+ * I don't know what is best to do. I think that Ambiguous is better
+ * than restructuring the command vector, because the user can get
+ * unexpected results. In any case, the command vector restructuring 
+ * code is present and the user can choose it by setting noambiguous
+ */
+static Char *
+splicepipe(t, cp)
+    register struct command *t;
+    Char *cp;  /* word after < or > */
+{
+    Char *blk[2];
+
+    if (adrof(STRnoambiguous)) {
+       Char **pv;
+
+       blk[0] = Dfix1(cp); /* expand $ */
+       blk[1] = NULL;
+
+       gflag = 0, tglob(blk);
+       if (gflag) {
+           pv = globall(blk);
+           if (pv == NULL) {
+               setname(vis_str(blk[0]));
+               xfree((ptr_t) blk[0]);
+               stderror(ERR_NAME | ERR_NOMATCH);
+           }
+           gargv = NULL;
+           if (pv[1] != NULL) { /* we need to fix the command vector */
+               Char **av = blkspl(t->t_dcom, &pv[1]);
+               xfree((ptr_t) t->t_dcom);
+               t->t_dcom = av;
+           }
+           xfree((ptr_t) blk[0]);
+           blk[0] = pv[0];
+           xfree((ptr_t) pv);
+       }
+    }
+    else {
+       blk[0] = globone(blk[1] = Dfix1(cp), G_ERROR);
+       xfree((ptr_t) blk[1]);
+    }
+    return(blk[0]);
 }
 }
-#endif
 
 /*
  * Perform io redirection.
  * We may or maynot be forked here.
  */
 
 /*
  * Perform io redirection.
  * We may or maynot be forked here.
  */
+static void
 doio(t, pipein, pipeout)
 doio(t, pipein, pipeout)
-       register struct command *t;
-       int *pipein, *pipeout;
+    register struct command *t;
+    int    *pipein, *pipeout;
 {
 {
-       register char *cp;
-       register int flags = t->t_dflg;
+    register int fd;
+    register Char *cp;
+    register int flags = t->t_dflg;
 
 
-       if (didfds || (flags & FREDO))
-               return;
-       if ((flags & FHERE) == 0) {     /* FHERE already done */
-               (void) close(0);
-               if (cp = t->t_dlef) {
-                       cp = globone(Dfix1(cp));
-                       xfree(cp);
-                       if (open(cp, 0) < 0)
-                               Perror(cp);
-               } else if (flags & FPIN) {
-                       (void) dup(pipein[0]);
-                       (void) close(pipein[0]);
-                       (void) close(pipein[1]);
-               } else if ((flags & FINT) && tpgrp == -1) {
-                       (void) close(0);
-                       (void) open(_PATH_DEVNULL, 0);
-               } else
-                       (void) dup(OLDSTD);
+    if (didfds || (flags & F_REPEAT))
+       return;
+    if ((flags & F_READ) == 0) {/* F_READ already done */
+       if (t->t_dlef) {
+           char    tmp[MAXPATHLEN+1];
+
+           /*
+            * so < /dev/std{in,out,err} work
+            */
+           (void) dcopy(SHIN, 0);
+           (void) dcopy(SHOUT, 1);
+           (void) dcopy(SHERR, 2);
+           cp = splicepipe(t, t->t_dlef);
+           (void) strncpy(tmp, short2str(cp), MAXPATHLEN);
+           tmp[MAXPATHLEN] = '\0';
+           xfree((ptr_t) cp);
+           if ((fd = open(tmp, O_RDONLY)) < 0)
+               stderror(ERR_SYSTEM, tmp, strerror(errno));
+           (void) dmove(fd, 0);
+       }
+       else if (flags & F_PIPEIN) {
+           (void) close(0);
+           (void) dup(pipein[0]);
+           (void) close(pipein[0]);
+           (void) close(pipein[1]);
        }
        }
+       else if ((flags & F_NOINTERRUPT) && tpgrp == -1) {
+           (void) close(0);
+           (void) open(_PATH_DEVNULL, O_RDONLY);
+       }
+       else {
+           (void) close(0);
+           (void) dup(OLDSTD);
+           (void) ioctl(0, FIONCLEX, NULL);
+       }
+    }
+    if (t->t_drit) {
+       char    tmp[MAXPATHLEN+1];
+
+       cp = splicepipe(t, t->t_drit);
+       (void) strncpy(tmp, short2str(cp), MAXPATHLEN);
+       tmp[MAXPATHLEN] = '\0';
+       xfree((ptr_t) cp);
+       /*
+        * so > /dev/std{out,err} work
+        */
+       (void) dcopy(SHOUT, 1);
+       (void) dcopy(SHERR, 2);
+       if ((flags & F_APPEND) &&
+#ifdef O_APPEND
+           (fd = open(tmp, O_WRONLY | O_APPEND)) >= 0);
+#else
+           (fd = open(tmp, O_WRONLY)) >= 0)
+           (void) lseek(1, (off_t) 0, L_XTND);
+#endif
+       else {
+           if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) {
+               if (flags & F_APPEND)
+                   stderror(ERR_SYSTEM, tmp, strerror(errno));
+               chkclob(tmp);
+           }
+           if ((fd = creat(tmp, 0666)) < 0)
+               stderror(ERR_SYSTEM, tmp, strerror(errno));
+       }
+       (void) dmove(fd, 1);
+    }
+    else if (flags & F_PIPEOUT) {
        (void) close(1);
        (void) close(1);
-       if (cp = t->t_drit) {
-               cp = globone(Dfix1(cp));
-               xfree(cp);
-               if (!(flags & FCAT) || open(cp, O_WRONLY|O_APPEND, 0) < 0) {
-                       if (!(flags & FANY) && adrof("noclobber")) {
-                               if (flags & FCAT)
-                                       Perror(cp);
-                               chkclob(cp);
-                       }
-                       if (creat(cp, 0666) < 0)
-                               Perror(cp);
-               }
-       } else if (flags & FPOU)
-               (void) dup(pipeout[1]);
-       else
-               (void) dup(SHOUT);
-
-       (void) close(2);
-       if (flags & FDIAG)
-               (void) dup(1);
-       else
-               (void) dup(SHDIAG);
-       didfds = 1;
+       (void) dup(pipeout[1]);
+    }
+    else {
+       (void) close(1);
+       (void) dup(SHOUT);
+       (void) ioctl(1, FIONCLEX, NULL);
+    }
+
+    (void) close(2);
+    if (flags & F_STDERR) {
+       (void) dup(1);
+    }
+    else {
+       (void) dup(SHERR);
+       (void) ioctl(2, FIONCLEX, NULL);
+    }
+    didfds = 1;
 }
 
 }
 
+void
 mypipe(pv)
 mypipe(pv)
-       register int *pv;
+    register int *pv;
 {
 
 {
 
-       if (pipe(pv) < 0)
-               goto oops;
-       pv[0] = dmove(pv[0], -1);
-       pv[1] = dmove(pv[1], -1);
-       if (pv[0] >= 0 && pv[1] >= 0)
-               return;
+    if (pipe(pv) < 0)
+       goto oops;
+    pv[0] = dmove(pv[0], -1);
+    pv[1] = dmove(pv[1], -1);
+    if (pv[0] >= 0 && pv[1] >= 0)
+       return;
 oops:
 oops:
-       error("Can't make pipe");
+    stderror(ERR_PIPE);
 }
 
 }
 
+static void
 chkclob(cp)
 chkclob(cp)
-       register char *cp;
+    register char *cp;
 {
 {
-       struct stat stb;
+    struct stat stb;
 
 
-       if (stat(cp, &stb) < 0)
-               return;
-       if ((stb.st_mode & S_IFMT) == S_IFCHR)
-               return;
-       error("%s: File exists", cp);
+    if (stat(cp, &stb) < 0)
+       return;
+    if (S_ISCHR(stb.st_mode))
+       return;
+    stderror(ERR_EXISTS, cp);
 }
 }