home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / k / ksh48.zip / sh / exec.c < prev    next >
C/C++ Source or Header  |  1993-01-12  |  21KB  |  1,030 lines

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