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*47292Smarc static char sccsid[] = "@(#)eval.c 5.2 (Berkeley) 03/13/91"; 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 /* 105*47292Smarc * The eval commmand. 10647112Sbostic */ 10747112Sbostic 108*47292Smarc evalcmd(argc, argv) 109*47292Smarc char **argv; 110*47292Smarc { 111*47292Smarc char *p; 112*47292Smarc char *concat; 113*47292Smarc char **ap; 11447112Sbostic 115*47292Smarc if (argc > 1) { 116*47292Smarc p = argv[1]; 117*47292Smarc if (argc > 2) { 118*47292Smarc STARTSTACKSTR(concat); 119*47292Smarc ap = argv + 2; 120*47292Smarc for (;;) { 121*47292Smarc while (*p) 122*47292Smarc STPUTC(*p++, concat); 123*47292Smarc if ((p = *ap++) == NULL) 124*47292Smarc break; 125*47292Smarc STPUTC(' ', concat); 126*47292Smarc } 127*47292Smarc STPUTC('\0', concat); 128*47292Smarc p = grabstackstr(concat); 129*47292Smarc } 130*47292Smarc evalstring(p); 131*47292Smarc } 132*47292Smarc 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; 20447112Sbostic case NIF: 20547112Sbostic evaltree(n->nif.test, EV_TESTED); 20647112Sbostic if (evalskip) 20747112Sbostic goto out; 20847112Sbostic if (exitstatus == 0) { 20947112Sbostic evaltree(n->nif.ifpart, flags); 21047112Sbostic } else if (n->nif.elsepart) { 21147112Sbostic evaltree(n->nif.elsepart, flags); 21247112Sbostic } 21347112Sbostic break; 21447112Sbostic case NWHILE: 21547112Sbostic case NUNTIL: 21647112Sbostic evalloop(n); 21747112Sbostic break; 21847112Sbostic case NFOR: 21947112Sbostic evalfor(n); 22047112Sbostic break; 22147112Sbostic case NCASE: 22247112Sbostic evalcase(n, flags); 22347112Sbostic break; 22447112Sbostic case NDEFUN: 22547112Sbostic defun(n->narg.text, n->narg.next); 22647112Sbostic exitstatus = 0; 22747112Sbostic break; 22847112Sbostic case NPIPE: 22947112Sbostic evalpipe(n); 23047112Sbostic break; 23147112Sbostic case NCMD: 23247112Sbostic evalcommand(n, flags, (struct backcmd *)NULL); 23347112Sbostic break; 23447112Sbostic default: 23547112Sbostic out1fmt("Node type = %d\n", n->type); 23647112Sbostic flushout(&output); 23747112Sbostic break; 23847112Sbostic } 23947112Sbostic out: 24047112Sbostic if (pendingsigs) 24147112Sbostic dotrap(); 24247112Sbostic if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) 24347112Sbostic exitshell(exitstatus); 24447112Sbostic } 24547112Sbostic 24647112Sbostic 24747112Sbostic STATIC void 24847112Sbostic evalloop(n) 24947112Sbostic union node *n; 25047112Sbostic { 25147112Sbostic int status; 25247112Sbostic 25347112Sbostic loopnest++; 25447112Sbostic status = 0; 25547112Sbostic for (;;) { 25647112Sbostic evaltree(n->nbinary.ch1, EV_TESTED); 25747112Sbostic if (evalskip) { 25847112Sbostic skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 25947112Sbostic evalskip = 0; 26047112Sbostic continue; 26147112Sbostic } 26247112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 26347112Sbostic evalskip = 0; 26447112Sbostic break; 26547112Sbostic } 26647112Sbostic if (n->type == NWHILE) { 26747112Sbostic if (exitstatus != 0) 26847112Sbostic break; 26947112Sbostic } else { 27047112Sbostic if (exitstatus == 0) 27147112Sbostic break; 27247112Sbostic } 27347112Sbostic evaltree(n->nbinary.ch2, 0); 27447112Sbostic status = exitstatus; 27547112Sbostic if (evalskip) 27647112Sbostic goto skipping; 27747112Sbostic } 27847112Sbostic loopnest--; 27947112Sbostic exitstatus = status; 28047112Sbostic } 28147112Sbostic 28247112Sbostic 28347112Sbostic 28447112Sbostic STATIC void 28547112Sbostic evalfor(n) 28647112Sbostic union node *n; 28747112Sbostic { 28847112Sbostic struct arglist arglist; 28947112Sbostic union node *argp; 29047112Sbostic struct strlist *sp; 29147112Sbostic struct stackmark smark; 29247112Sbostic 29347112Sbostic setstackmark(&smark); 29447112Sbostic arglist.lastp = &arglist.list; 29547112Sbostic for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 29647112Sbostic expandarg(argp, &arglist, 1); 29747112Sbostic if (evalskip) 29847112Sbostic goto out; 29947112Sbostic } 30047112Sbostic *arglist.lastp = NULL; 30147112Sbostic 30247112Sbostic exitstatus = 0; 30347112Sbostic loopnest++; 30447112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 30547112Sbostic setvar(n->nfor.var, sp->text, 0); 30647112Sbostic evaltree(n->nfor.body, 0); 30747112Sbostic if (evalskip) { 30847112Sbostic if (evalskip == SKIPCONT && --skipcount <= 0) { 30947112Sbostic evalskip = 0; 31047112Sbostic continue; 31147112Sbostic } 31247112Sbostic if (evalskip == SKIPBREAK && --skipcount <= 0) 31347112Sbostic evalskip = 0; 31447112Sbostic break; 31547112Sbostic } 31647112Sbostic } 31747112Sbostic loopnest--; 31847112Sbostic out: 31947112Sbostic popstackmark(&smark); 32047112Sbostic } 32147112Sbostic 32247112Sbostic 32347112Sbostic 32447112Sbostic STATIC void 32547112Sbostic evalcase(n, flags) 32647112Sbostic union node *n; 32747112Sbostic { 32847112Sbostic union node *cp; 32947112Sbostic union node *patp; 33047112Sbostic struct arglist arglist; 33147112Sbostic struct stackmark smark; 33247112Sbostic 33347112Sbostic setstackmark(&smark); 33447112Sbostic arglist.lastp = &arglist.list; 33547112Sbostic expandarg(n->ncase.expr, &arglist, 0); 33647112Sbostic for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 33747112Sbostic for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 33847112Sbostic if (casematch(patp, arglist.list->text)) { 33947112Sbostic if (evalskip == 0) { 34047112Sbostic evaltree(cp->nclist.body, flags); 34147112Sbostic } 34247112Sbostic goto out; 34347112Sbostic } 34447112Sbostic } 34547112Sbostic } 34647112Sbostic out: 34747112Sbostic popstackmark(&smark); 34847112Sbostic } 34947112Sbostic 35047112Sbostic 35147112Sbostic 35247112Sbostic /* 35347112Sbostic * Kick off a subshell to evaluate a tree. 35447112Sbostic */ 35547112Sbostic 35647112Sbostic STATIC void 35747112Sbostic evalsubshell(n, flags) 35847112Sbostic union node *n; 35947112Sbostic { 36047112Sbostic struct job *jp; 36147112Sbostic int backgnd = (n->type == NBACKGND); 36247112Sbostic 36347112Sbostic expredir(n->nredir.redirect); 36447112Sbostic jp = makejob(n, 1); 36547112Sbostic if (forkshell(jp, n, backgnd) == 0) { 36647112Sbostic if (backgnd) 36747112Sbostic flags &=~ EV_TESTED; 36847112Sbostic redirect(n->nredir.redirect, 0); 36947112Sbostic evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 37047112Sbostic } 37147112Sbostic if (! backgnd) { 37247112Sbostic INTOFF; 37347112Sbostic exitstatus = waitforjob(jp); 37447112Sbostic INTON; 37547112Sbostic } 37647112Sbostic } 37747112Sbostic 37847112Sbostic 37947112Sbostic 38047112Sbostic /* 38147112Sbostic * Compute the names of the files in a redirection list. 38247112Sbostic */ 38347112Sbostic 38447112Sbostic STATIC void 38547112Sbostic expredir(n) 38647112Sbostic union node *n; 38747112Sbostic { 38847112Sbostic register union node *redir; 38947112Sbostic 39047112Sbostic for (redir = n ; redir ; redir = redir->nfile.next) { 39147112Sbostic if (redir->type == NFROM 39247112Sbostic || redir->type == NTO 39347112Sbostic || redir->type == NAPPEND) { 39447112Sbostic struct arglist fn; 39547112Sbostic fn.lastp = &fn.list; 39647112Sbostic expandarg(redir->nfile.fname, &fn, 0); 39747112Sbostic redir->nfile.expfname = fn.list->text; 39847112Sbostic } 39947112Sbostic } 40047112Sbostic } 40147112Sbostic 40247112Sbostic 40347112Sbostic 40447112Sbostic /* 40547112Sbostic * Evaluate a pipeline. All the processes in the pipeline are children 40647112Sbostic * of the process creating the pipeline. (This differs from some versions 40747112Sbostic * of the shell, which make the last process in a pipeline the parent 40847112Sbostic * of all the rest.) 40947112Sbostic */ 41047112Sbostic 41147112Sbostic STATIC void 41247112Sbostic evalpipe(n) 41347112Sbostic union node *n; 41447112Sbostic { 41547112Sbostic struct job *jp; 41647112Sbostic struct nodelist *lp; 41747112Sbostic int pipelen; 41847112Sbostic int prevfd; 41947112Sbostic int pip[2]; 42047112Sbostic 42147112Sbostic TRACE(("evalpipe(0x%x) called\n", (int)n)); 42247112Sbostic pipelen = 0; 42347112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 42447112Sbostic pipelen++; 42547112Sbostic INTOFF; 42647112Sbostic jp = makejob(n, pipelen); 42747112Sbostic prevfd = -1; 42847112Sbostic for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 42947112Sbostic prehash(lp->n); 43047112Sbostic pip[1] = -1; 43147112Sbostic if (lp->next) { 43247112Sbostic if (pipe(pip) < 0) { 43347112Sbostic close(prevfd); 43447112Sbostic error("Pipe call failed"); 43547112Sbostic } 43647112Sbostic } 43747112Sbostic if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 43847112Sbostic INTON; 43947112Sbostic if (prevfd > 0) { 44047112Sbostic close(0); 44147112Sbostic copyfd(prevfd, 0); 44247112Sbostic close(prevfd); 44347112Sbostic } 44447112Sbostic if (pip[1] >= 0) { 44547112Sbostic close(pip[0]); 44647112Sbostic if (pip[1] != 1) { 44747112Sbostic close(1); 44847112Sbostic copyfd(pip[1], 1); 44947112Sbostic close(pip[1]); 45047112Sbostic } 45147112Sbostic } 45247112Sbostic evaltree(lp->n, EV_EXIT); 45347112Sbostic } 45447112Sbostic if (prevfd >= 0) 45547112Sbostic close(prevfd); 45647112Sbostic prevfd = pip[0]; 45747112Sbostic close(pip[1]); 45847112Sbostic } 45947112Sbostic INTON; 46047112Sbostic if (n->npipe.backgnd == 0) { 46147112Sbostic INTOFF; 46247112Sbostic exitstatus = waitforjob(jp); 46347112Sbostic TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 46447112Sbostic INTON; 46547112Sbostic } 46647112Sbostic } 46747112Sbostic 46847112Sbostic 46947112Sbostic 47047112Sbostic /* 47147112Sbostic * Execute a command inside back quotes. If it's a builtin command, we 47247112Sbostic * want to save its output in a block obtained from malloc. Otherwise 47347112Sbostic * we fork off a subprocess and get the output of the command via a pipe. 47447112Sbostic * Should be called with interrupts off. 47547112Sbostic */ 47647112Sbostic 47747112Sbostic void 47847112Sbostic evalbackcmd(n, result) 47947112Sbostic union node *n; 48047112Sbostic struct backcmd *result; 48147112Sbostic { 48247112Sbostic int pip[2]; 48347112Sbostic struct job *jp; 48447112Sbostic struct stackmark smark; /* unnecessary */ 48547112Sbostic 48647112Sbostic setstackmark(&smark); 48747112Sbostic result->fd = -1; 48847112Sbostic result->buf = NULL; 48947112Sbostic result->nleft = 0; 49047112Sbostic result->jp = NULL; 49147112Sbostic if (n->type == NCMD) { 49247112Sbostic evalcommand(n, EV_BACKCMD, result); 49347112Sbostic } else { 49447112Sbostic if (pipe(pip) < 0) 49547112Sbostic error("Pipe call failed"); 49647112Sbostic jp = makejob(n, 1); 49747112Sbostic if (forkshell(jp, n, FORK_NOJOB) == 0) { 49847112Sbostic FORCEINTON; 49947112Sbostic close(pip[0]); 50047112Sbostic if (pip[1] != 1) { 50147112Sbostic close(1); 50247112Sbostic copyfd(pip[1], 1); 50347112Sbostic close(pip[1]); 50447112Sbostic } 50547112Sbostic evaltree(n, EV_EXIT); 50647112Sbostic } 50747112Sbostic close(pip[1]); 50847112Sbostic result->fd = pip[0]; 50947112Sbostic result->jp = jp; 51047112Sbostic } 51147112Sbostic popstackmark(&smark); 51247112Sbostic TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 51347112Sbostic result->fd, result->buf, result->nleft, result->jp)); 51447112Sbostic } 51547112Sbostic 51647112Sbostic 51747112Sbostic 51847112Sbostic /* 51947112Sbostic * Execute a simple command. 52047112Sbostic */ 52147112Sbostic 52247112Sbostic STATIC void 52347112Sbostic evalcommand(cmd, flags, backcmd) 52447112Sbostic union node *cmd; 52547112Sbostic struct backcmd *backcmd; 52647112Sbostic { 52747112Sbostic struct stackmark smark; 52847112Sbostic union node *argp; 52947112Sbostic struct arglist arglist; 53047112Sbostic struct arglist varlist; 53147112Sbostic char **argv; 53247112Sbostic int argc; 53347112Sbostic char **envp; 53447112Sbostic int varflag; 53547112Sbostic struct strlist *sp; 53647112Sbostic register char *p; 53747112Sbostic int mode; 53847112Sbostic int pip[2]; 53947112Sbostic struct cmdentry cmdentry; 54047112Sbostic struct job *jp; 54147112Sbostic struct jmploc jmploc; 54247112Sbostic struct jmploc *volatile savehandler; 54347112Sbostic char *volatile savecmdname; 54447112Sbostic volatile struct shparam saveparam; 54547112Sbostic struct localvar *volatile savelocalvars; 54647112Sbostic volatile int e; 54747112Sbostic char *lastarg; 54847112Sbostic 54947112Sbostic /* First expand the arguments. */ 55047112Sbostic TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); 55147112Sbostic setstackmark(&smark); 55247112Sbostic arglist.lastp = &arglist.list; 55347112Sbostic varlist.lastp = &varlist.list; 55447112Sbostic varflag = 1; 55547112Sbostic for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 55647112Sbostic p = argp->narg.text; 55747112Sbostic if (varflag && is_name(*p)) { 55847112Sbostic do { 55947112Sbostic p++; 56047112Sbostic } while (is_in_name(*p)); 56147112Sbostic if (*p == '=') { 56247112Sbostic expandarg(argp, &varlist, 0); 56347112Sbostic continue; 56447112Sbostic } 56547112Sbostic } 56647112Sbostic expandarg(argp, &arglist, 1); 56747112Sbostic varflag = 0; 56847112Sbostic } 56947112Sbostic *arglist.lastp = NULL; 57047112Sbostic *varlist.lastp = NULL; 57147112Sbostic expredir(cmd->ncmd.redirect); 57247112Sbostic argc = 0; 57347112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) 57447112Sbostic argc++; 57547112Sbostic argv = stalloc(sizeof (char *) * (argc + 1)); 57647112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) 57747112Sbostic *argv++ = sp->text; 57847112Sbostic *argv = NULL; 57947112Sbostic lastarg = NULL; 58047112Sbostic if (iflag && funcnest == 0 && argc > 0) 58147112Sbostic lastarg = argv[-1]; 58247112Sbostic argv -= argc; 58347112Sbostic 58447112Sbostic /* Print the command if xflag is set. */ 58547112Sbostic if (xflag) { 58647112Sbostic outc('+', &errout); 58747112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) { 58847112Sbostic outc(' ', &errout); 58947112Sbostic out2str(sp->text); 59047112Sbostic } 59147112Sbostic for (sp = arglist.list ; sp ; sp = sp->next) { 59247112Sbostic outc(' ', &errout); 59347112Sbostic out2str(sp->text); 59447112Sbostic } 59547112Sbostic outc('\n', &errout); 59647112Sbostic flushout(&errout); 59747112Sbostic } 59847112Sbostic 59947112Sbostic /* Now locate the command. */ 60047112Sbostic if (argc == 0) { 60147112Sbostic cmdentry.cmdtype = CMDBUILTIN; 60247112Sbostic cmdentry.u.index = BLTINCMD; 60347112Sbostic } else { 60447112Sbostic find_command(argv[0], &cmdentry, 1); 60547112Sbostic if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ 60647112Sbostic exitstatus = 2; 60747112Sbostic flushout(&errout); 60847112Sbostic return; 60947112Sbostic } 61047112Sbostic /* implement the bltin builtin here */ 61147112Sbostic if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { 61247112Sbostic for (;;) { 61347112Sbostic argv++; 61447112Sbostic if (--argc == 0) 61547112Sbostic break; 61647112Sbostic if ((cmdentry.u.index = find_builtin(*argv)) < 0) { 61747112Sbostic outfmt(&errout, "%s: not found\n", *argv); 61847112Sbostic exitstatus = 2; 61947112Sbostic flushout(&errout); 62047112Sbostic return; 62147112Sbostic } 62247112Sbostic if (cmdentry.u.index != BLTINCMD) 62347112Sbostic break; 62447112Sbostic } 62547112Sbostic } 62647112Sbostic } 62747112Sbostic 62847112Sbostic /* Fork off a child process if necessary. */ 62947112Sbostic if (cmd->ncmd.backgnd 63047112Sbostic || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 63147112Sbostic || (flags & EV_BACKCMD) != 0 63247112Sbostic && (cmdentry.cmdtype != CMDBUILTIN 63347112Sbostic || cmdentry.u.index == DOTCMD 63447112Sbostic || cmdentry.u.index == EVALCMD)) { 63547112Sbostic jp = makejob(cmd, 1); 63647112Sbostic mode = cmd->ncmd.backgnd; 63747112Sbostic if (flags & EV_BACKCMD) { 63847112Sbostic mode = FORK_NOJOB; 63947112Sbostic if (pipe(pip) < 0) 64047112Sbostic error("Pipe call failed"); 64147112Sbostic } 64247112Sbostic if (forkshell(jp, cmd, mode) != 0) 64347112Sbostic goto parent; /* at end of routine */ 64447112Sbostic if (flags & EV_BACKCMD) { 64547112Sbostic FORCEINTON; 64647112Sbostic close(pip[0]); 64747112Sbostic if (pip[1] != 1) { 64847112Sbostic close(1); 64947112Sbostic copyfd(pip[1], 1); 65047112Sbostic close(pip[1]); 65147112Sbostic } 65247112Sbostic } 65347112Sbostic flags |= EV_EXIT; 65447112Sbostic } 65547112Sbostic 65647112Sbostic /* This is the child process if a fork occurred. */ 65747112Sbostic /* Execute the command. */ 65847112Sbostic if (cmdentry.cmdtype == CMDFUNCTION) { 65947112Sbostic trputs("Shell function: "); trargs(argv); 66047112Sbostic redirect(cmd->ncmd.redirect, REDIR_PUSH); 66147112Sbostic saveparam = shellparam; 66247112Sbostic shellparam.malloc = 0; 66347112Sbostic shellparam.nparam = argc - 1; 66447112Sbostic shellparam.p = argv + 1; 66547112Sbostic shellparam.optnext = NULL; 66647112Sbostic INTOFF; 66747112Sbostic savelocalvars = localvars; 66847112Sbostic localvars = NULL; 66947112Sbostic INTON; 67047112Sbostic if (setjmp(jmploc.loc)) { 67147112Sbostic if (exception == EXSHELLPROC) 67247112Sbostic freeparam((struct shparam *)&saveparam); 67347112Sbostic else { 67447112Sbostic freeparam(&shellparam); 67547112Sbostic shellparam = saveparam; 67647112Sbostic } 67747112Sbostic poplocalvars(); 67847112Sbostic localvars = savelocalvars; 67947112Sbostic handler = savehandler; 68047112Sbostic longjmp(handler->loc, 1); 68147112Sbostic } 68247112Sbostic savehandler = handler; 68347112Sbostic handler = &jmploc; 68447112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 68547112Sbostic mklocal(sp->text); 68647112Sbostic funcnest++; 68747112Sbostic evaltree(cmdentry.u.func, 0); 68847112Sbostic funcnest--; 68947112Sbostic INTOFF; 69047112Sbostic poplocalvars(); 69147112Sbostic localvars = savelocalvars; 69247112Sbostic freeparam(&shellparam); 69347112Sbostic shellparam = saveparam; 69447112Sbostic handler = savehandler; 69547112Sbostic popredir(); 69647112Sbostic INTON; 69747112Sbostic if (evalskip == SKIPFUNC) { 69847112Sbostic evalskip = 0; 69947112Sbostic skipcount = 0; 70047112Sbostic } 70147112Sbostic if (flags & EV_EXIT) 70247112Sbostic exitshell(exitstatus); 70347112Sbostic } else if (cmdentry.cmdtype == CMDBUILTIN) { 70447112Sbostic trputs("builtin command: "); trargs(argv); 70547112Sbostic mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 70647112Sbostic if (flags == EV_BACKCMD) { 70747112Sbostic memout.nleft = 0; 70847112Sbostic memout.nextc = memout.buf; 70947112Sbostic memout.bufsize = 64; 71047112Sbostic mode |= REDIR_BACKQ; 71147112Sbostic } 71247112Sbostic redirect(cmd->ncmd.redirect, mode); 71347112Sbostic savecmdname = commandname; 71447112Sbostic cmdenviron = varlist.list; 71547112Sbostic e = -1; 71647112Sbostic if (setjmp(jmploc.loc)) { 71747112Sbostic e = exception; 71847112Sbostic exitstatus = (e == EXINT)? SIGINT+128 : 2; 71947112Sbostic goto cmddone; 72047112Sbostic } 72147112Sbostic savehandler = handler; 72247112Sbostic handler = &jmploc; 72347112Sbostic commandname = argv[0]; 72447112Sbostic argptr = argv + 1; 72547112Sbostic optptr = NULL; /* initialize nextopt */ 72647112Sbostic exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 72747112Sbostic flushall(); 72847112Sbostic cmddone: 72947112Sbostic out1 = &output; 73047112Sbostic out2 = &errout; 73147112Sbostic freestdout(); 73247112Sbostic if (e != EXSHELLPROC) { 73347112Sbostic commandname = savecmdname; 73447112Sbostic if (flags & EV_EXIT) { 73547112Sbostic exitshell(exitstatus); 73647112Sbostic } 73747112Sbostic } 73847112Sbostic handler = savehandler; 73947112Sbostic if (e != -1) { 74047112Sbostic if (e != EXERROR || cmdentry.u.index == BLTINCMD 74147112Sbostic || cmdentry.u.index == DOTCMD 74247112Sbostic || cmdentry.u.index == EVALCMD 74347112Sbostic || cmdentry.u.index == EXECCMD) 74447112Sbostic exraise(e); 74547112Sbostic FORCEINTON; 74647112Sbostic } 74747112Sbostic if (cmdentry.u.index != EXECCMD) 74847112Sbostic popredir(); 74947112Sbostic if (flags == EV_BACKCMD) { 75047112Sbostic backcmd->buf = memout.buf; 75147112Sbostic backcmd->nleft = memout.nextc - memout.buf; 75247112Sbostic memout.buf = NULL; 75347112Sbostic } 75447112Sbostic } else { 75547112Sbostic trputs("normal command: "); trargs(argv); 75647112Sbostic clearredir(); 75747112Sbostic redirect(cmd->ncmd.redirect, 0); 75847112Sbostic if (varlist.list) { 75947112Sbostic p = stalloc(strlen(pathval()) + 1); 76047112Sbostic scopy(pathval(), p); 76147112Sbostic } else { 76247112Sbostic p = pathval(); 76347112Sbostic } 76447112Sbostic for (sp = varlist.list ; sp ; sp = sp->next) 76547112Sbostic setvareq(sp->text, VEXPORT|VSTACK); 76647112Sbostic envp = environment(); 76747112Sbostic shellexec(argv, envp, p, cmdentry.u.index); 76847112Sbostic /*NOTREACHED*/ 76947112Sbostic } 77047112Sbostic goto out; 77147112Sbostic 77247112Sbostic parent: /* parent process gets here (if we forked) */ 77347112Sbostic if (mode == 0) { /* argument to fork */ 77447112Sbostic INTOFF; 77547112Sbostic exitstatus = waitforjob(jp); 77647112Sbostic INTON; 77747112Sbostic } else if (mode == 2) { 77847112Sbostic backcmd->fd = pip[0]; 77947112Sbostic close(pip[1]); 78047112Sbostic backcmd->jp = jp; 78147112Sbostic } 78247112Sbostic 78347112Sbostic out: 78447112Sbostic if (lastarg) 78547112Sbostic setvar("_", lastarg, 0); 78647112Sbostic popstackmark(&smark); 78747112Sbostic } 78847112Sbostic 78947112Sbostic 79047112Sbostic 79147112Sbostic /* 79247112Sbostic * Search for a command. This is called before we fork so that the 79347112Sbostic * location of the command will be available in the parent as well as 79447112Sbostic * the child. The check for "goodname" is an overly conservative 79547112Sbostic * check that the name will not be subject to expansion. 79647112Sbostic */ 79747112Sbostic 79847112Sbostic STATIC void 79947112Sbostic prehash(n) 80047112Sbostic union node *n; 80147112Sbostic { 80247112Sbostic struct cmdentry entry; 80347112Sbostic 80447112Sbostic if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) 80547112Sbostic find_command(n->ncmd.args->narg.text, &entry, 0); 80647112Sbostic } 80747112Sbostic 80847112Sbostic 80947112Sbostic 81047112Sbostic /* 81147112Sbostic * Builtin commands. Builtin commands whose functions are closely 81247112Sbostic * tied to evaluation are implemented here. 81347112Sbostic */ 81447112Sbostic 81547112Sbostic /* 81647112Sbostic * No command given, or a bltin command with no arguments. Set the 81747112Sbostic * specified variables. 81847112Sbostic */ 81947112Sbostic 82047112Sbostic bltincmd(argc, argv) char **argv; { 82147112Sbostic listsetvar(cmdenviron); 82247112Sbostic return exitstatus; 82347112Sbostic } 82447112Sbostic 82547112Sbostic 82647112Sbostic /* 82747112Sbostic * Handle break and continue commands. Break, continue, and return are 82847112Sbostic * all handled by setting the evalskip flag. The evaluation routines 82947112Sbostic * above all check this flag, and if it is set they start skipping 83047112Sbostic * commands rather than executing them. The variable skipcount is 83147112Sbostic * the number of loops to break/continue, or the number of function 83247112Sbostic * levels to return. (The latter is always 1.) It should probably 83347112Sbostic * be an error to break out of more loops than exist, but it isn't 83447112Sbostic * in the standard shell so we don't make it one here. 83547112Sbostic */ 83647112Sbostic 83747112Sbostic breakcmd(argc, argv) char **argv; { 83847112Sbostic int n; 83947112Sbostic 84047112Sbostic n = 1; 84147112Sbostic if (argc > 1) 84247112Sbostic n = number(argv[1]); 84347112Sbostic if (n > loopnest) 84447112Sbostic n = loopnest; 84547112Sbostic if (n > 0) { 84647112Sbostic evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 84747112Sbostic skipcount = n; 84847112Sbostic } 84947112Sbostic return 0; 85047112Sbostic } 85147112Sbostic 85247112Sbostic 85347112Sbostic /* 85447112Sbostic * The return command. 85547112Sbostic */ 85647112Sbostic 85747112Sbostic returncmd(argc, argv) char **argv; { 85847112Sbostic int ret; 85947112Sbostic 86047112Sbostic ret = exitstatus; 86147112Sbostic if (argc > 1) 86247112Sbostic ret = number(argv[1]); 86347112Sbostic if (funcnest) { 86447112Sbostic evalskip = SKIPFUNC; 86547112Sbostic skipcount = 1; 86647112Sbostic } 86747112Sbostic return ret; 86847112Sbostic } 86947112Sbostic 87047112Sbostic 87147112Sbostic truecmd(argc, argv) char **argv; { 87247112Sbostic return 0; 87347112Sbostic } 87447112Sbostic 87547112Sbostic 87647112Sbostic execcmd(argc, argv) char **argv; { 87747112Sbostic if (argc > 1) { 87847112Sbostic iflag = 0; /* exit on error */ 87947112Sbostic setinteractive(0); 88047112Sbostic #if JOBS 88147112Sbostic jflag = 0; 88247112Sbostic setjobctl(0); 88347112Sbostic #endif 88447112Sbostic shellexec(argv + 1, environment(), pathval(), 0); 88547112Sbostic 88647112Sbostic } 88747112Sbostic return 0; 88847112Sbostic } 889