147112Sbostic /*- 247112Sbostic * Copyright (c) 1991 The Regents of the University of California. 347112Sbostic * All rights reserved. 447112Sbostic * 547112Sbostic * This code is derived from software contributed to Berkeley by 647112Sbostic * Kenneth Almquist. 747112Sbostic * 8*53298Smarc * Redistribution and use in source and binary forms, with or without 9*53298Smarc * modification, are permitted provided that the following conditions 10*53298Smarc * are met: 11*53298Smarc * 1. Redistributions of source code must retain the above copyright 12*53298Smarc * notice, this list of conditions and the following disclaimer. 13*53298Smarc * 2. Redistributions in binary form must reproduce the above copyright 14*53298Smarc * notice, this list of conditions and the following disclaimer in the 15*53298Smarc * documentation and/or other materials provided with the distribution. 16*53298Smarc * 3. All advertising materials mentioning features or use of this software 17*53298Smarc * must display the following acknowledgement: 18*53298Smarc * This product includes software developed by the University of 19*53298Smarc * California, Berkeley and its contributors. 20*53298Smarc * 4. Neither the name of the University nor the names of its contributors 21*53298Smarc * may be used to endorse or promote products derived from this software 22*53298Smarc * without specific prior written permission. 23*53298Smarc * 24*53298Smarc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25*53298Smarc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26*53298Smarc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27*53298Smarc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28*53298Smarc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29*53298Smarc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30*53298Smarc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31*53298Smarc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32*53298Smarc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33*53298Smarc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34*53298Smarc * SUCH DAMAGE. 3547112Sbostic */ 3647112Sbostic 3747112Sbostic #ifndef lint 38*53298Smarc static char sccsid[] = "@(#)eval.c 5.4 (Berkeley) 4/16/92"; 3947112Sbostic #endif /* not lint */ 4047112Sbostic 4147112Sbostic /* 4247112Sbostic * Evaluate a command. 4347112Sbostic */ 4447112Sbostic 4547112Sbostic #include "shell.h" 4647112Sbostic #include "nodes.h" 4747112Sbostic #include "syntax.h" 4847112Sbostic #include "expand.h" 4947112Sbostic #include "parser.h" 5047112Sbostic #include "jobs.h" 5147112Sbostic #include "eval.h" 5247112Sbostic #include "builtins.h" 5347112Sbostic #include "options.h" 5447112Sbostic #include "exec.h" 5547112Sbostic #include "redir.h" 5647112Sbostic #include "input.h" 5747112Sbostic #include "output.h" 5847112Sbostic #include "trap.h" 5947112Sbostic #include "var.h" 6047112Sbostic #include "memalloc.h" 6147112Sbostic #include "error.h" 6247112Sbostic #include "mystring.h" 6347112Sbostic #include <signal.h> 6447112Sbostic 6547112Sbostic 6647112Sbostic /* flags in argument to evaltree */ 6747112Sbostic #define EV_EXIT 01 /* exit after evaluating tree */ 6847112Sbostic #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ 6947112Sbostic #define EV_BACKCMD 04 /* command executing within back quotes */ 7047112Sbostic 7147112Sbostic 7247112Sbostic /* reasons for skipping commands (see comment on breakcmd routine) */ 7347112Sbostic #define SKIPBREAK 1 7447112Sbostic #define SKIPCONT 2 7547112Sbostic #define SKIPFUNC 3 7647112Sbostic 7747112Sbostic MKINIT int evalskip; /* set if we are skipping commands */ 7847112Sbostic STATIC int skipcount; /* number of levels to skip */ 7947112Sbostic MKINIT int loopnest; /* current loop nesting level */ 8047112Sbostic int funcnest; /* depth of function calls */ 8147112Sbostic 8247112Sbostic 8347112Sbostic char *commandname; 8447112Sbostic struct strlist *cmdenviron; 8547112Sbostic int exitstatus; /* exit status of last command */ 8647112Sbostic 8747112Sbostic 8847112Sbostic #ifdef __STDC__ 8947112Sbostic STATIC void evalloop(union node *); 9047112Sbostic STATIC void evalfor(union node *); 9147112Sbostic STATIC void evalcase(union node *, int); 9247112Sbostic STATIC void evalsubshell(union node *, int); 9347112Sbostic STATIC void expredir(union node *); 9447112Sbostic STATIC void evalpipe(union node *); 9547112Sbostic STATIC void evalcommand(union node *, int, struct backcmd *); 9647112Sbostic STATIC void prehash(union node *); 9747112Sbostic #else 9847112Sbostic STATIC void evalloop(); 9947112Sbostic STATIC void evalfor(); 10047112Sbostic STATIC void evalcase(); 10147112Sbostic STATIC void evalsubshell(); 10247112Sbostic STATIC void expredir(); 10347112Sbostic STATIC void evalpipe(); 10447112Sbostic STATIC void evalcommand(); 10547112Sbostic STATIC void prehash(); 10647112Sbostic #endif 10747112Sbostic 10847112Sbostic 10947112Sbostic 11047112Sbostic /* 11147112Sbostic * Called to reset things after an exception. 11247112Sbostic */ 11347112Sbostic 11447112Sbostic #ifdef mkinit 11547112Sbostic INCLUDE "eval.h" 11647112Sbostic 11747112Sbostic RESET { 11847112Sbostic evalskip = 0; 11947112Sbostic loopnest = 0; 12047112Sbostic funcnest = 0; 12147112Sbostic } 12247112Sbostic 12347112Sbostic SHELLPROC { 12447112Sbostic exitstatus = 0; 12547112Sbostic } 12647112Sbostic #endif 12747112Sbostic 12847112Sbostic 12947112Sbostic 13047112Sbostic /* 13147292Smarc * The eval commmand. 13247112Sbostic */ 13347112Sbostic 13447292Smarc evalcmd(argc, argv) 13547292Smarc char **argv; 13647292Smarc { 13747292Smarc char *p; 13847292Smarc char *concat; 13947292Smarc char **ap; 14047112Sbostic 14147292Smarc if (argc > 1) { 14247292Smarc p = argv[1]; 14347292Smarc if (argc > 2) { 14447292Smarc STARTSTACKSTR(concat); 14547292Smarc ap = argv + 2; 14647292Smarc for (;;) { 14747292Smarc while (*p) 14847292Smarc STPUTC(*p++, concat); 14947292Smarc if ((p = *ap++) == NULL) 15047292Smarc break; 15147292Smarc STPUTC(' ', concat); 15247292Smarc } 15347292Smarc STPUTC('\0', concat); 15447292Smarc p = grabstackstr(concat); 15547292Smarc } 15647292Smarc evalstring(p); 15747292Smarc } 15847292Smarc return exitstatus; 15947112Sbostic } 16047112Sbostic 16147112Sbostic 16247112Sbostic /* 16347112Sbostic * Execute a command or commands contained in a string. 16447112Sbostic */ 16547112Sbostic 16647112Sbostic void 16747112Sbostic evalstring(s) 16847112Sbostic char *s; 16947112Sbostic { 17047112Sbostic union node *n; 17147112Sbostic struct stackmark smark; 17247112Sbostic 17347112Sbostic setstackmark(&smark); 17447112Sbostic setinputstring(s, 1); 17547112Sbostic while ((n = parsecmd(0)) != NEOF) { 17647112Sbostic evaltree(n, 0); 17747112Sbostic popstackmark(&smark); 17847112Sbostic } 17947112Sbostic popfile(); 18047112Sbostic popstackmark(&smark); 18147112Sbostic } 18247112Sbostic 18347112Sbostic 18447112Sbostic 18547112Sbostic /* 18647112Sbostic * Evaluate a parse tree. The value is left in the global variable 18747112Sbostic * exitstatus. 18847112Sbostic */ 18947112Sbostic 19047112Sbostic void 19147112Sbostic evaltree(n, flags) 19247112Sbostic union node *n; 19347112Sbostic { 19447112Sbostic if (n == NULL) { 19547112Sbostic TRACE(("evaltree(NULL) called\n")); 19647112Sbostic return; 19747112Sbostic } 19847112Sbostic TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type)); 19947112Sbostic switch (n->type) { 20047112Sbostic case NSEMI: 20147112Sbostic evaltree(n->nbinary.ch1, 0); 20247112Sbostic if (evalskip) 20347112Sbostic goto out; 20447112Sbostic evaltree(n->nbinary.ch2, flags); 20547112Sbostic break; 20647112Sbostic case NAND: 20747112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 20847112Sbostic if (evalskip || exitstatus != 0) 20947112Sbostic goto out; 21047112Sbostic evaltree(n->nbinary.ch2, flags); 21147112Sbostic break; 21247112Sbostic case NOR: 21347112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 21447112Sbostic if (evalskip || exitstatus == 0) 21547112Sbostic goto out; 21647112Sbostic evaltree(n->nbinary.ch2, flags); 21747112Sbostic break; 21847112Sbostic case NREDIR: 21947112Sbostic expredir(n->nredir.redirect); 22047112Sbostic redirect(n->nredir.redirect, REDIR_PUSH); 22147112Sbostic evaltree(n->nredir.n, flags); 22247112Sbostic popredir(); 22347112Sbostic break; 22447112Sbostic case NSUBSHELL: 22547112Sbostic evalsubshell(n, flags); 22647112Sbostic break; 22747112Sbostic case NBACKGND: 22847112Sbostic evalsubshell(n, flags); 22947112Sbostic break; 23047982Smarc case NIF: { 23147982Smarc int status = 0; 23247982Smarc 23347112Sbostic evaltree(n->nif.test, EV_TESTED); 23447112Sbostic if (evalskip) 23547112Sbostic goto out; 23647112Sbostic if (exitstatus == 0) { 23747112Sbostic evaltree(n->nif.ifpart, flags); 23847982Smarc status = exitstatus; 23947112Sbostic } else if (n->nif.elsepart) { 24047112Sbostic evaltree(n->nif.elsepart, flags); 24147982Smarc status = exitstatus; 24247112Sbostic } 24347982Smarc exitstatus = status; 24447112Sbostic break; 24547982Smarc } 24647112Sbostic case NWHILE: 24747112Sbostic case NUNTIL: 24847112Sbostic evalloop(n); 24947112Sbostic break; 25047112Sbostic case NFOR: 25147112Sbostic evalfor(n); 25247112Sbostic break; 25347112Sbostic case NCASE: 25447112Sbostic evalcase(n, flags); 25547112Sbostic break; 25647112Sbostic case NDEFUN: 25747112Sbostic defun(n->narg.text, n->narg.next); 25847112Sbostic exitstatus = 0; 25947112Sbostic break; 26053175Smarc case NNOT: 26153175Smarc evaltree(n->nnot.com, EV_TESTED); 26253175Smarc exitstatus = !exitstatus; 26353175Smarc break; 26453175Smarc 26547112Sbostic case NPIPE: 26647112Sbostic evalpipe(n); 26747112Sbostic break; 26847112Sbostic case NCMD: 26947112Sbostic evalcommand(n, flags, (struct backcmd *)NULL); 27047112Sbostic break; 27147112Sbostic default: 27247112Sbostic out1fmt("Node type = %d\n", n->type); 27347112Sbostic flushout(&output); 27447112Sbostic break; 27547112Sbostic } 27647112Sbostic out: 27747112Sbostic if (pendingsigs) 27847112Sbostic dotrap(); 27947112Sbostic if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) 28047112Sbostic exitshell(exitstatus); 28147112Sbostic } 28247112Sbostic 28347112Sbostic 28447112Sbostic STATIC void 28547112Sbostic evalloop(n) 28647112Sbostic union node *n; 28747112Sbostic { 28847112Sbostic int status; 28947112Sbostic 29047112Sbostic loopnest++; 29147112Sbostic status = 0; 29247112Sbostic for (;;) { 29347112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 29447112Sbostic if (evalskip) { 29547112Sbostic skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 29647112Sbostic evalskip = 0; 29747112Sbostic continue; 29847112Sbostic } 29947112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 30047112Sbostic evalskip = 0; 30147112Sbostic break; 30247112Sbostic } 30347112Sbostic if (n->type == NWHILE) { 30447112Sbostic if (exitstatus != 0) 30547112Sbostic break; 30647112Sbostic } else { 30747112Sbostic if (exitstatus == 0) 30847112Sbostic break; 30947112Sbostic } 31047112Sbostic evaltree(n->nbinary.ch2, 0); 31147112Sbostic status = exitstatus; 31247112Sbostic if (evalskip) 31347112Sbostic goto skipping; 31447112Sbostic } 31547112Sbostic loopnest--; 31647112Sbostic exitstatus = status; 31747112Sbostic } 31847112Sbostic 31947112Sbostic 32047112Sbostic 32147112Sbostic STATIC void 32247112Sbostic evalfor(n) 32347112Sbostic union node *n; 32447112Sbostic { 32547112Sbostic struct arglist arglist; 32647112Sbostic union node *argp; 32747112Sbostic struct strlist *sp; 32847112Sbostic struct stackmark smark; 32947112Sbostic 33047112Sbostic setstackmark(&smark); 33147112Sbostic arglist.lastp = &arglist.list; 33247112Sbostic for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 333*53298Smarc expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 33447112Sbostic if (evalskip) 33547112Sbostic goto out; 33647112Sbostic } 33747112Sbostic *arglist.lastp = NULL; 33847112Sbostic 33947112Sbostic exitstatus = 0; 34047112Sbostic loopnest++; 34147112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 34247112Sbostic setvar(n->nfor.var, sp->text, 0); 34347112Sbostic evaltree(n->nfor.body, 0); 34447112Sbostic if (evalskip) { 34547112Sbostic if (evalskip == SKIPCONT && --skipcount <= 0) { 34647112Sbostic evalskip = 0; 34747112Sbostic continue; 34847112Sbostic } 34947112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 35047112Sbostic evalskip = 0; 35147112Sbostic break; 35247112Sbostic } 35347112Sbostic } 35447112Sbostic loopnest--; 35547112Sbostic out: 35647112Sbostic popstackmark(&smark); 35747112Sbostic } 35847112Sbostic 35947112Sbostic 36047112Sbostic 36147112Sbostic STATIC void 36247112Sbostic evalcase(n, flags) 36347112Sbostic union node *n; 36447112Sbostic { 36547112Sbostic union node *cp; 36647112Sbostic union node *patp; 36747112Sbostic struct arglist arglist; 36847112Sbostic struct stackmark smark; 36947112Sbostic 37047112Sbostic setstackmark(&smark); 37147112Sbostic arglist.lastp = &arglist.list; 372*53298Smarc expandarg(n->ncase.expr, &arglist, EXP_TILDE); 37347112Sbostic for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 37447112Sbostic for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 37547112Sbostic if (casematch(patp, arglist.list->text)) { 37647112Sbostic if (evalskip == 0) { 37747112Sbostic evaltree(cp->nclist.body, flags); 37847112Sbostic } 37947112Sbostic goto out; 38047112Sbostic } 38147112Sbostic } 38247112Sbostic } 38347112Sbostic out: 38447112Sbostic popstackmark(&smark); 38547112Sbostic } 38647112Sbostic 38747112Sbostic 38847112Sbostic 38947112Sbostic /* 39047112Sbostic * Kick off a subshell to evaluate a tree. 39147112Sbostic */ 39247112Sbostic 39347112Sbostic STATIC void 39447112Sbostic evalsubshell(n, flags) 39547112Sbostic union node *n; 39647112Sbostic { 39747112Sbostic struct job *jp; 39847112Sbostic int backgnd = (n->type == NBACKGND); 39947112Sbostic 40047112Sbostic expredir(n->nredir.redirect); 40147112Sbostic jp = makejob(n, 1); 40247112Sbostic if (forkshell(jp, n, backgnd) == 0) { 40347112Sbostic if (backgnd) 40447112Sbostic flags &=~ EV_TESTED; 40547112Sbostic redirect(n->nredir.redirect, 0); 40647112Sbostic evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 40747112Sbostic } 40847112Sbostic if (! backgnd) { 40947112Sbostic INTOFF; 41047112Sbostic exitstatus = waitforjob(jp); 41147112Sbostic INTON; 41247112Sbostic } 41347112Sbostic } 41447112Sbostic 41547112Sbostic 41647112Sbostic 41747112Sbostic /* 41847112Sbostic * Compute the names of the files in a redirection list. 41947112Sbostic */ 42047112Sbostic 42147112Sbostic STATIC void 42247112Sbostic expredir(n) 42347112Sbostic union node *n; 42447112Sbostic { 42547112Sbostic register union node *redir; 42647112Sbostic 42747112Sbostic for (redir = n ; redir ; redir = redir->nfile.next) { 42847112Sbostic if (redir->type == NFROM 42947112Sbostic || redir->type == NTO 43047112Sbostic || redir->type == NAPPEND) { 43147112Sbostic struct arglist fn; 43247112Sbostic fn.lastp = &fn.list; 433*53298Smarc expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 43447112Sbostic redir->nfile.expfname = fn.list->text; 43547112Sbostic } 43647112Sbostic } 43747112Sbostic } 43847112Sbostic 43947112Sbostic 44047112Sbostic 44147112Sbostic /* 44247112Sbostic * Evaluate a pipeline. All the processes in the pipeline are children 44347112Sbostic * of the process creating the pipeline. (This differs from some versions 44447112Sbostic * of the shell, which make the last process in a pipeline the parent 44547112Sbostic * of all the rest.) 44647112Sbostic */ 44747112Sbostic 44847112Sbostic STATIC void 44947112Sbostic evalpipe(n) 45047112Sbostic union node *n; 45147112Sbostic { 45247112Sbostic struct job *jp; 45347112Sbostic struct nodelist *lp; 45447112Sbostic int pipelen; 45547112Sbostic int prevfd; 45647112Sbostic int pip[2]; 45747112Sbostic 45847112Sbostic TRACE(("evalpipe(0x%x) called\n", (int)n)); 45947112Sbostic pipelen = 0; 46047112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 46147112Sbostic pipelen++; 46247112Sbostic INTOFF; 46347112Sbostic jp = makejob(n, pipelen); 46447112Sbostic prevfd = -1; 46547112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 46647112Sbostic prehash(lp->n); 46747112Sbostic pip[1] = -1; 46847112Sbostic if (lp->next) { 46947112Sbostic if (pipe(pip) < 0) { 47047112Sbostic close(prevfd); 47147112Sbostic error("Pipe call failed"); 47247112Sbostic } 47347112Sbostic } 47447112Sbostic if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 47547112Sbostic INTON; 47647112Sbostic if (prevfd > 0) { 47747112Sbostic close(0); 47847112Sbostic copyfd(prevfd, 0); 47947112Sbostic close(prevfd); 48047112Sbostic } 48147112Sbostic if (pip[1] >= 0) { 48247112Sbostic close(pip[0]); 48347112Sbostic if (pip[1] != 1) { 48447112Sbostic close(1); 48547112Sbostic copyfd(pip[1], 1); 48647112Sbostic close(pip[1]); 48747112Sbostic } 48847112Sbostic } 48947112Sbostic evaltree(lp->n, EV_EXIT); 49047112Sbostic } 49147112Sbostic if (prevfd >= 0) 49247112Sbostic close(prevfd); 49347112Sbostic prevfd = pip[0]; 49447112Sbostic close(pip[1]); 49547112Sbostic } 49647112Sbostic INTON; 49747112Sbostic if (n->npipe.backgnd == 0) { 49847112Sbostic INTOFF; 49947112Sbostic exitstatus = waitforjob(jp); 50047112Sbostic TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 50147112Sbostic INTON; 50247112Sbostic } 50347112Sbostic } 50447112Sbostic 50547112Sbostic 50647112Sbostic 50747112Sbostic /* 50847112Sbostic * Execute a command inside back quotes. If it's a builtin command, we 50947112Sbostic * want to save its output in a block obtained from malloc. Otherwise 51047112Sbostic * we fork off a subprocess and get the output of the command via a pipe. 51147112Sbostic * Should be called with interrupts off. 51247112Sbostic */ 51347112Sbostic 51447112Sbostic void 51547112Sbostic evalbackcmd(n, result) 51647112Sbostic union node *n; 51747112Sbostic struct backcmd *result; 51847112Sbostic { 51947112Sbostic int pip[2]; 52047112Sbostic struct job *jp; 52147112Sbostic struct stackmark smark; /* unnecessary */ 52247112Sbostic 52347112Sbostic setstackmark(&smark); 52447112Sbostic result->fd = -1; 52547112Sbostic result->buf = NULL; 52647112Sbostic result->nleft = 0; 52747112Sbostic result->jp = NULL; 52847112Sbostic if (n->type == NCMD) { 52947112Sbostic evalcommand(n, EV_BACKCMD, result); 53047112Sbostic } else { 53147112Sbostic if (pipe(pip) < 0) 53247112Sbostic error("Pipe call failed"); 53347112Sbostic jp = makejob(n, 1); 53447112Sbostic if (forkshell(jp, n, FORK_NOJOB) == 0) { 53547112Sbostic FORCEINTON; 53647112Sbostic close(pip[0]); 53747112Sbostic if (pip[1] != 1) { 53847112Sbostic close(1); 53947112Sbostic copyfd(pip[1], 1); 54047112Sbostic close(pip[1]); 54147112Sbostic } 54247112Sbostic evaltree(n, EV_EXIT); 54347112Sbostic } 54447112Sbostic close(pip[1]); 54547112Sbostic result->fd = pip[0]; 54647112Sbostic result->jp = jp; 54747112Sbostic } 54847112Sbostic popstackmark(&smark); 54947112Sbostic TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 55047112Sbostic result->fd, result->buf, result->nleft, result->jp)); 55147112Sbostic } 55247112Sbostic 55347112Sbostic 55447112Sbostic 55547112Sbostic /* 55647112Sbostic * Execute a simple command. 55747112Sbostic */ 55847112Sbostic 55947112Sbostic STATIC void 56047112Sbostic evalcommand(cmd, flags, backcmd) 56147112Sbostic union node *cmd; 56247112Sbostic struct backcmd *backcmd; 56347112Sbostic { 56447112Sbostic struct stackmark smark; 56547112Sbostic union node *argp; 56647112Sbostic struct arglist arglist; 56747112Sbostic struct arglist varlist; 56847112Sbostic char **argv; 56947112Sbostic int argc; 57047112Sbostic char **envp; 57147112Sbostic int varflag; 57247112Sbostic struct strlist *sp; 57347112Sbostic register char *p; 57447112Sbostic int mode; 57547112Sbostic int pip[2]; 57647112Sbostic struct cmdentry cmdentry; 57747112Sbostic struct job *jp; 57847112Sbostic struct jmploc jmploc; 57947112Sbostic struct jmploc *volatile savehandler; 58047112Sbostic char *volatile savecmdname; 58147112Sbostic volatile struct shparam saveparam; 58247112Sbostic struct localvar *volatile savelocalvars; 58347112Sbostic volatile int e; 58447112Sbostic char *lastarg; 58547112Sbostic 58647112Sbostic /* First expand the arguments. */ 58747112Sbostic TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); 58847112Sbostic setstackmark(&smark); 58947112Sbostic arglist.lastp = &arglist.list; 59047112Sbostic varlist.lastp = &varlist.list; 59147112Sbostic varflag = 1; 59247112Sbostic for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 59347112Sbostic p = argp->narg.text; 59447112Sbostic if (varflag && is_name(*p)) { 59547112Sbostic do { 59647112Sbostic p++; 59747112Sbostic } while (is_in_name(*p)); 59847112Sbostic if (*p == '=') { 599*53298Smarc expandarg(argp, &varlist, EXP_VARTILDE); 60047112Sbostic continue; 60147112Sbostic } 60247112Sbostic } 603*53298Smarc expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 60447112Sbostic varflag = 0; 60547112Sbostic } 60647112Sbostic *arglist.lastp = NULL; 60747112Sbostic *varlist.lastp = NULL; 60847112Sbostic expredir(cmd->ncmd.redirect); 60947112Sbostic argc = 0; 61047112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) 61147112Sbostic argc++; 61247112Sbostic argv = stalloc(sizeof (char *) * (argc + 1)); 613*53298Smarc for (sp = arglist.list ; sp ; sp = sp->next) { 614*53298Smarc TRACE(("evalcommand arg: %s\n", sp->text)); 61547112Sbostic *argv++ = sp->text; 616*53298Smarc } 61747112Sbostic *argv = NULL; 61847112Sbostic lastarg = NULL; 61947112Sbostic if (iflag && funcnest == 0 && argc > 0) 62047112Sbostic lastarg = argv[-1]; 62147112Sbostic argv -= argc; 62247112Sbostic 62347112Sbostic /* Print the command if xflag is set. */ 62447112Sbostic if (xflag) { 62547112Sbostic outc('+', &errout); 62647112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) { 62747112Sbostic outc(' ', &errout); 62847112Sbostic out2str(sp->text); 62947112Sbostic } 63047112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 63147112Sbostic outc(' ', &errout); 63247112Sbostic out2str(sp->text); 63347112Sbostic } 63447112Sbostic outc('\n', &errout); 63547112Sbostic flushout(&errout); 63647112Sbostic } 63747112Sbostic 63847112Sbostic /* Now locate the command. */ 63947112Sbostic if (argc == 0) { 64047112Sbostic cmdentry.cmdtype = CMDBUILTIN; 64147112Sbostic cmdentry.u.index = BLTINCMD; 64247112Sbostic } else { 64347112Sbostic find_command(argv[0], &cmdentry, 1); 64447112Sbostic if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ 64547112Sbostic exitstatus = 2; 64647112Sbostic flushout(&errout); 64747112Sbostic return; 64847112Sbostic } 64947112Sbostic /* implement the bltin builtin here */ 65047112Sbostic if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { 65147112Sbostic for (;;) { 65247112Sbostic argv++; 65347112Sbostic if (--argc == 0) 65447112Sbostic break; 65547112Sbostic if ((cmdentry.u.index = find_builtin(*argv)) < 0) { 65647112Sbostic outfmt(&errout, "%s: not found\n", *argv); 65747112Sbostic exitstatus = 2; 65847112Sbostic flushout(&errout); 65947112Sbostic return; 66047112Sbostic } 66147112Sbostic if (cmdentry.u.index != BLTINCMD) 66247112Sbostic break; 66347112Sbostic } 66447112Sbostic } 66547112Sbostic } 66647112Sbostic 66747112Sbostic /* Fork off a child process if necessary. */ 66847112Sbostic if (cmd->ncmd.backgnd 66947112Sbostic || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 67047112Sbostic || (flags & EV_BACKCMD) != 0 67147112Sbostic && (cmdentry.cmdtype != CMDBUILTIN 67247112Sbostic || cmdentry.u.index == DOTCMD 67347112Sbostic || cmdentry.u.index == EVALCMD)) { 67447112Sbostic jp = makejob(cmd, 1); 67547112Sbostic mode = cmd->ncmd.backgnd; 67647112Sbostic if (flags & EV_BACKCMD) { 67747112Sbostic mode = FORK_NOJOB; 67847112Sbostic if (pipe(pip) < 0) 67947112Sbostic error("Pipe call failed"); 68047112Sbostic } 68147112Sbostic if (forkshell(jp, cmd, mode) != 0) 68247112Sbostic goto parent; /* at end of routine */ 68347112Sbostic if (flags & EV_BACKCMD) { 68447112Sbostic FORCEINTON; 68547112Sbostic close(pip[0]); 68647112Sbostic if (pip[1] != 1) { 68747112Sbostic close(1); 68847112Sbostic copyfd(pip[1], 1); 68947112Sbostic close(pip[1]); 69047112Sbostic } 69147112Sbostic } 69247112Sbostic flags |= EV_EXIT; 69347112Sbostic } 69447112Sbostic 69547112Sbostic /* This is the child process if a fork occurred. */ 69647112Sbostic /* Execute the command. */ 69747112Sbostic if (cmdentry.cmdtype == CMDFUNCTION) { 69847112Sbostic trputs("Shell function: "); trargs(argv); 69947112Sbostic redirect(cmd->ncmd.redirect, REDIR_PUSH); 70047112Sbostic saveparam = shellparam; 70147112Sbostic shellparam.malloc = 0; 70247112Sbostic shellparam.nparam = argc - 1; 70347112Sbostic shellparam.p = argv + 1; 70447112Sbostic shellparam.optnext = NULL; 70547112Sbostic INTOFF; 70647112Sbostic savelocalvars = localvars; 70747112Sbostic localvars = NULL; 70847112Sbostic INTON; 70947112Sbostic if (setjmp(jmploc.loc)) { 71047112Sbostic if (exception == EXSHELLPROC) 71147112Sbostic freeparam((struct shparam *)&saveparam); 71247112Sbostic else { 71347112Sbostic freeparam(&shellparam); 71447112Sbostic shellparam = saveparam; 71547112Sbostic } 71647112Sbostic poplocalvars(); 71747112Sbostic localvars = savelocalvars; 71847112Sbostic handler = savehandler; 71947112Sbostic longjmp(handler->loc, 1); 72047112Sbostic } 72147112Sbostic savehandler = handler; 72247112Sbostic handler = &jmploc; 72347112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 72447112Sbostic mklocal(sp->text); 72547112Sbostic funcnest++; 72647112Sbostic evaltree(cmdentry.u.func, 0); 72747112Sbostic funcnest--; 72847112Sbostic INTOFF; 72947112Sbostic poplocalvars(); 73047112Sbostic localvars = savelocalvars; 73147112Sbostic freeparam(&shellparam); 73247112Sbostic shellparam = saveparam; 73347112Sbostic handler = savehandler; 73447112Sbostic popredir(); 73547112Sbostic INTON; 73647112Sbostic if (evalskip == SKIPFUNC) { 73747112Sbostic evalskip = 0; 73847112Sbostic skipcount = 0; 73947112Sbostic } 74047112Sbostic if (flags & EV_EXIT) 74147112Sbostic exitshell(exitstatus); 74247112Sbostic } else if (cmdentry.cmdtype == CMDBUILTIN) { 74347112Sbostic trputs("builtin command: "); trargs(argv); 74447112Sbostic mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 74547112Sbostic if (flags == EV_BACKCMD) { 74647112Sbostic memout.nleft = 0; 74747112Sbostic memout.nextc = memout.buf; 74847112Sbostic memout.bufsize = 64; 74947112Sbostic mode |= REDIR_BACKQ; 75047112Sbostic } 75147112Sbostic redirect(cmd->ncmd.redirect, mode); 75247112Sbostic savecmdname = commandname; 75347112Sbostic cmdenviron = varlist.list; 75447112Sbostic e = -1; 75547112Sbostic if (setjmp(jmploc.loc)) { 75647112Sbostic e = exception; 75747112Sbostic exitstatus = (e == EXINT)? SIGINT+128 : 2; 75847112Sbostic goto cmddone; 75947112Sbostic } 76047112Sbostic savehandler = handler; 76147112Sbostic handler = &jmploc; 76247112Sbostic commandname = argv[0]; 76347112Sbostic argptr = argv + 1; 76447112Sbostic optptr = NULL; /* initialize nextopt */ 76547112Sbostic exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 76647112Sbostic flushall(); 76747112Sbostic cmddone: 76847112Sbostic out1 = &output; 76947112Sbostic out2 = &errout; 77047112Sbostic freestdout(); 77147112Sbostic if (e != EXSHELLPROC) { 77247112Sbostic commandname = savecmdname; 77347112Sbostic if (flags & EV_EXIT) { 77447112Sbostic exitshell(exitstatus); 77547112Sbostic } 77647112Sbostic } 77747112Sbostic handler = savehandler; 77847112Sbostic if (e != -1) { 77947112Sbostic if (e != EXERROR || cmdentry.u.index == BLTINCMD 78047112Sbostic || cmdentry.u.index == DOTCMD 78147112Sbostic || cmdentry.u.index == EVALCMD 78247112Sbostic || cmdentry.u.index == EXECCMD) 78347112Sbostic exraise(e); 78447112Sbostic FORCEINTON; 78547112Sbostic } 78647112Sbostic if (cmdentry.u.index != EXECCMD) 78747112Sbostic popredir(); 78847112Sbostic if (flags == EV_BACKCMD) { 78947112Sbostic backcmd->buf = memout.buf; 79047112Sbostic backcmd->nleft = memout.nextc - memout.buf; 79147112Sbostic memout.buf = NULL; 79247112Sbostic } 79347112Sbostic } else { 79447112Sbostic trputs("normal command: "); trargs(argv); 79547112Sbostic clearredir(); 79647112Sbostic redirect(cmd->ncmd.redirect, 0); 79747112Sbostic if (varlist.list) { 79847112Sbostic p = stalloc(strlen(pathval()) + 1); 79947112Sbostic scopy(pathval(), p); 80047112Sbostic } else { 80147112Sbostic p = pathval(); 80247112Sbostic } 80347112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 80447112Sbostic setvareq(sp->text, VEXPORT|VSTACK); 80547112Sbostic envp = environment(); 80647112Sbostic shellexec(argv, envp, p, cmdentry.u.index); 80747112Sbostic /*NOTREACHED*/ 80847112Sbostic } 80947112Sbostic goto out; 81047112Sbostic 81147112Sbostic parent: /* parent process gets here (if we forked) */ 81247112Sbostic if (mode == 0) { /* argument to fork */ 81347112Sbostic INTOFF; 81447112Sbostic exitstatus = waitforjob(jp); 81547112Sbostic INTON; 81647112Sbostic } else if (mode == 2) { 81747112Sbostic backcmd->fd = pip[0]; 81847112Sbostic close(pip[1]); 81947112Sbostic backcmd->jp = jp; 82047112Sbostic } 82147112Sbostic 82247112Sbostic out: 82347112Sbostic if (lastarg) 82447112Sbostic setvar("_", lastarg, 0); 82547112Sbostic popstackmark(&smark); 82647112Sbostic } 82747112Sbostic 82847112Sbostic 82947112Sbostic 83047112Sbostic /* 83147112Sbostic * Search for a command. This is called before we fork so that the 83247112Sbostic * location of the command will be available in the parent as well as 83347112Sbostic * the child. The check for "goodname" is an overly conservative 83447112Sbostic * check that the name will not be subject to expansion. 83547112Sbostic */ 83647112Sbostic 83747112Sbostic STATIC void 83847112Sbostic prehash(n) 83947112Sbostic union node *n; 84047112Sbostic { 84147112Sbostic struct cmdentry entry; 84247112Sbostic 84347112Sbostic if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) 84447112Sbostic find_command(n->ncmd.args->narg.text, &entry, 0); 84547112Sbostic } 84647112Sbostic 84747112Sbostic 84847112Sbostic 84947112Sbostic /* 85047112Sbostic * Builtin commands. Builtin commands whose functions are closely 85147112Sbostic * tied to evaluation are implemented here. 85247112Sbostic */ 85347112Sbostic 85447112Sbostic /* 85547112Sbostic * No command given, or a bltin command with no arguments. Set the 85647112Sbostic * specified variables. 85747112Sbostic */ 85847112Sbostic 85947112Sbostic bltincmd(argc, argv) char **argv; { 86047112Sbostic listsetvar(cmdenviron); 86147112Sbostic return exitstatus; 86247112Sbostic } 86347112Sbostic 86447112Sbostic 86547112Sbostic /* 86647112Sbostic * Handle break and continue commands. Break, continue, and return are 86747112Sbostic * all handled by setting the evalskip flag. The evaluation routines 86847112Sbostic * above all check this flag, and if it is set they start skipping 86947112Sbostic * commands rather than executing them. The variable skipcount is 87047112Sbostic * the number of loops to break/continue, or the number of function 87147112Sbostic * levels to return. (The latter is always 1.) It should probably 87247112Sbostic * be an error to break out of more loops than exist, but it isn't 87347112Sbostic * in the standard shell so we don't make it one here. 87447112Sbostic */ 87547112Sbostic 87647112Sbostic breakcmd(argc, argv) char **argv; { 87747112Sbostic int n; 87847112Sbostic 87947112Sbostic n = 1; 88047112Sbostic if (argc > 1) 88147112Sbostic n = number(argv[1]); 88247112Sbostic if (n > loopnest) 88347112Sbostic n = loopnest; 88447112Sbostic if (n > 0) { 88547112Sbostic evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 88647112Sbostic skipcount = n; 88747112Sbostic } 88847112Sbostic return 0; 88947112Sbostic } 89047112Sbostic 89147112Sbostic 89247112Sbostic /* 89347112Sbostic * The return command. 89447112Sbostic */ 89547112Sbostic 89647112Sbostic returncmd(argc, argv) char **argv; { 89747112Sbostic int ret; 89847112Sbostic 89947112Sbostic ret = exitstatus; 90047112Sbostic if (argc > 1) 90147112Sbostic ret = number(argv[1]); 90247112Sbostic if (funcnest) { 90347112Sbostic evalskip = SKIPFUNC; 90447112Sbostic skipcount = 1; 90547112Sbostic } 90647112Sbostic return ret; 90747112Sbostic } 90847112Sbostic 90947112Sbostic 91047112Sbostic truecmd(argc, argv) char **argv; { 91147112Sbostic return 0; 91247112Sbostic } 91347112Sbostic 91447112Sbostic 91547112Sbostic execcmd(argc, argv) char **argv; { 91647112Sbostic if (argc > 1) { 91747112Sbostic iflag = 0; /* exit on error */ 91847112Sbostic setinteractive(0); 91947112Sbostic #if JOBS 92047112Sbostic jflag = 0; 92147112Sbostic setjobctl(0); 92247112Sbostic #endif 92347112Sbostic shellexec(argv + 1, environment(), pathval(), 0); 92447112Sbostic 92547112Sbostic } 92647112Sbostic return 0; 92747112Sbostic } 928