Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $NetBSD: eval.c,v 1.56 2000/05/22 10:18:46 elric Exp $  */
  2.  
  3. /*-
  4.  * Copyright (c) 1993
  5.  *  The Regents of the University of California.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Kenneth Almquist.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *  This product includes software developed by the University of
  21.  *  California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. #include <sys/cdefs.h>
  40. #ifndef lint
  41. #if 0
  42. static char sccsid[] = "@(#)eval.c  8.9 (Berkeley) 6/8/95";
  43. #else
  44. __RCSID("$NetBSD: eval.c,v 1.56 2000/05/22 10:18:46 elric Exp $");
  45. #endif
  46. #endif /* not lint */
  47.  
  48. #include <sys/types.h>
  49. #include <signal.h>
  50. #include <malloc.h>
  51. #include <unistd.h>
  52.  
  53. /*
  54.  * Evaluate a command.
  55.  */
  56.  
  57. #include "shell.h"
  58. #include "nodes.h"
  59. #include "syntax.h"
  60. #include "expand.h"
  61. #include "parser.h"
  62. #include "jobs.h"
  63. #include "eval.h"
  64. #include "builtins.h"
  65. #include "options.h"
  66. #include "exec.h"
  67. #include "redir.h"
  68. #include "input.h"
  69. #include "output.h"
  70. #include "trap.h"
  71. #include "var.h"
  72. #include "memalloc.h"
  73. #include "error.h"
  74. #include "show.h"
  75. #include "mystring.h"
  76. #ifndef SMALL
  77. #include "myhistedit.h"
  78. #endif
  79.  
  80.  
  81. /* flags in argument to evaltree */
  82. #define EV_EXIT 01      /* exit after evaluating tree */
  83. #define EV_TESTED 02        /* exit status is checked; ignore -e flag */
  84. #define EV_BACKCMD 04       /* command executing within back quotes */
  85.  
  86. MKINIT int evalskip;        /* set if we are skipping commands */
  87. STATIC int skipcount;       /* number of levels to skip */
  88. MKINIT int loopnest;        /* current loop nesting level */
  89. int funcnest;           /* depth of function calls */
  90.  
  91.  
  92. char *commandname;
  93. struct strlist *cmdenviron;
  94. int exitstatus;         /* exit status of last command */
  95. int oexitstatus;        /* saved exit status */
  96.  
  97.  
  98. STATIC void evalloop (union node *, int);
  99. STATIC void evalfor (union node *, int);
  100. STATIC void evalcase (union node *, int);
  101. STATIC void evalsubshell (union node *, int);
  102. STATIC void expredir (union node *);
  103. STATIC void evalpipe (union node *);
  104. STATIC void evalcommand (union node *, int, struct backcmd *);
  105. STATIC void prehash (union node *);
  106. STATIC int is_assignment_builtin (const char *);
  107. STATIC const char *get_standard_path (void);
  108.  
  109.  
  110. /*
  111.  * Called to reset things after an exception.
  112.  */
  113.  
  114. #ifdef mkinit
  115. INCLUDE "eval.h"
  116.  
  117. RESET {
  118.     evalskip = 0;
  119.     loopnest = 0;
  120.     funcnest = 0;
  121. }
  122.  
  123. SHELLPROC {
  124.     exitstatus = 0;
  125. }
  126. #endif
  127.  
  128.  
  129.  
  130. /*
  131.  * The eval commmand.
  132.  */
  133.  
  134. int
  135. evalcmd(argc, argv)
  136.     int argc;
  137.     char **argv;
  138. {
  139.         char *p;
  140.         char *concat;
  141.         char **ap;
  142.  
  143.         if (argc > 1) {
  144.                 p = argv[1];
  145.                 if (argc > 2) {
  146.                         STARTSTACKSTR(concat);
  147.                         ap = argv + 2;
  148.                         for (;;) {
  149.                                 while (*p)
  150.                                         STPUTC(*p++, concat);
  151.                                 if ((p = *ap++) == NULL)
  152.                                         break;
  153.                                 STPUTC(' ', concat);
  154.                         }
  155.                         STPUTC('\0', concat);
  156.                         p = grabstackstr(concat);
  157.                 }
  158.                 evalstring(p, EV_TESTED);
  159.         }
  160.         return exitstatus;
  161. }
  162.  
  163.  
  164. /*
  165.  * Execute a command or commands contained in a string.
  166.  */
  167.  
  168. void
  169. evalstring(s, flag)
  170.     char *s;
  171.     int flag;
  172.     {
  173.     union node *n;
  174.     struct stackmark smark;
  175.  
  176.     setstackmark(&smark);
  177.     setinputstring(s, 1);
  178.     while ((n = parsecmd(0)) != NEOF) {
  179.         evaltree(n, flag);
  180.         popstackmark(&smark);
  181.     }
  182.     popfile();
  183.     popstackmark(&smark);
  184. }
  185.  
  186.  
  187.  
  188. /*
  189.  * Evaluate a parse tree.  The value is left in the global variable
  190.  * exitstatus.
  191.  */
  192.  
  193. void
  194. evaltree(n, flags)
  195.     union node *n;
  196.     int flags;
  197. {
  198.     if (n == NULL) {
  199.         TRACE(("evaltree(NULL) called\n"));
  200.         exitstatus = 0;
  201.         goto out;
  202.     }
  203. #ifndef SMALL
  204.     displayhist = 1;    /* show history substitutions done with fc */
  205. #endif
  206.     TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
  207.     switch (n->type) {
  208.     case NSEMI:
  209.         evaltree(n->nbinary.ch1, flags & EV_TESTED);
  210.         if (evalskip)
  211.             goto out;
  212.         evaltree(n->nbinary.ch2, flags);
  213.         break;
  214.     case NAND:
  215.         evaltree(n->nbinary.ch1, EV_TESTED);
  216.         if (evalskip || exitstatus != 0) {
  217.             /* don't bomb out on "set -e; false && true" */
  218.             flags |= EV_TESTED;
  219.             goto out;
  220.         }
  221.         evaltree(n->nbinary.ch2, flags | EV_TESTED);
  222.         break;
  223.     case NOR:
  224.         evaltree(n->nbinary.ch1, EV_TESTED);
  225.         if (evalskip || exitstatus == 0)
  226.             goto out;
  227.         evaltree(n->nbinary.ch2, flags | EV_TESTED);
  228.         break;
  229.     case NREDIR:
  230.         expredir(n->nredir.redirect);
  231.         redirect(n->nredir.redirect, REDIR_PUSH);
  232.         evaltree(n->nredir.n, flags);
  233.         popredir();
  234.         break;
  235.     case NSUBSHELL:
  236.         evalsubshell(n, flags);
  237.         break;
  238.     case NBACKGND:
  239.         evalsubshell(n, flags);
  240.         break;
  241.     case NIF: {
  242.         evaltree(n->nif.test, EV_TESTED);
  243.         if (evalskip)
  244.             goto out;
  245.         if (exitstatus == 0)
  246.             evaltree(n->nif.ifpart, flags);
  247.         else if (n->nif.elsepart)
  248.             evaltree(n->nif.elsepart, flags);
  249.         else
  250.             exitstatus = 0;
  251.         break;
  252.     }
  253.     case NWHILE:
  254.     case NUNTIL:
  255.         evalloop(n, flags);
  256.         break;
  257.     case NFOR:
  258.         evalfor(n, flags);
  259.         break;
  260.     case NCASE:
  261.         evalcase(n, flags);
  262.         break;
  263.     case NDEFUN:
  264.         if (is_special_builtin(n->narg.text)) {
  265.             outfmt(out2, "%s is a special built-in\n", n->narg.text);
  266.             exitstatus = 1;
  267.             break;
  268.         }
  269.         defun(n->narg.text, n->narg.next);
  270.         exitstatus = 0;
  271.         break;
  272.     case NNOT:
  273.         evaltree(n->nnot.com, EV_TESTED);
  274.         exitstatus = !exitstatus;
  275.         break;
  276.  
  277.     case NPIPE:
  278.         evalpipe(n);
  279.         break;
  280.     case NCMD:
  281.         evalcommand(n, flags, (struct backcmd *)NULL);
  282.         break;
  283.     default:
  284.         out1fmt("Node type = %d\n", n->type);
  285.         flushout(&output);
  286.         break;
  287.     }
  288. out:
  289.     if (pendingsigs)
  290.         dotrap();
  291.     if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
  292.         exitshell(exitstatus);
  293. }
  294.  
  295.  
  296. STATIC void
  297. evalloop(n, flags)
  298.     union node *n;
  299.     int flags;
  300. {
  301.     int status;
  302.  
  303.     loopnest++;
  304.     status = 0;
  305.     for (;;) {
  306.         evaltree(n->nbinary.ch1, EV_TESTED);
  307.         if (evalskip) {
  308. skipping:     if (evalskip == SKIPCONT && --skipcount <= 0) {
  309.                 evalskip = 0;
  310.                 continue;
  311.             }
  312.             if (evalskip == SKIPBREAK && --skipcount <= 0)
  313.                 evalskip = 0;
  314.             break;
  315.         }
  316.         if (n->type == NWHILE) {
  317.             if (exitstatus != 0)
  318.                 break;
  319.         } else {
  320.             if (exitstatus == 0)
  321.                 break;
  322.         }
  323.         evaltree(n->nbinary.ch2, flags & EV_TESTED);
  324.         status = exitstatus;
  325.         if (evalskip)
  326.             goto skipping;
  327.     }
  328.     loopnest--;
  329.     exitstatus = status;
  330. }
  331.  
  332.  
  333.  
  334. STATIC void
  335. evalfor(n, flags)
  336.     union node *n;
  337.     int flags;
  338. {
  339.     struct arglist arglist;
  340.     union node *argp;
  341.     struct strlist *sp;
  342.     struct stackmark smark;
  343.  
  344.     setstackmark(&smark);
  345.     arglist.lastp = &arglist.list;
  346.     for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
  347.         oexitstatus = exitstatus;
  348.         expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
  349.         if (evalskip)
  350.             goto out;
  351.     }
  352.     *arglist.lastp = NULL;
  353.  
  354.     exitstatus = 0;
  355.     loopnest++;
  356.     for (sp = arglist.list ; sp ; sp = sp->next) {
  357.         setvar(n->nfor.var, sp->text, 0);
  358.         evaltree(n->nfor.body, flags & EV_TESTED);
  359.         if (evalskip) {
  360.             if (evalskip == SKIPCONT && --skipcount <= 0) {
  361.                 evalskip = 0;
  362.                 continue;
  363.             }
  364.             if (evalskip == SKIPBREAK && --skipcount <= 0)
  365.                 evalskip = 0;
  366.             break;
  367.         }
  368.     }
  369.     loopnest--;
  370. out:
  371.     popstackmark(&smark);
  372. }
  373.  
  374.  
  375.  
  376. STATIC void
  377. evalcase(n, flags)
  378.     union node *n;
  379.     int flags;
  380. {
  381.     union node *cp;
  382.     union node *patp;
  383.     struct arglist arglist;
  384.     struct stackmark smark;
  385.  
  386.     setstackmark(&smark);
  387.     arglist.lastp = &arglist.list;
  388.     oexitstatus = exitstatus;
  389.     expandarg(n->ncase.expr, &arglist, EXP_TILDE);
  390.     for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
  391.         for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
  392.             if (casematch(patp, arglist.list->text)) {
  393.                 if (evalskip == 0) {
  394.                     evaltree(cp->nclist.body, flags);
  395.                 }
  396.                 goto out;
  397.             }
  398.         }
  399.     }
  400. out:
  401.     popstackmark(&smark);
  402. }
  403.  
  404.  
  405.  
  406. /*
  407.  * Kick off a subshell to evaluate a tree.
  408.  */
  409.  
  410. STATIC void
  411. evalsubshell(n, flags)
  412.     union node *n;
  413.     int flags;
  414. {
  415.     struct job *jp;
  416.     int backgnd = (n->type == NBACKGND);
  417.  
  418.     expredir(n->nredir.redirect);
  419.     jp = makejob(n, 1);
  420.     if (forkshell(jp, n, backgnd) == 0) {
  421.         if (backgnd)
  422.             flags &=~ EV_TESTED;
  423.         redirect(n->nredir.redirect, 0);
  424.         evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
  425.     }
  426.     if (! backgnd) {
  427.         INTOFF;
  428.         exitstatus = waitforjob(jp);
  429.         INTON;
  430.     }
  431. }
  432.  
  433.  
  434.  
  435. /*
  436.  * Compute the names of the files in a redirection list.
  437.  */
  438.  
  439. STATIC void
  440. expredir(n)
  441.     union node *n;
  442. {
  443.     union node *redir;
  444.  
  445.     for (redir = n ; redir ; redir = redir->nfile.next) {
  446.         struct arglist fn;
  447.         fn.lastp = &fn.list;
  448.         oexitstatus = exitstatus;
  449.         switch (redir->type) {
  450.         case NFROMTO:
  451.         case NFROM:
  452.         case NTO:
  453.         case NAPPEND:
  454.         case NTOOV:
  455.             expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
  456.             redir->nfile.expfname = fn.list->text;
  457.             break;
  458.         case NFROMFD:
  459.         case NTOFD:
  460.             if (redir->ndup.vname) {
  461.                 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
  462.                 fixredir(redir, fn.list->text, 1);
  463.             }
  464.             break;
  465.         }
  466.     }
  467. }
  468.  
  469.  
  470.  
  471. /*
  472.  * Evaluate a pipeline.  All the processes in the pipeline are children
  473.  * of the process creating the pipeline.  (This differs from some versions
  474.  * of the shell, which make the last process in a pipeline the parent
  475.  * of all the rest.)
  476.  */
  477.  
  478. STATIC void
  479. evalpipe(n)
  480.     union node *n;
  481. {
  482.     struct job *jp;
  483.     struct nodelist *lp;
  484.     int pipelen;
  485.     int prevfd;
  486.     int pip[2];
  487.  
  488.     TRACE(("evalpipe(0x%lx) called\n", (long)n));
  489.     pipelen = 0;
  490.     for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
  491.         pipelen++;
  492.     INTOFF;
  493.     jp = makejob(n, pipelen);
  494.     prevfd = -1;
  495.     for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
  496.         prehash(lp->n);
  497.         pip[1] = -1;
  498.         if (lp->next) {
  499.             if (pipe(pip) < 0) {
  500.                 close(prevfd);
  501.                 error("Pipe call failed");
  502.             }
  503.         }
  504.         if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
  505.             INTON;
  506.             if (prevfd > 0) {
  507.                 close(0);
  508.                 copyfd(prevfd, 0);
  509.                 close(prevfd);
  510.                 if (pip[0] == 0) {
  511.                     pip[0] = -1;
  512.                 }
  513.             }
  514.             if (pip[1] >= 0) {
  515.                 if (pip[0] >= 0) {
  516.                     close(pip[0]);
  517.                 }
  518.                 if (pip[1] != 1) {
  519.                     close(1);
  520.                     copyfd(pip[1], 1);
  521.                     close(pip[1]);
  522.                 }
  523.             }
  524.             evaltree(lp->n, EV_EXIT);
  525.         }
  526.         if (prevfd >= 0)
  527.             close(prevfd);
  528.         prevfd = pip[0];
  529.         close(pip[1]);
  530.     }
  531.     INTON;
  532.     if (n->npipe.backgnd == 0) {
  533.         INTOFF;
  534.         exitstatus = waitforjob(jp);
  535.         TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
  536.         INTON;
  537.     }
  538. }
  539.  
  540.  
  541.  
  542. /*
  543.  * Execute a command inside back quotes.  If it's a builtin command, we
  544.  * want to save its output in a block obtained from malloc.  Otherwise
  545.  * we fork off a subprocess and get the output of the command via a pipe.
  546.  * Should be called with interrupts off.
  547.  */
  548.  
  549. void
  550. evalbackcmd(n, result)
  551.     union node *n;
  552.     struct backcmd *result;
  553. {
  554.     int pip[2];
  555.     struct job *jp;
  556.     struct stackmark smark;     /* unnecessary */
  557.  
  558.     setstackmark(&smark);
  559.     result->fd = -1;
  560.     result->buf = NULL;
  561.     result->nleft = 0;
  562.     result->jp = NULL;
  563.     if (n == NULL) {
  564.         exitstatus = 0;
  565.         goto out;
  566.     }
  567. #ifdef notyet
  568.     /*
  569.      * For now we disable executing builtins in the same
  570.      * context as the shell, because we are not keeping
  571.      * enough state to recover from changes that are
  572.      * supposed only to affect subshells. eg. echo "`cd /`"
  573.      */
  574.     if (n->type == NCMD) {
  575.         exitstatus = oexitstatus;
  576.         evalcommand(n, EV_BACKCMD, result);
  577.     } else
  578. #endif
  579.     {
  580.         exitstatus = 0;
  581.         if (pipe(pip) < 0)
  582.             error("Pipe call failed");
  583.         jp = makejob(n, 1);
  584.         if (forkshell(jp, n, FORK_NOJOB) == 0) {
  585.             FORCEINTON;
  586.             close(pip[0]);
  587.             if (pip[1] != 1) {
  588.                 close(1);
  589.                 copyfd(pip[1], 1);
  590.                 close(pip[1]);
  591.             }
  592.             eflag = 0;
  593.             evaltree(n, EV_EXIT);
  594.         }
  595.         close(pip[1]);
  596.         result->fd = pip[0];
  597.         result->jp = jp;
  598.     }
  599. out:
  600.     popstackmark(&smark);
  601.     TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
  602.         result->fd, result->buf, result->nleft, result->jp));
  603. }
  604.  
  605.  
  606.  
  607. /*
  608.  * Execute a simple command.
  609.  */
  610.  
  611. STATIC void
  612. evalcommand(cmd, flags, backcmd)
  613.     union node *cmd;
  614.     int flags;
  615.     struct backcmd *backcmd;
  616. {
  617.     struct stackmark smark;
  618.     union node *argp;
  619.     struct arglist arglist;
  620.     struct arglist varlist;
  621.     char **argv;
  622.     int argc;
  623.     char **envp;
  624.     int varflag;
  625.     int pseudovarflag;
  626.     struct strlist *sp;
  627.     int mode;
  628.     int pip[2];
  629.     struct cmdentry cmdentry;
  630.     struct job *jp;
  631.     struct jmploc jmploc;
  632.     struct jmploc *volatile savehandler;
  633.     char *volatile savecmdname;
  634.     volatile struct shparam saveparam;
  635.     struct localvar *volatile savelocalvars;
  636.     volatile int e;
  637.     char *lastarg;
  638.     int not_special;
  639.     const char *path;
  640.     const char *standard_path;
  641. #if __GNUC__
  642.     /* Avoid longjmp clobbering */
  643.     (void) &argv;
  644.     (void) &argc;
  645.     (void) &lastarg;
  646.     (void) &flags;
  647.     (void) &not_special;
  648.     (void) &standard_path;
  649. #endif
  650.  
  651.     /* First expand the arguments. */
  652.     TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
  653.     setstackmark(&smark);
  654.     arglist.lastp = &arglist.list;
  655.     varlist.lastp = &varlist.list;
  656.     arglist.list = 0;
  657.     varflag = 1;
  658.     pseudovarflag = 0;
  659.     oexitstatus = exitstatus;
  660.     exitstatus = 0;
  661.     not_special = 0;
  662.     path = pathval();
  663.     standard_path = NULL;
  664.     for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
  665.         char *p = argp->narg.text;
  666.         if ((varflag || pseudovarflag) && is_name(*p)) {
  667.             do {
  668.                 p++;
  669.             } while (is_in_name(*p));
  670.             if (*p == '=') {
  671.                 if (varflag)
  672.                     expandarg(argp, &varlist, EXP_VARTILDE);
  673.                 else
  674.                     expandarg(argp, &arglist, EXP_VARTILDE);
  675.                 continue;
  676.             }
  677.         }
  678.         expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
  679.         if (varflag && arglist.list && is_assignment_builtin(arglist.list->text))
  680.             pseudovarflag = 1;
  681.         varflag = 0;
  682.     }
  683.     *arglist.lastp = NULL;
  684.     *varlist.lastp = NULL;
  685.     expredir(cmd->ncmd.redirect);
  686.     argc = 0;
  687.     for (sp = arglist.list ; sp ; sp = sp->next)
  688.         argc++;
  689.     argv = stalloc(sizeof (char *) * (argc + 1));
  690.  
  691.     for (sp = arglist.list ; sp ; sp = sp->next) {
  692.         TRACE(("evalcommand arg: %s\n", sp->text));
  693.         *argv++ = sp->text;
  694.     }
  695.     *argv = NULL;
  696.     lastarg = NULL;
  697.     if (iflag && funcnest == 0 && argc > 0)
  698.         lastarg = argv[-1];
  699.     argv -= argc;
  700.  
  701.     /* Print the command if xflag is set. */
  702.     if (xflag) {
  703.         outc('+', &errout);
  704.         for (sp = varlist.list ; sp ; sp = sp->next) {
  705.             outc(' ', &errout);
  706.             out2str(sp->text);
  707.         }
  708.         for (sp = arglist.list ; sp ; sp = sp->next) {
  709.             outc(' ', &errout);
  710.             out2str(sp->text);
  711.         }
  712.         outc('\n', &errout);
  713.         flushout(&errout);
  714.     }
  715.  
  716.     /* Now locate the command. */
  717.     if (argc == 0) {
  718.         cmdentry.cmdtype = CMDBUILTIN;
  719.         cmdentry.u.index = BLTINCMD;
  720.     } else {
  721.         static const char PATH[] = "PATH=";
  722.         const char *oldpath = NULL;
  723.         int findflag = DO_ERR;
  724.  
  725.         /*
  726.          * Modify the command lookup path, if a PATH= assignment
  727.          * is present
  728.          */
  729.         for (sp = varlist.list ; sp ; sp = sp->next)
  730.             if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) {
  731.                 path = sp->text + sizeof(PATH) - 1;
  732.                 findflag |= DO_BRUTE;
  733.             }
  734.         for(;;) {
  735.             find_command(argv[0], &cmdentry, findflag, path);
  736.             if (oldpath) {
  737.                 path = oldpath;
  738.                 oldpath = NULL;
  739.             }
  740.             if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
  741.                 exitstatus = 127;
  742.                 flushout(&errout);
  743.                 goto out;
  744.             }
  745.             /* implement the bltin builtin here */
  746.             if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
  747.                 not_special = 1;
  748.                 for(;;) {
  749.                     argv++;
  750.                     if (--argc == 0)
  751.                         break;
  752.                     if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
  753.                         outfmt(&errout, "%s: not found\n", *argv);
  754.                         exitstatus = 127;
  755.                         flushout(&errout);
  756.                         goto out;
  757.                     }
  758.                     if (cmdentry.u.index != BLTINCMD)
  759.                         break;
  760.                 }
  761.             }
  762.             if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == COMMANDCMD) {
  763.                 not_special = 1;
  764.                 argv++;
  765.                 if (--argc == 0) {
  766.                     exitstatus = 0;
  767.                     goto out;
  768.                 }
  769.                 if (*argv[0] == '-') {
  770.                     if (!equal(argv[0], "-p")) {
  771.                         argv--;
  772.                         argc++;
  773.                         break;
  774.                     }
  775.                     argv++;
  776.                     if (--argc == 0) {
  777.                         exitstatus = 0;
  778.                         goto out;
  779.                     }
  780.                     if (!standard_path) {
  781.                         standard_path = get_standard_path();
  782.                     }
  783.                     oldpath = path;
  784.                     path = standard_path;
  785.                     findflag |= DO_BRUTE;
  786.                 }
  787.                 findflag |= DO_NOFUN;
  788.                 continue;
  789.             }
  790.             break;
  791.         }
  792.     }
  793.  
  794.     /* Fork off a child process if necessary. */
  795.     if (cmd->ncmd.backgnd
  796.      || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
  797.      || ((flags & EV_BACKCMD) != 0
  798.         && (cmdentry.cmdtype != CMDBUILTIN
  799.          || cmdentry.u.index == DOTCMD
  800.          || cmdentry.u.index == EVALCMD))) {
  801.         jp = makejob(cmd, 1);
  802.         mode = cmd->ncmd.backgnd;
  803.         if (flags & EV_BACKCMD) {
  804.             mode = FORK_NOJOB;
  805.             if (pipe(pip) < 0)
  806.                 error("Pipe call failed");
  807.         }
  808.         if (forkshell(jp, cmd, mode) != 0)
  809.             goto parent;    /* at end of routine */
  810.         if (flags & EV_BACKCMD) {
  811.             FORCEINTON;
  812.             close(pip[0]);
  813.             if (pip[1] != 1) {
  814.                 close(1);
  815.                 copyfd(pip[1], 1);
  816.                 close(pip[1]);
  817.             }
  818.         }
  819.         flags |= EV_EXIT;
  820.     }
  821.  
  822.     /* This is the child process if a fork occurred. */
  823.     /* Execute the command. */
  824.     if (cmdentry.cmdtype == CMDFUNCTION) {
  825. #ifdef DEBUG
  826.         trputs("Shell function:  ");  trargs(argv);
  827. #endif
  828.         exitstatus = oexitstatus;
  829.         redirect(cmd->ncmd.redirect, REDIR_PUSH);
  830.         saveparam = shellparam;
  831.         shellparam.malloc = 0;
  832.         shellparam.nparam = argc - 1;
  833.         shellparam.p = argv + 1;
  834.         INTOFF;
  835.         savelocalvars = localvars;
  836.         localvars = NULL;
  837.         INTON;
  838.         if (setjmp(jmploc.loc)) {
  839.             if (exception == EXSHELLPROC) {
  840.                 freeparam((volatile struct shparam *)
  841.                     &saveparam);
  842.             } else {
  843.                 saveparam.optind = shellparam.optind;
  844.                 saveparam.optoff = shellparam.optoff;
  845.                 freeparam(&shellparam);
  846.                 shellparam = saveparam;
  847.             }
  848.             poplocalvars();
  849.             localvars = savelocalvars;
  850.             handler = savehandler;
  851.             longjmp(handler->loc, 1);
  852.         }
  853.         savehandler = handler;
  854.         handler = &jmploc;
  855.         for (sp = varlist.list ; sp ; sp = sp->next)
  856.             mklocal(sp->text);
  857.         funcnest++;
  858.         evaltree(cmdentry.u.func, flags & EV_TESTED);
  859.         funcnest--;
  860.         INTOFF;
  861.         poplocalvars();
  862.         localvars = savelocalvars;
  863.         saveparam.optind = shellparam.optind;
  864.         saveparam.optoff = shellparam.optoff;
  865.         freeparam(&shellparam);
  866.         shellparam = saveparam;
  867.         handler = savehandler;
  868.         popredir();
  869.         INTON;
  870.         if (evalskip == SKIPFUNC) {
  871.             evalskip = 0;
  872.             skipcount = 0;
  873.         }
  874.         if (flags & EV_EXIT)
  875.             exitshell(exitstatus);
  876.     } else if (cmdentry.cmdtype == CMDBUILTIN) {
  877. #ifdef DEBUG
  878.         trputs("builtin command:  ");  trargs(argv);
  879. #endif
  880.         mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
  881.         if (flags == EV_BACKCMD) {
  882. #if defined(_GNU_SOURCE) && !defined(__UCLIBC__)
  883.             openmemout();
  884. #else
  885.             memout.nleft = 0;
  886.             memout.nextc = memout.buf;
  887.             memout.bufsize = 64;
  888. #endif
  889.             mode |= REDIR_BACKQ;
  890.         }
  891.         redirect(cmd->ncmd.redirect, mode);
  892.         savecmdname = commandname;
  893.         cmdenviron = varlist.list;
  894.         e = -1;
  895.         if (setjmp(jmploc.loc)) {
  896.             e = exception;
  897.             exitstatus = (e == EXINT)? SIGINT+128 : 2;
  898.             goto cmddone;
  899.         }
  900.         savehandler = handler;
  901.         handler = &jmploc;
  902.         commandname = argv[0];
  903.         argptr = argv + 1;
  904.         optptr = NULL;          /* initialize nextopt */
  905.         exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
  906.         flushall();
  907. cmddone:
  908.         out1 = &output;
  909.         out2 = &errout;
  910.         freestdout();
  911.         if (!not_special && is_special_builtin(commandname))
  912.             listsetvar(cmdenviron);
  913.         cmdenviron = NULL;
  914.         if (e != EXSHELLPROC) {
  915.             commandname = savecmdname;
  916.             if (flags & EV_EXIT)
  917.                 exitshell(exitstatus);
  918.         }
  919.         handler = savehandler;
  920.         if (e != -1) {
  921.             if ((e != EXERROR && e != EXEXEC)
  922.                || cmdentry.u.index == BLTINCMD
  923.                || cmdentry.u.index == DOTCMD
  924.                || cmdentry.u.index == EVALCMD
  925. #ifndef SMALL
  926.                || cmdentry.u.index == HISTCMD
  927. #endif
  928.                || cmdentry.u.index == EXECCMD)
  929.                 exraise(e);
  930.             FORCEINTON;
  931.         }
  932.         if (cmdentry.u.index != EXECCMD)
  933.             popredir();
  934.         if (flags == EV_BACKCMD) {
  935. #if defined(_GNU_SOURCE) && !defined(__UCLIBC__)
  936.             closememout();
  937. #endif
  938.             backcmd->buf = memout.buf;
  939. #if defined(_GNU_SOURCE) && !defined(__UCLIBC__)
  940.             backcmd->nleft = memout.bufsize;
  941. #else
  942.             backcmd->nleft = memout.nextc - memout.buf;
  943. #endif
  944.             memout.buf = NULL;
  945.         }
  946.         cmdenviron = NULL;
  947.     } else {
  948. #ifdef DEBUG
  949.         trputs("normal command:  ");  trargs(argv);
  950. #endif
  951.         clearredir();
  952.         redirect(cmd->ncmd.redirect, 0);
  953.         for (sp = varlist.list ; sp ; sp = sp->next)
  954.             setvareq(sp->text, VEXPORT|VSTACK);
  955.         envp = environment();
  956.         shellexec(argv, envp, path, cmdentry.u.index);
  957.     }
  958.     goto out;
  959.  
  960. parent: /* parent process gets here (if we forked) */
  961.     if (mode == 0) {    /* argument to fork */
  962.         INTOFF;
  963.         exitstatus = waitforjob(jp);
  964.         INTON;
  965.     } else if (mode == 2) {
  966.         backcmd->fd = pip[0];
  967.         close(pip[1]);
  968.         backcmd->jp = jp;
  969.     }
  970.  
  971. out:
  972.     if (lastarg)
  973.         setvar("_", lastarg, 0);
  974.     popstackmark(&smark);
  975.  
  976.     if (eflag && exitstatus && !(flags & EV_TESTED))
  977.         exitshell(exitstatus);
  978. }
  979.  
  980.  
  981.  
  982. /*
  983.  * Search for a command.  This is called before we fork so that the
  984.  * location of the command will be available in the parent as well as
  985.  * the child.  The check for "goodname" is an overly conservative
  986.  * check that the name will not be subject to expansion.
  987.  */
  988.  
  989. STATIC void
  990. prehash(n)
  991.     union node *n;
  992. {
  993.     struct cmdentry entry;
  994.  
  995.     if (n->type == NCMD && n->ncmd.args)
  996.         if (goodname(n->ncmd.args->narg.text))
  997.             find_command(n->ncmd.args->narg.text, &entry, 0,
  998.                      pathval());
  999. }
  1000.  
  1001.  
  1002.  
  1003. /*
  1004.  * Builtin commands.  Builtin commands whose functions are closely
  1005.  * tied to evaluation are implemented here.
  1006.  */
  1007.  
  1008. /*
  1009.  * No command given, or a bltin command with no arguments.  Set the
  1010.  * specified variables.
  1011.  */
  1012.  
  1013. int
  1014. bltincmd(argc, argv)
  1015.     int argc;
  1016.     char **argv;
  1017. {
  1018.     listsetvar(cmdenviron);
  1019.     /*
  1020.      * Preserve exitstatus of a previous possible redirection
  1021.      * as POSIX mandates
  1022.      */
  1023.     return exitstatus;
  1024. }
  1025.  
  1026.  
  1027. /*
  1028.  * Handle break and continue commands.  Break, continue, and return are
  1029.  * all handled by setting the evalskip flag.  The evaluation routines
  1030.  * above all check this flag, and if it is set they start skipping
  1031.  * commands rather than executing them.  The variable skipcount is
  1032.  * the number of loops to break/continue, or the number of function
  1033.  * levels to return.  (The latter is always 1.)  It should probably
  1034.  * be an error to break out of more loops than exist, but it isn't
  1035.  * in the standard shell so we don't make it one here.
  1036.  */
  1037.  
  1038. int
  1039. breakcmd(argc, argv)
  1040.     int argc;
  1041.     char **argv;
  1042. {
  1043.     int n = argc > 1 ? number(argv[1]) : 1;
  1044.  
  1045.     if (n > loopnest)
  1046.         n = loopnest;
  1047.     if (n > 0) {
  1048.         evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
  1049.         skipcount = n;
  1050.     }
  1051.     return 0;
  1052. }
  1053.  
  1054.  
  1055. /*
  1056.  * The return command.
  1057.  */
  1058.  
  1059. int
  1060. returncmd(argc, argv)
  1061.     int argc;
  1062.     char **argv;
  1063. {
  1064.     int ret = argc > 1 ? number(argv[1]) : oexitstatus;
  1065.  
  1066.     if (funcnest) {
  1067.         evalskip = SKIPFUNC;
  1068.         skipcount = 1;
  1069.         return ret;
  1070.     }
  1071.     else {
  1072.         /* Do what ksh does; skip the rest of the file */
  1073.         evalskip = SKIPFILE;
  1074.         skipcount = 1;
  1075.         return ret;
  1076.     }
  1077. }
  1078.  
  1079.  
  1080. int
  1081. falsecmd(argc, argv)
  1082.     int argc;
  1083.     char **argv;
  1084. {
  1085.     return 1;
  1086. }
  1087.  
  1088.  
  1089. int
  1090. truecmd(argc, argv)
  1091.     int argc;
  1092.     char **argv;
  1093. {
  1094.     return 0;
  1095. }
  1096.  
  1097.  
  1098. int
  1099. execcmd(argc, argv)
  1100.     int argc;
  1101.     char **argv;
  1102. {
  1103.     if (argc > 1) {
  1104.         struct strlist *sp;
  1105.  
  1106.         iflag = 0;      /* exit on error */
  1107.         mflag = 0;
  1108.         optschanged();
  1109.         for (sp = cmdenviron; sp ; sp = sp->next)
  1110.             setvareq(sp->text, VEXPORT|VSTACK);
  1111.         shellexec(argv + 1, environment(), pathval(), 0);
  1112.     }
  1113.     return 0;
  1114. }
  1115.  
  1116. STATIC int
  1117. is_assignment_builtin (command)
  1118.     const char *command;
  1119. {
  1120.     static const char *assignment_builtins[] = {
  1121.         "alias", "declare", "export", "local", "readonly", "typeset",
  1122.         (char *)NULL
  1123.     };
  1124.     int i;
  1125.  
  1126.     for (i = 0; assignment_builtins[i]; i++)
  1127.         if (strcmp(command, assignment_builtins[i]) == 0) return 1;
  1128.     return 0;
  1129. }
  1130.  
  1131. int
  1132. is_special_builtin(name)
  1133.     const char *name;
  1134. {
  1135.     static const char *special_builtins[] = {
  1136.         "break", ":", ".", "continue", "eval", "exec", "exit",
  1137.         "export", "readonly", "return", "set", "shift", "times",
  1138.         "trap", "unset", (char *)NULL
  1139.     };
  1140.     int i;
  1141.  
  1142.     if (!name) return 0;
  1143.     for (i = 0; special_builtins[i]; i++)
  1144.         if (equal(name, special_builtins[i])) return 1;
  1145.     return 0;
  1146. }
  1147.  
  1148. STATIC const char *
  1149. get_standard_path()
  1150. {
  1151.     char *p;
  1152.     size_t len;
  1153.  
  1154.     len = confstr(_CS_PATH, NULL, 0);
  1155.     p = stalloc(len + 2);
  1156.     *p = '\0';
  1157.     confstr(_CS_PATH, p, len);
  1158.     return p;
  1159. }
  1160.