home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / sysutils / kornshel / exec.c < prev    next >
C/C++ Source or Header  |  1992-09-21  |  21KB  |  1,014 lines

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