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 * 847112Sbostic * %sccs.include.redist.c% 947112Sbostic */ 1047112Sbostic 1147112Sbostic #ifndef lint 12*53175Smarc static char sccsid[] = "@(#)eval.c 5.4 (Berkeley) 04/16/92"; 1347112Sbostic #endif /* not lint */ 1447112Sbostic 1547112Sbostic /* 1647112Sbostic * Evaluate a command. 1747112Sbostic */ 1847112Sbostic 1947112Sbostic #include "shell.h" 2047112Sbostic #include "nodes.h" 2147112Sbostic #include "syntax.h" 2247112Sbostic #include "expand.h" 2347112Sbostic #include "parser.h" 2447112Sbostic #include "jobs.h" 2547112Sbostic #include "eval.h" 2647112Sbostic #include "builtins.h" 2747112Sbostic #include "options.h" 2847112Sbostic #include "exec.h" 2947112Sbostic #include "redir.h" 3047112Sbostic #include "input.h" 3147112Sbostic #include "output.h" 3247112Sbostic #include "trap.h" 3347112Sbostic #include "var.h" 3447112Sbostic #include "memalloc.h" 3547112Sbostic #include "error.h" 3647112Sbostic #include "mystring.h" 3747112Sbostic #include <signal.h> 3847112Sbostic 3947112Sbostic 4047112Sbostic /* flags in argument to evaltree */ 4147112Sbostic #define EV_EXIT 01 /* exit after evaluating tree */ 4247112Sbostic #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ 4347112Sbostic #define EV_BACKCMD 04 /* command executing within back quotes */ 4447112Sbostic 4547112Sbostic 4647112Sbostic /* reasons for skipping commands (see comment on breakcmd routine) */ 4747112Sbostic #define SKIPBREAK 1 4847112Sbostic #define SKIPCONT 2 4947112Sbostic #define SKIPFUNC 3 5047112Sbostic 5147112Sbostic MKINIT int evalskip; /* set if we are skipping commands */ 5247112Sbostic STATIC int skipcount; /* number of levels to skip */ 5347112Sbostic MKINIT int loopnest; /* current loop nesting level */ 5447112Sbostic int funcnest; /* depth of function calls */ 5547112Sbostic 5647112Sbostic 5747112Sbostic char *commandname; 5847112Sbostic struct strlist *cmdenviron; 5947112Sbostic int exitstatus; /* exit status of last command */ 6047112Sbostic 6147112Sbostic 6247112Sbostic #ifdef __STDC__ 6347112Sbostic STATIC void evalloop(union node *); 6447112Sbostic STATIC void evalfor(union node *); 6547112Sbostic STATIC void evalcase(union node *, int); 6647112Sbostic STATIC void evalsubshell(union node *, int); 6747112Sbostic STATIC void expredir(union node *); 6847112Sbostic STATIC void evalpipe(union node *); 6947112Sbostic STATIC void evalcommand(union node *, int, struct backcmd *); 7047112Sbostic STATIC void prehash(union node *); 7147112Sbostic #else 7247112Sbostic STATIC void evalloop(); 7347112Sbostic STATIC void evalfor(); 7447112Sbostic STATIC void evalcase(); 7547112Sbostic STATIC void evalsubshell(); 7647112Sbostic STATIC void expredir(); 7747112Sbostic STATIC void evalpipe(); 7847112Sbostic STATIC void evalcommand(); 7947112Sbostic STATIC void prehash(); 8047112Sbostic #endif 8147112Sbostic 8247112Sbostic 8347112Sbostic 8447112Sbostic /* 8547112Sbostic * Called to reset things after an exception. 8647112Sbostic */ 8747112Sbostic 8847112Sbostic #ifdef mkinit 8947112Sbostic INCLUDE "eval.h" 9047112Sbostic 9147112Sbostic RESET { 9247112Sbostic evalskip = 0; 9347112Sbostic loopnest = 0; 9447112Sbostic funcnest = 0; 9547112Sbostic } 9647112Sbostic 9747112Sbostic SHELLPROC { 9847112Sbostic exitstatus = 0; 9947112Sbostic } 10047112Sbostic #endif 10147112Sbostic 10247112Sbostic 10347112Sbostic 10447112Sbostic /* 10547292Smarc * The eval commmand. 10647112Sbostic */ 10747112Sbostic 10847292Smarc evalcmd(argc, argv) 10947292Smarc char **argv; 11047292Smarc { 11147292Smarc char *p; 11247292Smarc char *concat; 11347292Smarc char **ap; 11447112Sbostic 11547292Smarc if (argc > 1) { 11647292Smarc p = argv[1]; 11747292Smarc if (argc > 2) { 11847292Smarc STARTSTACKSTR(concat); 11947292Smarc ap = argv + 2; 12047292Smarc for (;;) { 12147292Smarc while (*p) 12247292Smarc STPUTC(*p++, concat); 12347292Smarc if ((p = *ap++) == NULL) 12447292Smarc break; 12547292Smarc STPUTC(' ', concat); 12647292Smarc } 12747292Smarc STPUTC('\0', concat); 12847292Smarc p = grabstackstr(concat); 12947292Smarc } 13047292Smarc evalstring(p); 13147292Smarc } 13247292Smarc return exitstatus; 13347112Sbostic } 13447112Sbostic 13547112Sbostic 13647112Sbostic /* 13747112Sbostic * Execute a command or commands contained in a string. 13847112Sbostic */ 13947112Sbostic 14047112Sbostic void 14147112Sbostic evalstring(s) 14247112Sbostic char *s; 14347112Sbostic { 14447112Sbostic union node *n; 14547112Sbostic struct stackmark smark; 14647112Sbostic 14747112Sbostic setstackmark(&smark); 14847112Sbostic setinputstring(s, 1); 14947112Sbostic while ((n = parsecmd(0)) != NEOF) { 15047112Sbostic evaltree(n, 0); 15147112Sbostic popstackmark(&smark); 15247112Sbostic } 15347112Sbostic popfile(); 15447112Sbostic popstackmark(&smark); 15547112Sbostic } 15647112Sbostic 15747112Sbostic 15847112Sbostic 15947112Sbostic /* 16047112Sbostic * Evaluate a parse tree. The value is left in the global variable 16147112Sbostic * exitstatus. 16247112Sbostic */ 16347112Sbostic 16447112Sbostic void 16547112Sbostic evaltree(n, flags) 16647112Sbostic union node *n; 16747112Sbostic { 16847112Sbostic if (n == NULL) { 16947112Sbostic TRACE(("evaltree(NULL) called\n")); 17047112Sbostic return; 17147112Sbostic } 17247112Sbostic TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type)); 17347112Sbostic switch (n->type) { 17447112Sbostic case NSEMI: 17547112Sbostic evaltree(n->nbinary.ch1, 0); 17647112Sbostic if (evalskip) 17747112Sbostic goto out; 17847112Sbostic evaltree(n->nbinary.ch2, flags); 17947112Sbostic break; 18047112Sbostic case NAND: 18147112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 18247112Sbostic if (evalskip || exitstatus != 0) 18347112Sbostic goto out; 18447112Sbostic evaltree(n->nbinary.ch2, flags); 18547112Sbostic break; 18647112Sbostic case NOR: 18747112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 18847112Sbostic if (evalskip || exitstatus == 0) 18947112Sbostic goto out; 19047112Sbostic evaltree(n->nbinary.ch2, flags); 19147112Sbostic break; 19247112Sbostic case NREDIR: 19347112Sbostic expredir(n->nredir.redirect); 19447112Sbostic redirect(n->nredir.redirect, REDIR_PUSH); 19547112Sbostic evaltree(n->nredir.n, flags); 19647112Sbostic popredir(); 19747112Sbostic break; 19847112Sbostic case NSUBSHELL: 19947112Sbostic evalsubshell(n, flags); 20047112Sbostic break; 20147112Sbostic case NBACKGND: 20247112Sbostic evalsubshell(n, flags); 20347112Sbostic break; 20447982Smarc case NIF: { 20547982Smarc int status = 0; 20647982Smarc 20747112Sbostic evaltree(n->nif.test, EV_TESTED); 20847112Sbostic if (evalskip) 20947112Sbostic goto out; 21047112Sbostic if (exitstatus == 0) { 21147112Sbostic evaltree(n->nif.ifpart, flags); 21247982Smarc status = exitstatus; 21347112Sbostic } else if (n->nif.elsepart) { 21447112Sbostic evaltree(n->nif.elsepart, flags); 21547982Smarc status = exitstatus; 21647112Sbostic } 21747982Smarc exitstatus = status; 21847112Sbostic break; 21947982Smarc } 22047112Sbostic case NWHILE: 22147112Sbostic case NUNTIL: 22247112Sbostic evalloop(n); 22347112Sbostic break; 22447112Sbostic case NFOR: 22547112Sbostic evalfor(n); 22647112Sbostic break; 22747112Sbostic case NCASE: 22847112Sbostic evalcase(n, flags); 22947112Sbostic break; 23047112Sbostic case NDEFUN: 23147112Sbostic defun(n->narg.text, n->narg.next); 23247112Sbostic exitstatus = 0; 23347112Sbostic break; 234*53175Smarc case NNOT: 235*53175Smarc evaltree(n->nnot.com, EV_TESTED); 236*53175Smarc exitstatus = !exitstatus; 237*53175Smarc break; 238*53175Smarc 23947112Sbostic case NPIPE: 24047112Sbostic evalpipe(n); 24147112Sbostic break; 24247112Sbostic case NCMD: 24347112Sbostic evalcommand(n, flags, (struct backcmd *)NULL); 24447112Sbostic break; 24547112Sbostic default: 24647112Sbostic out1fmt("Node type = %d\n", n->type); 24747112Sbostic flushout(&output); 24847112Sbostic break; 24947112Sbostic } 25047112Sbostic out: 25147112Sbostic if (pendingsigs) 25247112Sbostic dotrap(); 25347112Sbostic if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) 25447112Sbostic exitshell(exitstatus); 25547112Sbostic } 25647112Sbostic 25747112Sbostic 25847112Sbostic STATIC void 25947112Sbostic evalloop(n) 26047112Sbostic union node *n; 26147112Sbostic { 26247112Sbostic int status; 26347112Sbostic 26447112Sbostic loopnest++; 26547112Sbostic status = 0; 26647112Sbostic for (;;) { 26747112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 26847112Sbostic if (evalskip) { 26947112Sbostic skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 27047112Sbostic evalskip = 0; 27147112Sbostic continue; 27247112Sbostic } 27347112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 27447112Sbostic evalskip = 0; 27547112Sbostic break; 27647112Sbostic } 27747112Sbostic if (n->type == NWHILE) { 27847112Sbostic if (exitstatus != 0) 27947112Sbostic break; 28047112Sbostic } else { 28147112Sbostic if (exitstatus == 0) 28247112Sbostic break; 28347112Sbostic } 28447112Sbostic evaltree(n->nbinary.ch2, 0); 28547112Sbostic status = exitstatus; 28647112Sbostic if (evalskip) 28747112Sbostic goto skipping; 28847112Sbostic } 28947112Sbostic loopnest--; 29047112Sbostic exitstatus = status; 29147112Sbostic } 29247112Sbostic 29347112Sbostic 29447112Sbostic 29547112Sbostic STATIC void 29647112Sbostic evalfor(n) 29747112Sbostic union node *n; 29847112Sbostic { 29947112Sbostic struct arglist arglist; 30047112Sbostic union node *argp; 30147112Sbostic struct strlist *sp; 30247112Sbostic struct stackmark smark; 30347112Sbostic 30447112Sbostic setstackmark(&smark); 30547112Sbostic arglist.lastp = &arglist.list; 30647112Sbostic for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 30747112Sbostic expandarg(argp, &arglist, 1); 30847112Sbostic if (evalskip) 30947112Sbostic goto out; 31047112Sbostic } 31147112Sbostic *arglist.lastp = NULL; 31247112Sbostic 31347112Sbostic exitstatus = 0; 31447112Sbostic loopnest++; 31547112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 31647112Sbostic setvar(n->nfor.var, sp->text, 0); 31747112Sbostic evaltree(n->nfor.body, 0); 31847112Sbostic if (evalskip) { 31947112Sbostic if (evalskip == SKIPCONT && --skipcount <= 0) { 32047112Sbostic evalskip = 0; 32147112Sbostic continue; 32247112Sbostic } 32347112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 32447112Sbostic evalskip = 0; 32547112Sbostic break; 32647112Sbostic } 32747112Sbostic } 32847112Sbostic loopnest--; 32947112Sbostic out: 33047112Sbostic popstackmark(&smark); 33147112Sbostic } 33247112Sbostic 33347112Sbostic 33447112Sbostic 33547112Sbostic STATIC void 33647112Sbostic evalcase(n, flags) 33747112Sbostic union node *n; 33847112Sbostic { 33947112Sbostic union node *cp; 34047112Sbostic union node *patp; 34147112Sbostic struct arglist arglist; 34247112Sbostic struct stackmark smark; 34347112Sbostic 34447112Sbostic setstackmark(&smark); 34547112Sbostic arglist.lastp = &arglist.list; 34647112Sbostic expandarg(n->ncase.expr, &arglist, 0); 34747112Sbostic for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 34847112Sbostic for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 34947112Sbostic if (casematch(patp, arglist.list->text)) { 35047112Sbostic if (evalskip == 0) { 35147112Sbostic evaltree(cp->nclist.body, flags); 35247112Sbostic } 35347112Sbostic goto out; 35447112Sbostic } 35547112Sbostic } 35647112Sbostic } 35747112Sbostic out: 35847112Sbostic popstackmark(&smark); 35947112Sbostic } 36047112Sbostic 36147112Sbostic 36247112Sbostic 36347112Sbostic /* 36447112Sbostic * Kick off a subshell to evaluate a tree. 36547112Sbostic */ 36647112Sbostic 36747112Sbostic STATIC void 36847112Sbostic evalsubshell(n, flags) 36947112Sbostic union node *n; 37047112Sbostic { 37147112Sbostic struct job *jp; 37247112Sbostic int backgnd = (n->type == NBACKGND); 37347112Sbostic 37447112Sbostic expredir(n->nredir.redirect); 37547112Sbostic jp = makejob(n, 1); 37647112Sbostic if (forkshell(jp, n, backgnd) == 0) { 37747112Sbostic if (backgnd) 37847112Sbostic flags &=~ EV_TESTED; 37947112Sbostic redirect(n->nredir.redirect, 0); 38047112Sbostic evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 38147112Sbostic } 38247112Sbostic if (! backgnd) { 38347112Sbostic INTOFF; 38447112Sbostic exitstatus = waitforjob(jp); 38547112Sbostic INTON; 38647112Sbostic } 38747112Sbostic } 38847112Sbostic 38947112Sbostic 39047112Sbostic 39147112Sbostic /* 39247112Sbostic * Compute the names of the files in a redirection list. 39347112Sbostic */ 39447112Sbostic 39547112Sbostic STATIC void 39647112Sbostic expredir(n) 39747112Sbostic union node *n; 39847112Sbostic { 39947112Sbostic register union node *redir; 40047112Sbostic 40147112Sbostic for (redir = n ; redir ; redir = redir->nfile.next) { 40247112Sbostic if (redir->type == NFROM 40347112Sbostic || redir->type == NTO 40447112Sbostic || redir->type == NAPPEND) { 40547112Sbostic struct arglist fn; 40647112Sbostic fn.lastp = &fn.list; 40747112Sbostic expandarg(redir->nfile.fname, &fn, 0); 40847112Sbostic redir->nfile.expfname = fn.list->text; 40947112Sbostic } 41047112Sbostic } 41147112Sbostic } 41247112Sbostic 41347112Sbostic 41447112Sbostic 41547112Sbostic /* 41647112Sbostic * Evaluate a pipeline. All the processes in the pipeline are children 41747112Sbostic * of the process creating the pipeline. (This differs from some versions 41847112Sbostic * of the shell, which make the last process in a pipeline the parent 41947112Sbostic * of all the rest.) 42047112Sbostic */ 42147112Sbostic 42247112Sbostic STATIC void 42347112Sbostic evalpipe(n) 42447112Sbostic union node *n; 42547112Sbostic { 42647112Sbostic struct job *jp; 42747112Sbostic struct nodelist *lp; 42847112Sbostic int pipelen; 42947112Sbostic int prevfd; 43047112Sbostic int pip[2]; 43147112Sbostic 43247112Sbostic TRACE(("evalpipe(0x%x) called\n", (int)n)); 43347112Sbostic pipelen = 0; 43447112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 43547112Sbostic pipelen++; 43647112Sbostic INTOFF; 43747112Sbostic jp = makejob(n, pipelen); 43847112Sbostic prevfd = -1; 43947112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 44047112Sbostic prehash(lp->n); 44147112Sbostic pip[1] = -1; 44247112Sbostic if (lp->next) { 44347112Sbostic if (pipe(pip) < 0) { 44447112Sbostic close(prevfd); 44547112Sbostic error("Pipe call failed"); 44647112Sbostic } 44747112Sbostic } 44847112Sbostic if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 44947112Sbostic INTON; 45047112Sbostic if (prevfd > 0) { 45147112Sbostic close(0); 45247112Sbostic copyfd(prevfd, 0); 45347112Sbostic close(prevfd); 45447112Sbostic } 45547112Sbostic if (pip[1] >= 0) { 45647112Sbostic close(pip[0]); 45747112Sbostic if (pip[1] != 1) { 45847112Sbostic close(1); 45947112Sbostic copyfd(pip[1], 1); 46047112Sbostic close(pip[1]); 46147112Sbostic } 46247112Sbostic } 46347112Sbostic evaltree(lp->n, EV_EXIT); 46447112Sbostic } 46547112Sbostic if (prevfd >= 0) 46647112Sbostic close(prevfd); 46747112Sbostic prevfd = pip[0]; 46847112Sbostic close(pip[1]); 46947112Sbostic } 47047112Sbostic INTON; 47147112Sbostic if (n->npipe.backgnd == 0) { 47247112Sbostic INTOFF; 47347112Sbostic exitstatus = waitforjob(jp); 47447112Sbostic TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 47547112Sbostic INTON; 47647112Sbostic } 47747112Sbostic } 47847112Sbostic 47947112Sbostic 48047112Sbostic 48147112Sbostic /* 48247112Sbostic * Execute a command inside back quotes. If it's a builtin command, we 48347112Sbostic * want to save its output in a block obtained from malloc. Otherwise 48447112Sbostic * we fork off a subprocess and get the output of the command via a pipe. 48547112Sbostic * Should be called with interrupts off. 48647112Sbostic */ 48747112Sbostic 48847112Sbostic void 48947112Sbostic evalbackcmd(n, result) 49047112Sbostic union node *n; 49147112Sbostic struct backcmd *result; 49247112Sbostic { 49347112Sbostic int pip[2]; 49447112Sbostic struct job *jp; 49547112Sbostic struct stackmark smark; /* unnecessary */ 49647112Sbostic 49747112Sbostic setstackmark(&smark); 49847112Sbostic result->fd = -1; 49947112Sbostic result->buf = NULL; 50047112Sbostic result->nleft = 0; 50147112Sbostic result->jp = NULL; 50247112Sbostic if (n->type == NCMD) { 50347112Sbostic evalcommand(n, EV_BACKCMD, result); 50447112Sbostic } else { 50547112Sbostic if (pipe(pip) < 0) 50647112Sbostic error("Pipe call failed"); 50747112Sbostic jp = makejob(n, 1); 50847112Sbostic if (forkshell(jp, n, FORK_NOJOB) == 0) { 50947112Sbostic FORCEINTON; 51047112Sbostic close(pip[0]); 51147112Sbostic if (pip[1] != 1) { 51247112Sbostic close(1); 51347112Sbostic copyfd(pip[1], 1); 51447112Sbostic close(pip[1]); 51547112Sbostic } 51647112Sbostic evaltree(n, EV_EXIT); 51747112Sbostic } 51847112Sbostic close(pip[1]); 51947112Sbostic result->fd = pip[0]; 52047112Sbostic result->jp = jp; 52147112Sbostic } 52247112Sbostic popstackmark(&smark); 52347112Sbostic TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 52447112Sbostic result->fd, result->buf, result->nleft, result->jp)); 52547112Sbostic } 52647112Sbostic 52747112Sbostic 52847112Sbostic 52947112Sbostic /* 53047112Sbostic * Execute a simple command. 53147112Sbostic */ 53247112Sbostic 53347112Sbostic STATIC void 53447112Sbostic evalcommand(cmd, flags, backcmd) 53547112Sbostic union node *cmd; 53647112Sbostic struct backcmd *backcmd; 53747112Sbostic { 53847112Sbostic struct stackmark smark; 53947112Sbostic union node *argp; 54047112Sbostic struct arglist arglist; 54147112Sbostic struct arglist varlist; 54247112Sbostic char **argv; 54347112Sbostic int argc; 54447112Sbostic char **envp; 54547112Sbostic int varflag; 54647112Sbostic struct strlist *sp; 54747112Sbostic register char *p; 54847112Sbostic int mode; 54947112Sbostic int pip[2]; 55047112Sbostic struct cmdentry cmdentry; 55147112Sbostic struct job *jp; 55247112Sbostic struct jmploc jmploc; 55347112Sbostic struct jmploc *volatile savehandler; 55447112Sbostic char *volatile savecmdname; 55547112Sbostic volatile struct shparam saveparam; 55647112Sbostic struct localvar *volatile savelocalvars; 55747112Sbostic volatile int e; 55847112Sbostic char *lastarg; 55947112Sbostic 56047112Sbostic /* First expand the arguments. */ 56147112Sbostic TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); 56247112Sbostic setstackmark(&smark); 56347112Sbostic arglist.lastp = &arglist.list; 56447112Sbostic varlist.lastp = &varlist.list; 56547112Sbostic varflag = 1; 56647112Sbostic for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 56747112Sbostic p = argp->narg.text; 56847112Sbostic if (varflag && is_name(*p)) { 56947112Sbostic do { 57047112Sbostic p++; 57147112Sbostic } while (is_in_name(*p)); 57247112Sbostic if (*p == '=') { 57347112Sbostic expandarg(argp, &varlist, 0); 57447112Sbostic continue; 57547112Sbostic } 57647112Sbostic } 57747112Sbostic expandarg(argp, &arglist, 1); 57847112Sbostic varflag = 0; 57947112Sbostic } 58047112Sbostic *arglist.lastp = NULL; 58147112Sbostic *varlist.lastp = NULL; 58247112Sbostic expredir(cmd->ncmd.redirect); 58347112Sbostic argc = 0; 58447112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) 58547112Sbostic argc++; 58647112Sbostic argv = stalloc(sizeof (char *) * (argc + 1)); 58747112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) 58847112Sbostic *argv++ = sp->text; 58947112Sbostic *argv = NULL; 59047112Sbostic lastarg = NULL; 59147112Sbostic if (iflag && funcnest == 0 && argc > 0) 59247112Sbostic lastarg = argv[-1]; 59347112Sbostic argv -= argc; 59447112Sbostic 59547112Sbostic /* Print the command if xflag is set. */ 59647112Sbostic if (xflag) { 59747112Sbostic outc('+', &errout); 59847112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) { 59947112Sbostic outc(' ', &errout); 60047112Sbostic out2str(sp->text); 60147112Sbostic } 60247112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 60347112Sbostic outc(' ', &errout); 60447112Sbostic out2str(sp->text); 60547112Sbostic } 60647112Sbostic outc('\n', &errout); 60747112Sbostic flushout(&errout); 60847112Sbostic } 60947112Sbostic 61047112Sbostic /* Now locate the command. */ 61147112Sbostic if (argc == 0) { 61247112Sbostic cmdentry.cmdtype = CMDBUILTIN; 61347112Sbostic cmdentry.u.index = BLTINCMD; 61447112Sbostic } else { 61547112Sbostic find_command(argv[0], &cmdentry, 1); 61647112Sbostic if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ 61747112Sbostic exitstatus = 2; 61847112Sbostic flushout(&errout); 61947112Sbostic return; 62047112Sbostic } 62147112Sbostic /* implement the bltin builtin here */ 62247112Sbostic if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { 62347112Sbostic for (;;) { 62447112Sbostic argv++; 62547112Sbostic if (--argc == 0) 62647112Sbostic break; 62747112Sbostic if ((cmdentry.u.index = find_builtin(*argv)) < 0) { 62847112Sbostic outfmt(&errout, "%s: not found\n", *argv); 62947112Sbostic exitstatus = 2; 63047112Sbostic flushout(&errout); 63147112Sbostic return; 63247112Sbostic } 63347112Sbostic if (cmdentry.u.index != BLTINCMD) 63447112Sbostic break; 63547112Sbostic } 63647112Sbostic } 63747112Sbostic } 63847112Sbostic 63947112Sbostic /* Fork off a child process if necessary. */ 64047112Sbostic if (cmd->ncmd.backgnd 64147112Sbostic || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 64247112Sbostic || (flags & EV_BACKCMD) != 0 64347112Sbostic && (cmdentry.cmdtype != CMDBUILTIN 64447112Sbostic || cmdentry.u.index == DOTCMD 64547112Sbostic || cmdentry.u.index == EVALCMD)) { 64647112Sbostic jp = makejob(cmd, 1); 64747112Sbostic mode = cmd->ncmd.backgnd; 64847112Sbostic if (flags & EV_BACKCMD) { 64947112Sbostic mode = FORK_NOJOB; 65047112Sbostic if (pipe(pip) < 0) 65147112Sbostic error("Pipe call failed"); 65247112Sbostic } 65347112Sbostic if (forkshell(jp, cmd, mode) != 0) 65447112Sbostic goto parent; /* at end of routine */ 65547112Sbostic if (flags & EV_BACKCMD) { 65647112Sbostic FORCEINTON; 65747112Sbostic close(pip[0]); 65847112Sbostic if (pip[1] != 1) { 65947112Sbostic close(1); 66047112Sbostic copyfd(pip[1], 1); 66147112Sbostic close(pip[1]); 66247112Sbostic } 66347112Sbostic } 66447112Sbostic flags |= EV_EXIT; 66547112Sbostic } 66647112Sbostic 66747112Sbostic /* This is the child process if a fork occurred. */ 66847112Sbostic /* Execute the command. */ 66947112Sbostic if (cmdentry.cmdtype == CMDFUNCTION) { 67047112Sbostic trputs("Shell function: "); trargs(argv); 67147112Sbostic redirect(cmd->ncmd.redirect, REDIR_PUSH); 67247112Sbostic saveparam = shellparam; 67347112Sbostic shellparam.malloc = 0; 67447112Sbostic shellparam.nparam = argc - 1; 67547112Sbostic shellparam.p = argv + 1; 67647112Sbostic shellparam.optnext = NULL; 67747112Sbostic INTOFF; 67847112Sbostic savelocalvars = localvars; 67947112Sbostic localvars = NULL; 68047112Sbostic INTON; 68147112Sbostic if (setjmp(jmploc.loc)) { 68247112Sbostic if (exception == EXSHELLPROC) 68347112Sbostic freeparam((struct shparam *)&saveparam); 68447112Sbostic else { 68547112Sbostic freeparam(&shellparam); 68647112Sbostic shellparam = saveparam; 68747112Sbostic } 68847112Sbostic poplocalvars(); 68947112Sbostic localvars = savelocalvars; 69047112Sbostic handler = savehandler; 69147112Sbostic longjmp(handler->loc, 1); 69247112Sbostic } 69347112Sbostic savehandler = handler; 69447112Sbostic handler = &jmploc; 69547112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 69647112Sbostic mklocal(sp->text); 69747112Sbostic funcnest++; 69847112Sbostic evaltree(cmdentry.u.func, 0); 69947112Sbostic funcnest--; 70047112Sbostic INTOFF; 70147112Sbostic poplocalvars(); 70247112Sbostic localvars = savelocalvars; 70347112Sbostic freeparam(&shellparam); 70447112Sbostic shellparam = saveparam; 70547112Sbostic handler = savehandler; 70647112Sbostic popredir(); 70747112Sbostic INTON; 70847112Sbostic if (evalskip == SKIPFUNC) { 70947112Sbostic evalskip = 0; 71047112Sbostic skipcount = 0; 71147112Sbostic } 71247112Sbostic if (flags & EV_EXIT) 71347112Sbostic exitshell(exitstatus); 71447112Sbostic } else if (cmdentry.cmdtype == CMDBUILTIN) { 71547112Sbostic trputs("builtin command: "); trargs(argv); 71647112Sbostic mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 71747112Sbostic if (flags == EV_BACKCMD) { 71847112Sbostic memout.nleft = 0; 71947112Sbostic memout.nextc = memout.buf; 72047112Sbostic memout.bufsize = 64; 72147112Sbostic mode |= REDIR_BACKQ; 72247112Sbostic } 72347112Sbostic redirect(cmd->ncmd.redirect, mode); 72447112Sbostic savecmdname = commandname; 72547112Sbostic cmdenviron = varlist.list; 72647112Sbostic e = -1; 72747112Sbostic if (setjmp(jmploc.loc)) { 72847112Sbostic e = exception; 72947112Sbostic exitstatus = (e == EXINT)? SIGINT+128 : 2; 73047112Sbostic goto cmddone; 73147112Sbostic } 73247112Sbostic savehandler = handler; 73347112Sbostic handler = &jmploc; 73447112Sbostic commandname = argv[0]; 73547112Sbostic argptr = argv + 1; 73647112Sbostic optptr = NULL; /* initialize nextopt */ 73747112Sbostic exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 73847112Sbostic flushall(); 73947112Sbostic cmddone: 74047112Sbostic out1 = &output; 74147112Sbostic out2 = &errout; 74247112Sbostic freestdout(); 74347112Sbostic if (e != EXSHELLPROC) { 74447112Sbostic commandname = savecmdname; 74547112Sbostic if (flags & EV_EXIT) { 74647112Sbostic exitshell(exitstatus); 74747112Sbostic } 74847112Sbostic } 74947112Sbostic handler = savehandler; 75047112Sbostic if (e != -1) { 75147112Sbostic if (e != EXERROR || cmdentry.u.index == BLTINCMD 75247112Sbostic || cmdentry.u.index == DOTCMD 75347112Sbostic || cmdentry.u.index == EVALCMD 75447112Sbostic || cmdentry.u.index == EXECCMD) 75547112Sbostic exraise(e); 75647112Sbostic FORCEINTON; 75747112Sbostic } 75847112Sbostic if (cmdentry.u.index != EXECCMD) 75947112Sbostic popredir(); 76047112Sbostic if (flags == EV_BACKCMD) { 76147112Sbostic backcmd->buf = memout.buf; 76247112Sbostic backcmd->nleft = memout.nextc - memout.buf; 76347112Sbostic memout.buf = NULL; 76447112Sbostic } 76547112Sbostic } else { 76647112Sbostic trputs("normal command: "); trargs(argv); 76747112Sbostic clearredir(); 76847112Sbostic redirect(cmd->ncmd.redirect, 0); 76947112Sbostic if (varlist.list) { 77047112Sbostic p = stalloc(strlen(pathval()) + 1); 77147112Sbostic scopy(pathval(), p); 77247112Sbostic } else { 77347112Sbostic p = pathval(); 77447112Sbostic } 77547112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 77647112Sbostic setvareq(sp->text, VEXPORT|VSTACK); 77747112Sbostic envp = environment(); 77847112Sbostic shellexec(argv, envp, p, cmdentry.u.index); 77947112Sbostic /*NOTREACHED*/ 78047112Sbostic } 78147112Sbostic goto out; 78247112Sbostic 78347112Sbostic parent: /* parent process gets here (if we forked) */ 78447112Sbostic if (mode == 0) { /* argument to fork */ 78547112Sbostic INTOFF; 78647112Sbostic exitstatus = waitforjob(jp); 78747112Sbostic INTON; 78847112Sbostic } else if (mode == 2) { 78947112Sbostic backcmd->fd = pip[0]; 79047112Sbostic close(pip[1]); 79147112Sbostic backcmd->jp = jp; 79247112Sbostic } 79347112Sbostic 79447112Sbostic out: 79547112Sbostic if (lastarg) 79647112Sbostic setvar("_", lastarg, 0); 79747112Sbostic popstackmark(&smark); 79847112Sbostic } 79947112Sbostic 80047112Sbostic 80147112Sbostic 80247112Sbostic /* 80347112Sbostic * Search for a command. This is called before we fork so that the 80447112Sbostic * location of the command will be available in the parent as well as 80547112Sbostic * the child. The check for "goodname" is an overly conservative 80647112Sbostic * check that the name will not be subject to expansion. 80747112Sbostic */ 80847112Sbostic 80947112Sbostic STATIC void 81047112Sbostic prehash(n) 81147112Sbostic union node *n; 81247112Sbostic { 81347112Sbostic struct cmdentry entry; 81447112Sbostic 81547112Sbostic if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) 81647112Sbostic find_command(n->ncmd.args->narg.text, &entry, 0); 81747112Sbostic } 81847112Sbostic 81947112Sbostic 82047112Sbostic 82147112Sbostic /* 82247112Sbostic * Builtin commands. Builtin commands whose functions are closely 82347112Sbostic * tied to evaluation are implemented here. 82447112Sbostic */ 82547112Sbostic 82647112Sbostic /* 82747112Sbostic * No command given, or a bltin command with no arguments. Set the 82847112Sbostic * specified variables. 82947112Sbostic */ 83047112Sbostic 83147112Sbostic bltincmd(argc, argv) char **argv; { 83247112Sbostic listsetvar(cmdenviron); 83347112Sbostic return exitstatus; 83447112Sbostic } 83547112Sbostic 83647112Sbostic 83747112Sbostic /* 83847112Sbostic * Handle break and continue commands. Break, continue, and return are 83947112Sbostic * all handled by setting the evalskip flag. The evaluation routines 84047112Sbostic * above all check this flag, and if it is set they start skipping 84147112Sbostic * commands rather than executing them. The variable skipcount is 84247112Sbostic * the number of loops to break/continue, or the number of function 84347112Sbostic * levels to return. (The latter is always 1.) It should probably 84447112Sbostic * be an error to break out of more loops than exist, but it isn't 84547112Sbostic * in the standard shell so we don't make it one here. 84647112Sbostic */ 84747112Sbostic 84847112Sbostic breakcmd(argc, argv) char **argv; { 84947112Sbostic int n; 85047112Sbostic 85147112Sbostic n = 1; 85247112Sbostic if (argc > 1) 85347112Sbostic n = number(argv[1]); 85447112Sbostic if (n > loopnest) 85547112Sbostic n = loopnest; 85647112Sbostic if (n > 0) { 85747112Sbostic evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 85847112Sbostic skipcount = n; 85947112Sbostic } 86047112Sbostic return 0; 86147112Sbostic } 86247112Sbostic 86347112Sbostic 86447112Sbostic /* 86547112Sbostic * The return command. 86647112Sbostic */ 86747112Sbostic 86847112Sbostic returncmd(argc, argv) char **argv; { 86947112Sbostic int ret; 87047112Sbostic 87147112Sbostic ret = exitstatus; 87247112Sbostic if (argc > 1) 87347112Sbostic ret = number(argv[1]); 87447112Sbostic if (funcnest) { 87547112Sbostic evalskip = SKIPFUNC; 87647112Sbostic skipcount = 1; 87747112Sbostic } 87847112Sbostic return ret; 87947112Sbostic } 88047112Sbostic 88147112Sbostic 88247112Sbostic truecmd(argc, argv) char **argv; { 88347112Sbostic return 0; 88447112Sbostic } 88547112Sbostic 88647112Sbostic 88747112Sbostic execcmd(argc, argv) char **argv; { 88847112Sbostic if (argc > 1) { 88947112Sbostic iflag = 0; /* exit on error */ 89047112Sbostic setinteractive(0); 89147112Sbostic #if JOBS 89247112Sbostic jflag = 0; 89347112Sbostic setjobctl(0); 89447112Sbostic #endif 89547112Sbostic shellexec(argv + 1, environment(), pathval(), 0); 89647112Sbostic 89747112Sbostic } 89847112Sbostic return 0; 89947112Sbostic } 900