home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / pdksh-4.9-src.tgz / tar.out / contrib / pdksh / sh / exec.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  19KB  |  975 lines

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