home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 4 / FreshFish_May-June1994.bin / bbs / gnu / pdksh-src.lha / src / amiga / pdksh / sh / exec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-01  |  15.1 KB  |  776 lines

  1. /*
  2.  * execute command tree
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: exec.c,v 1.3 1992/04/25 08:29:52 sjg Exp $";
  7. #endif
  8.  
  9. #include "stdh.h"
  10. #include <errno.h>
  11. #include <signal.h>
  12. #include <setjmp.h>
  13. #include <unistd.h>
  14. #include <fcntl.h>
  15. #include <sys/stat.h>
  16. #include "sh.h"
  17.  
  18. static int      comexec     ARGS((struct op *t, char **vp, char **ap, int flags));
  19. #ifdef    SHARPBANG
  20. static void     scriptexec  ARGS((struct op *tp, char **ap));
  21. #endif
  22. static void     iosetup     ARGS((struct ioword *iop));
  23. static int      herein      ARGS((char *hname, int sub));
  24. static void     echo        ARGS((char **vp, char **ap));
  25.  
  26.  
  27. /*
  28.  * handle systems that don't have F_SETFD
  29.  */
  30. #ifndef F_SETFD
  31. # ifndef MAXFD
  32. #   define  MAXFD 64
  33. # endif
  34. /*
  35.  * a bit field would be smaller, but this
  36.  * will work
  37.  */
  38. static char clexec_tab[MAXFD+1];
  39.  
  40. /* this is so that main() can clear it */
  41. void
  42. init_clexec()
  43. {
  44.   (void) memset(clexec_tab, 0, sizeof(clexec_tab)-1);
  45. }
  46.  
  47. int
  48. fd_clexec(fd)
  49.   int fd;
  50. {
  51.   if (fd < sizeof(clexec_tab))
  52.   {
  53.     clexec_tab[fd] = 1;
  54.     return 0;
  55.   }
  56.   return -1;
  57. }
  58. #endif
  59.  
  60.  
  61. /*
  62.  * execute command tree
  63.  */
  64. int
  65. execute(t, flags)
  66.     register struct op *t;
  67.     volatile int flags;    /* if XEXEC don't fork */
  68. {
  69.     int i;
  70.     int volatile rv = 0;
  71.     int pv[2];
  72.     register char **ap;
  73.     char *s, *cp;
  74.     struct ioword **iowp;
  75.  
  76.     if (t == NULL)
  77.         return 0;
  78.  
  79.     if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
  80.         return exchild(t, flags); /* run in sub-process */
  81.  
  82.     newenv(E_EXEC);
  83.     if (trap)
  84.         runtraps();
  85.  
  86.     if (t->ioact != NULL || t->type == TPIPE) {
  87.         e.savefd = (short*) alloc(sizeofN(short, NUFILE), ATEMP);
  88.         for (i = 0; i < NUFILE; i++)
  89.             e.savefd[i] = 0; /* not redirected */
  90.         /* mark fd 0/1 in-use if pipeline */
  91.         if (flags&XPIPEI)
  92.             e.savefd[0] = -1;
  93.         if (flags&XPIPEO)
  94.             e.savefd[1] = -1;
  95.     }
  96.  
  97.     /* do redirection, to be restored in quitenv() */
  98.     if (t->ioact != NULL)
  99.         for (iowp = t->ioact; *iowp != NULL; iowp++)
  100.             iosetup(*iowp);
  101.  
  102.     switch(t->type) {
  103.       case TCOM:
  104.         e.type = E_TCOM;
  105.         rv = comexec(t, eval(t->vars, DOTILDE),
  106.                  eval(t->args, DOBLANK|DOGLOB|DOTILDE), flags);
  107.         break;
  108.  
  109.       case TPAREN:
  110.         exstat = rv = execute(t->left, flags|XFORK);
  111.         break;
  112.  
  113.       case TPIPE:
  114.         flags |= XFORK;
  115.         flags &= ~XEXEC;
  116.         e.savefd[0] = savefd(0);
  117.         e.savefd[1] = savefd(1);
  118.         flags |= XPIPEO;
  119.         (void) dup2(e.savefd[0], 0); /* stdin of first */
  120.         while (t->type == TPIPE) {
  121.             openpipe(pv);
  122.             (void) dup2(pv[1], 1);    /* stdout of curr */
  123.             exchild(t->left, flags);
  124.             (void) dup2(pv[0], 0);    /* stdin of next */
  125.             closepipe(pv);
  126.             flags |= XPIPEI;
  127.             t = t->right;
  128.         }
  129.         flags &= ~ XPIPEO;
  130.         (void) dup2(e.savefd[1], 1); /* stdout of last */
  131.         exchild(t, flags);
  132.         (void) dup2(e.savefd[0], 0); /* close pipe in */
  133.         if (!(flags&XBGND))
  134.             exstat = rv = waitlast();
  135.         break;
  136.  
  137.       case TLIST:
  138.         while (t->type == TLIST) {
  139.             execute(t->left, 0);
  140.             t = t->right;
  141.         }
  142.         rv = execute(t, 0);
  143.         break;
  144.  
  145.       case TASYNC:
  146.         rv = execute(t->left, flags|XBGND|XFORK);
  147.         break;
  148.  
  149.       case TOR:
  150.       case TAND:
  151.         rv = execute(t->left, 0);
  152.         if (t->right != NULL && (rv == 0) == (t->type == TAND))
  153.             rv = execute(t->right, 0);
  154.         break;
  155.  
  156.       case TFOR:
  157.         e.type = E_LOOP;
  158.         ap = (t->vars != NULL) ?
  159.             eval(t->vars, DOBLANK|DOGLOB|DOTILDE) : e.loc->argv + 1;
  160.         while ((i = setjmp(e.jbuf)))
  161.             if (i == LBREAK)
  162.                 goto Break1;
  163.         while (*ap != NULL) {
  164.             setstr(global(t->str), *ap++);
  165.             rv = execute(t->left, 0);
  166.         }
  167.       Break1:
  168.         break;
  169.  
  170.       case TWHILE:
  171.       case TUNTIL:
  172.         e.type = E_LOOP;
  173.         while ((i = setjmp(e.jbuf)))
  174.             if (i == LBREAK)
  175.                 goto Break2;
  176.         while ((execute(t->left, 0) == 0) == (t->type == TWHILE))
  177.             rv = execute(t->right, 0);
  178.       Break2:
  179.         break;
  180.  
  181.       case TIF:
  182.       case TELIF:
  183.         if (t->right == NULL)
  184.             break;    /* should be error */
  185.         rv = execute(t->left, 0) == 0 ?
  186.             execute(t->right->left, 0) :
  187.             execute(t->right->right, 0);
  188.         break;
  189.  
  190.       case TCASE:
  191.         cp = evalstr(t->str, 0);
  192.         for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
  193.             for (ap = t->vars; *ap; ap++)
  194.             if ((s = evalstr(*ap, DOPAT)) && gmatch(cp, s))
  195.                 goto Found;
  196.         break;
  197.       Found:
  198.         rv = execute(t->left, 0);
  199.         break;
  200.  
  201.       case TBRACE:
  202.         rv = execute(t->left, 0);
  203.         break;
  204.  
  205.       case TFUNCT:
  206.         rv = define(t->str, t->left);
  207.         break;
  208.  
  209.       case TTIME:
  210.         rv = timex(t, flags);
  211.         break;
  212.  
  213.       case TEXEC:        /* an eval'd TCOM */
  214.         s = t->args[0];
  215.         ap = makenv();
  216. #ifndef F_SETFD
  217.         for (i = 0; i < sizeof(clexec_tab); i++)
  218.           if (clexec_tab[i])
  219.           {
  220.             close(i);
  221.             clexec_tab[i] = 0;
  222.           }
  223. #endif
  224.         execve(t->str, t->args, ap);
  225.         if (errno == ENOEXEC) {
  226.             char *shell;
  227. #ifdef    SHARPBANG
  228.             scriptexec(t, ap);
  229. #else
  230.             shell = strval(global("EXECSHELL"));
  231.             if (shell && *shell) {
  232.                 if ((shell = search(shell,path,1)) == NULL)
  233.                     shell = SHELL;
  234.             } else {
  235.                 shell = SHELL;
  236.             }
  237.             *t->args-- = t->str;
  238.             *t->args = shell;
  239.             execve(t->args[0], t->args, ap);
  240.             errorf("No shell\n");
  241. #endif    /* SHARPBANG */
  242.         }
  243.         errorf("%s: %s\n", s, strerror(errno));
  244.     }
  245.  
  246.     quitenv();        /* restores IO */
  247.     if (e.interactive) {    /* flush stdout, shlout */
  248.         fflush(shf[1]);
  249.         fflush(shf[2]);
  250.     }
  251.     if ((flags&XEXEC))
  252.         exit(rv);    /* exit child */
  253.     return rv;
  254. }
  255.  
  256. /*
  257.  * execute simple command
  258.  */
  259.  
  260. static int
  261. comexec(t, vp, ap, flags)
  262.     struct op *t;
  263.     register char **ap, **vp;
  264.     int flags;
  265. {
  266.     int i;
  267.     int rv = 0;
  268.     register char *cp;
  269.     register char **lastp;
  270.     register struct tbl *tp = NULL;
  271.     register struct block *l;
  272.     static struct op texec = {TEXEC};
  273.     extern int c_exec(), c_builtin();
  274.  
  275.     if (flag[FXTRACE])
  276.         echo(vp, ap);
  277.  
  278.     /* snag the last argument for $_ */
  279.     if ((lastp = ap) && *lastp) {
  280.         while (*++lastp)
  281.             ;
  282.         setstr(typeset("_",LOCAL,0),*--lastp);
  283.     }    
  284.  
  285.     /* create new variable/function block */
  286.     l = (struct block*) alloc(sizeof(struct block), ATEMP);
  287.     l->next = e.loc; e.loc = l;
  288.     newblock();
  289.  
  290.  Doexec:
  291.     if ((cp = *ap) == NULL)
  292.         cp = ":";
  293.     tp = findcom(cp, flag[FHASHALL]);
  294.  
  295.     switch (tp->type) {
  296.       case CSHELL:            /* shell built-in */
  297.         while (tp->val.f == c_builtin) {
  298.             if ((cp = *++ap) == NULL)
  299.                 break;
  300.             tp = tsearch(&builtins, cp, hash(cp));
  301.             if (tp == NULL)
  302.                 errorf("%s: not builtin\n", cp);
  303.         }
  304.         if (tp->val.f == c_exec) {
  305.             if (*++ap == NULL) {
  306.                 e.savefd = NULL; /* don't restore redirection */
  307.                 break;
  308.             }
  309.             flags |= XEXEC;
  310.             goto Doexec;
  311.         }
  312.         if ((tp->flag&TRACE))
  313.             e.loc = l->next; /* no local block */
  314.         i = (tp->flag&TRACE) ? 0 : LOCAL;
  315.         while (*vp != NULL)
  316.             (void) typeset(*vp++, i, 0);
  317.         rv = (*tp->val.f)(ap);
  318.         break;
  319.  
  320.     case CFUNC:            /* function call */
  321.         if (!(tp->flag&ISSET))
  322.             errorf("%s: undefined function\n", cp);
  323.         l->argv = ap;
  324.         for (i = 0; *ap++ != NULL; i++)
  325.             ;
  326.         l->argc = i - 1;
  327.         resetopts();
  328.         while (*vp != NULL)
  329.             (void) typeset(*vp++, LOCAL, 0);
  330.         e.type = E_FUNC;
  331.         if (setjmp(e.jbuf))
  332.             rv = exstat; /* return # */
  333.         else
  334.             rv = execute(tp->val.t, 0);
  335.         break;
  336.  
  337.     case CEXEC:        /* executable command */
  338.         if (!(tp->flag&ISSET)) {
  339.             /*
  340.              * mlj addition:
  341.              *
  342.              * If you specify a full path to a file
  343.              * (or type the name of a file in .) which
  344.              * doesn't have execute priv's, it used to
  345.              * just say "not found".  Kind of annoying,
  346.              * particularly if you just wrote a script
  347.              * but forgot to say chmod 755 script.
  348.              *
  349.              * This should probably be done in eaccess(),
  350.              * but it works here (at least I haven't noticed
  351.              * changing errno here breaking something
  352.              * else).
  353.              *
  354.              * So, we assume that if the file exists, it
  355.              * doesn't have execute privs; else, it really
  356.              * is not found.
  357.              */
  358.             if (access(cp, 0) < 0)
  359.                 shellf("%s: not found\n", cp);
  360.             else
  361.                 shellf("%s: cannot execute\n", cp);
  362.             rv = 1;
  363.             break;
  364.         }
  365.  
  366.         /* set $_ to program's full path */
  367.         setstr(typeset("_", LOCAL|EXPORT, 0), tp->val.s);
  368.         while (*vp != NULL)
  369.             (void) typeset(*vp++, LOCAL|EXPORT, 0);
  370.  
  371.         if ((flags&XEXEC)) {
  372.             j_exit();
  373.             if (flag[FMONITOR] || !(flags&XBGND))
  374.             {
  375. #ifdef USE_SIGACT
  376.               sigaction(SIGINT, &Sigact_dfl, NULL);
  377.               sigaction(SIGQUIT, &Sigact_dfl, NULL);
  378. #else
  379.               signal(SIGINT, SIG_DFL);
  380.               signal(SIGQUIT, SIG_DFL);
  381. #endif
  382.             }
  383.         }
  384.  
  385.         /* to fork we set up a TEXEC node and call execute */
  386.         texec.left = t;    /* for tprint */
  387.         texec.str = tp->val.s;
  388.         texec.args = ap;
  389.         rv = exchild(&texec, flags);
  390.         break;
  391.     }
  392.     if (rv != 0 && flag[FERREXIT])
  393.         leave(rv);
  394.     return (exstat = rv);
  395. }
  396.  
  397. #ifdef    SHARPBANG
  398. static void
  399. scriptexec(tp, ap)
  400.     register struct op *tp;
  401.     register char **ap;
  402. {
  403.     char line[LINE];
  404.     register char *cp;
  405.     register int fd, n;
  406.     char *shell;
  407.  
  408.     shell = strval(global("EXECSHELL"));
  409.     if (shell && *shell) {
  410.         if ((shell = search(shell,path,1)) == NULL)
  411.             shell = SHELL;
  412.     } else {
  413.         shell = SHELL;
  414.     }
  415.  
  416.     *tp->args-- = tp->str;
  417.     line[0] = '\0';
  418.     if ((fd = open(tp->str,0)) >= 0) {
  419.         if ((n = read(fd, line, LINE - 1)) > 0)
  420.             line[n] = '\0';
  421.         (void) close(fd);
  422.     }
  423.     if (line[0] == '#' && line[1] == '!') {
  424.         cp = &line[2];
  425.         while (*cp && (*cp == ' ' || *cp == '\t'))
  426.             cp++;
  427.         if (*cp && *cp != '\n') {
  428.             *tp->args = cp;
  429.             while (*cp && *cp != '\n' && *cp != ' ' && *cp != '\t')
  430.                 cp++;
  431.             if (*cp && *cp != '\n') {
  432.                 *cp++ = '\0';
  433.                 while (*cp && (*cp == ' ' || *cp == '\t'))
  434.                     cp++;
  435.                 if (*cp && *cp != '\n') {
  436.                     tp->args--;
  437.                     tp->args[0] = tp->args[1];
  438.                     tp->args[1] = cp;
  439.                     while (*cp && *cp != '\n' &&
  440.                            *cp != ' ' && *cp != '\t')
  441.                         cp++;
  442.                 }
  443.             }
  444.             *cp = '\0';
  445.         } else
  446.             *tp->args = shell;
  447.     } else
  448.         *tp->args = shell;
  449.  
  450.     (void) execve(tp->args[0], tp->args, ap);
  451.     errorf( "No shell\n" );
  452. }
  453. #endif    /* SHARPBANG */
  454.  
  455. int
  456. shcomexec(wp)
  457.     register char **wp;
  458. {
  459.     register struct tbl *tp;
  460.  
  461.     tp = tsearch(&builtins, *wp, hash(*wp));
  462.     if (tp == NULL)
  463.         errorf("%s: shcomexec botch\n", *wp);
  464.     return (*tp->val.f)(wp);
  465. }
  466.  
  467. /*
  468.  * define function
  469.  */
  470. int
  471. define(name, t)
  472.     char    *name;
  473.     struct op *t;
  474. {
  475.     register struct block *l;
  476.     register struct tbl *tp;
  477.  
  478.     for (l = e.loc; l != NULL; l = l->next) {
  479.         lastarea = &l->area;
  480.         tp = tsearch(&l->funs, name, hash(name));
  481.         if (tp != NULL && (tp->flag&DEFINED))
  482.             break;
  483.         if (l->next == NULL) {
  484.             tp = tenter(&l->funs, name, hash(name));
  485.             tp->flag = DEFINED|FUNCT;
  486.             tp->type = CFUNC;
  487.         }
  488.     }
  489.  
  490.     if ((tp->flag&ALLOC))
  491.         tfree(tp->val.t, lastarea);
  492.     tp->flag &= ~(ISSET|ALLOC);
  493.  
  494.     if (t == NULL) {        /* undefine */
  495.         tdelete(tp);
  496.         return 0;
  497.     }
  498.  
  499.     tp->val.t = tcopy(t, lastarea);
  500.     tp->flag |= (ISSET|ALLOC);
  501.  
  502.     return 0;
  503. }
  504.  
  505. /*
  506.  * add builtin
  507.  */
  508. builtin(name, func)
  509.     char *name;
  510.     int (*func)();
  511. {
  512.     register struct tbl *tp;
  513.     int flag = DEFINED;
  514.  
  515.     if (*name == '=') {        /* sets keyword variables */
  516.         name++;
  517.         flag |= TRACE;    /* command does variable assignment */
  518.     }
  519.  
  520.     tp = tenter(&builtins, name, hash(name));
  521.     tp->flag |= flag;
  522.     tp->type = CSHELL;
  523.     tp->val.f = func;
  524. }
  525.  
  526. /*
  527.  * find command
  528.  * either function, hashed command, or built-in (in that order)
  529.  */
  530. struct tbl *
  531. findcom(name, insert)
  532.     char    *name;
  533.     int    insert;            /* insert if not found */
  534. {
  535.     register struct block *l = e.loc;
  536.     unsigned int h = hash(name);
  537.     register struct    tbl *tp = NULL;
  538.     static struct tbl temp;
  539.  
  540.     if (strchr(name, '/') != NULL) {
  541.         tp = &temp;
  542.         tp->type = CEXEC;
  543.         tp->flag = 0;    /* make ~ISSET */
  544.         goto Search;
  545.     }
  546.     for (l = e.loc; l != NULL; l = l->next) {
  547.         tp = tsearch(&l->funs, name, h);
  548.         if (tp != NULL && (tp->flag&DEFINED))
  549.             break;
  550.     }
  551.     if (tp == NULL) {
  552.         tp = tsearch(&commands, name, h);
  553.         if (tp != NULL && eaccess(tp->val.s,1) != 0) {
  554.             if (tp->flag&ALLOC)
  555.                 afree(tp->val.s, commands.areap);
  556.             tp->type = CEXEC;
  557.             tp->flag = DEFINED;
  558.         }
  559.     }
  560.     if (tp == NULL)
  561.         tp = tsearch(&builtins, name, h);
  562.     if (tp == NULL) {
  563.         tp = tenter(&commands, name, h);
  564.         tp->type = CEXEC;
  565.         tp->flag = DEFINED;
  566.     }
  567.   Search:
  568.     if (tp->type == CEXEC && !(tp->flag&ISSET)) {
  569.         if (!insert) {
  570.             tp = &temp;
  571.             tp->type = CEXEC;
  572.             tp->flag = 0;    /* make ~ISSET */
  573.         }
  574.         name = search(name, path, 1);
  575.         if (name != NULL) {
  576.             tp->val.s = strsave(name,
  577.                         (tp == &temp) ? ATEMP : APERM);
  578.             tp->flag |= ISSET|ALLOC;
  579.         }
  580.     }
  581.     return tp;
  582. }
  583.  
  584. /*
  585.  * flush executable commands with relative paths
  586.  */
  587. flushcom(all)
  588.     int all;        /* just relative or all */
  589. {
  590.     register struct tbl *tp;
  591.  
  592.     for (twalk(&commands); (tp = tnext()) != NULL; )
  593.         if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
  594.             if ((tp->flag&ALLOC))
  595.                 afree(tp->val.s, commands.areap);
  596.             tp->flag = DEFINED; /* make ~ISSET */
  597.         }
  598. }
  599.  
  600. /*
  601.  * search for command with PATH
  602.  */
  603. char *
  604. search(name, path, mode)
  605.     char *name, *path;
  606.     int mode;        /* 0: readable; 1: executable */
  607. {
  608.     register int i;
  609.     register char *sp, *tp;
  610.     struct stat buf;
  611.  
  612.     if (strchr(name, '/'))
  613.         return (eaccess(name, mode) == 0) ? name : NULL;
  614.  
  615.     sp = path;
  616.     while (sp != NULL) {
  617.         tp = line;
  618.         for (; *sp != '\0'; tp++)
  619.             if ((*tp = *sp++) == ':') {
  620.                 --sp;
  621.                 break;
  622.             }
  623.         if (tp != line)
  624.             *tp++ = '/';
  625.         for (i = 0; (*tp++ = name[i++]) != '\0';)
  626.             ;
  627.         i = eaccess(line, mode);
  628.         if (i == 0 && (mode != 1 || (stat(line,&buf) == 0 &&
  629.             (buf.st_mode & S_IFMT) == S_IFREG)))
  630.             return line;
  631.         /* what should we do about EACCES? */
  632.         if (*sp++ == '\0')
  633.             sp = NULL;
  634.     }
  635.     return NULL;
  636. }
  637.  
  638. /*
  639.  * set up redirection, saving old fd's in e.savefd
  640.  */
  641. static void
  642. iosetup(iop)
  643.     register struct ioword *iop;
  644. {
  645.     register int u = -1;
  646.     char *cp = iop->name;
  647.     extern long lseek();
  648.  
  649.     if (iop->unit == 0 || iop->unit == 1 || iop->unit == 2)
  650.         e.interactive = 0;
  651. #if 0
  652.     if (e.savefd[iop->unit] != 0)
  653.         errorf("file descriptor %d already redirected\n", iop->unit);
  654. #endif
  655.     e.savefd[iop->unit] = savefd(iop->unit);
  656.  
  657.     if ((iop->flag&IOTYPE) != IOHERE)
  658.         cp = evalstr(cp, DOTILDE);
  659.  
  660.     switch (iop->flag&IOTYPE) {
  661.       case IOREAD:
  662.         u = open(cp, 0);
  663.         break;
  664.  
  665.       case IOCAT:
  666.         if ((u = open(cp, 1)) >= 0) {
  667.             (void) lseek(u, (long)0, 2);
  668.             break;
  669.         }
  670.         /* FALLTHROUGH */
  671.       case IOWRITE:
  672.         u = creat(cp, 0666);
  673.         break;
  674.  
  675.       case IORDWR:
  676.         u = open(cp, 2);
  677.         break;
  678.  
  679.       case IOHERE:
  680.         u = herein(cp, iop->flag&IOEVAL);
  681.         /* cp may have wrong name */
  682.         break;
  683.  
  684.       case IODUP:
  685.         if (*cp == '-')
  686.             close(u = iop->unit);
  687.         else
  688.         if (digit(*cp))
  689.             u = *cp - '0';
  690.         else
  691.             errorf("%s: illegal >& argument\n", cp);
  692.         break;
  693.     }
  694.     if (u < 0)
  695.         errorf("%s: cannot %s\n", cp,
  696.                (iop->flag&IOTYPE) == IOWRITE ? "create" : "open");
  697.     if (u != iop->unit) {
  698.         (void) dup2(u, iop->unit);
  699.         if (iop->flag != IODUP)
  700.             close(u);
  701.     }
  702.  
  703.     fopenshf(iop->unit);
  704. }
  705.  
  706. /*
  707.  * open here document temp file.
  708.  * if unquoted here, expand here temp file into second temp file.
  709.  */
  710. static int
  711. herein(hname, sub)
  712.     char *hname;
  713.     int sub;
  714. {
  715.     int fd;
  716.     FILE * volatile f = NULL;
  717.  
  718.     f = fopen(hname, "r");
  719.     if (f == NULL)
  720.         return -1;
  721.  
  722.     setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  723.  
  724.     if (sub) {
  725.         char *cp;
  726.         struct source *s;
  727.         struct temp *h;
  728.  
  729.         newenv(E_ERRH);
  730.         if (setjmp(e.jbuf)) {
  731.             if (f != NULL)
  732.                 fclose(f);
  733.             quitenv();
  734.             return -1; /* todo: error()? */
  735.         }
  736.  
  737.         /* set up yylex input from here file */
  738.         s = pushs(SFILE);
  739.         s->u.file = f;
  740.         source = s;
  741.         if (yylex(ONEWORD) != LWORD)
  742.             errorf("exec:herein error\n");
  743.         cp = evalstr(yylval.cp, 0);
  744.  
  745.         /* write expanded input to another temp file */
  746.         h = maketemp(ATEMP);
  747.         h->next = e.temps; e.temps = h;
  748.         if (h == NULL)
  749.             error();
  750.         f = fopen(h->name, "w+");
  751.         if (f == NULL)
  752.             error();
  753.         setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  754.         fputs(cp, f);
  755.         rewind(f);
  756.  
  757.         quitenv();
  758.     }
  759.     fd = dup(fileno(f));
  760.     fclose(f);
  761.     return fd;
  762. }
  763.  
  764. static void
  765. echo(vp, ap)
  766.     register char **vp, **ap;
  767. {
  768.     shellf("+");
  769.     while (*vp != NULL)
  770.         shellf(" %s", *vp++);
  771.     while (*ap != NULL)
  772.         shellf(" %s", *ap++);
  773.     shellf("\n");
  774. }
  775.  
  776.