STATIC void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); expredir(n->nredir.redirect); jp = makejob(n, 1); if (forkshell(jp, n, backgnd) == 0) { if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ } if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; } }
static void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); oexitstatus = exitstatus; expredir(n->nredir.redirect); if ((!backgnd && flags & EV_EXIT && !have_traps()) || forkshell(jp = makejob(n, 1), n, backgnd) == 0) { if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ } else if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; } }
STATIC void evalsubshell(shinstance *psh, union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); expredir(psh, n->nredir.redirect); INTOFF; jp = makejob(psh, n, 1); if (forkshell(psh, jp, n, backgnd ? FORK_BG : FORK_FG) == 0) { INTON; if (backgnd) flags &=~ EV_TESTED; redirect(psh, n->nredir.redirect, 0); /* never returns */ evaltree(psh, n->nredir.n, flags | EV_EXIT); } if (! backgnd) psh->exitstatus = waitforjob(psh, jp); INTON; }
static void evalredir(union node *n, int flags) { struct jmploc jmploc; struct jmploc *savehandler; volatile int in_redirect = 1; oexitstatus = exitstatus; expredir(n->nredir.redirect); savehandler = handler; if (setjmp(jmploc.loc)) { int e; handler = savehandler; e = exception; popredir(); if (e == EXERROR || e == EXEXEC) { if (in_redirect) { exitstatus = 2; return; } } longjmp(handler->loc, 1); } else { INTOFF; handler = &jmploc; redirect(n->nredir.redirect, REDIR_PUSH); in_redirect = 0; INTON; evaltree(n->nredir.n, flags); } INTOFF; handler = savehandler; popredir(); INTON; }
static void evalcommand(union node *cmd, int flags, struct backcmd *backcmd) { union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *savehandler; char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; struct parsefile *savetopfile; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; const char *path = pathval(); /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; jp = NULL; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { if (varflag && isassignment(argp->narg.text)) { expandarg(argp, varflag == 1 ? &varlist : &arglist, EXP_VARTILDE); continue; } else if (varflag == 1) varflag = isdeclarationcmd(&argp->narg) ? 2 : 0; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); } *arglist.lastp = NULL; *varlist.lastp = NULL; expredir(cmd->ncmd.redirect); argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; /* Add one slot at the beginning for tryexec(). */ argv = stalloc(sizeof (char *) * (argc + 2)); argv++; for (sp = arglist.list ; sp ; sp = sp->next) { TRACE(("evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag && funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag) { char sep = 0; const char *p, *ps4; ps4 = expandstr(ps4val()); out2str(ps4 != NULL ? ps4 : ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); p = strchr(sp->text, '='); if (p != NULL) { p++; outbin(sp->text, p - sp->text, out2); out2qstr(p); } else out2qstr(sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); /* Disambiguate command looking like assignment. */ if (sp == arglist.list && strchr(sp->text, '=') != NULL && strchr(sp->text, '\'') == NULL) { out2c('\''); out2str(sp->text); out2c('\''); } else out2qstr(sp->text); sep = ' '; } out2c('\n'); flushout(&errout); } /* Now locate the command. */ if (argc == 0) { /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; cmdentry.special = 0; } else { static const char PATH[] = "PATH="; int cmd_flags = 0, bltinonly = 0; /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list ; sp ; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { path = sp->text + sizeof(PATH) - 1; /* * On `PATH=... command`, we need to make * sure that the command isn't using the * non-updated hash table of the outer PATH * setting and we need to make sure that * the hash table isn't filled with items * from the temporary setting. * * It would be better to forbit using and * updating the table while this command * runs, by the command finding mechanism * is heavily integrated with hash handling, * so we just delete the hash before and after * the command runs. Partly deleting like * changepatch() does doesn't seem worth the * bookinging effort, since most such runs add * directories in front of the new PATH. */ clearcmdentry(); do_clearcmdentry = 1; } for (;;) { if (bltinonly) { cmdentry.u.index = find_builtin(*argv, &cmdentry.special); if (cmdentry.u.index < 0) { cmdentry.u.index = BLTINCMD; argv--; argc++; break; } } else find_command(argv[0], &cmdentry, cmd_flags, path); /* implement the bltin and command builtins here */ if (cmdentry.cmdtype != CMDBUILTIN) break; if (cmdentry.u.index == BLTINCMD) { if (argc == 1) break; argv++; argc--; bltinonly = 1; } else if (cmdentry.u.index == COMMANDCMD) { if (argc == 1) break; if (!strcmp(argv[1], "-p")) { if (argc == 2) break; if (argv[2][0] == '-') { if (strcmp(argv[2], "--")) break; if (argc == 3) break; argv += 3; argc -= 3; } else { argv += 2; argc -= 2; } path = _PATH_STDPATH; clearcmdentry(); do_clearcmdentry = 1; } else if (!strcmp(argv[1], "--")) { if (argc == 2) break; argv += 2; argc -= 2; } else if (argv[1][0] == '-') break; else { argv++; argc--; } cmd_flags |= DO_NOFUNC; bltinonly = 0; } else break; } /* * Special builtins lose their special properties when * called via 'command'. */ if (cmd_flags & DO_NOFUNC) cmdentry.special = 0; } /* Fork off a child process if necessary. */ if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) { jp = makejob(cmd, 1); mode = FORK_FG; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); } if (cmdentry.cmdtype == CMDNORMAL && cmd->ncmd.redirect == NULL && varlist.list == NULL && (mode == FORK_FG || mode == FORK_NOJOB) && !disvforkset() && !iflag && !mflag) { vforkexecshell(jp, argv, environment(), path, cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL); goto parent; } if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ if (flags & EV_BACKCMD) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } flags &= ~EV_BACKCMD; } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ if (cmdentry.cmdtype == CMDFUNCTION) { #ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; localvars = NULL; reffunc(cmdentry.u.func); savehandler = handler; if (setjmp(jmploc.loc)) { freeparam(&shellparam); shellparam = saveparam; popredir(); unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; funcnest--; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; funcnest++; redirect(cmd->ncmd.redirect, REDIR_PUSH); INTON; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); exitstatus = oexitstatus; evaltree(getfuncnode(cmdentry.u.func), flags & (EV_TESTED | EV_EXIT)); INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; funcnest--; popredir(); INTON; if (evalskip == SKIPFUNC) { evalskip = 0; skipcount = 0; } if (jp) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } savecmdname = commandname; savetopfile = getcurrentfile(); cmdenviron = varlist.list; e = -1; savehandler = handler; if (setjmp(jmploc.loc)) { e = exception; if (e == EXINT) exitstatus = SIGINT+128; else if (e != EXEXIT) exitstatus = 2; goto cmddone; } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); outclearerror(out1); /* * If there is no command word, redirection errors should * not be fatal but assignment errors should. */ if (argc == 0) cmdentry.special = 1; listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); if (argc > 0) bltinsetlocale(); commandname = argv[0]; argptr = argv + 1; nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); if (outiserror(out1)) { warning("write error on stdout"); if (exitstatus == 0 || exitstatus == 1) exitstatus = 2; } cmddone: if (argc > 0) bltinunsetlocale(); cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); handler = savehandler; commandname = savecmdname; if (jp) exitshell(exitstatus); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } if (cmdentry.u.index != EXECCMD) popredir(); if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.special) exraise(e); popfilesupto(savetopfile); if (flags != EV_BACKCMD) FORCEINTON; } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, path, cmdentry.u.index); /*NOTREACHED*/ } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ INTOFF; exitstatus = waitforjob(jp, &realstatus); INTON; if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { evalskip = SKIPBREAK; skipcount = loopnest; } } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) clearcmdentry(); }
void evaltree(union node *n, int flags) { int checkexit = 0; void (*evalfn)(union node *, int); unsigned isor; int status; if (n == NULL) { //TRACE(("evaltree(NULL) called\n")); goto out; } #ifndef SMALL displayhist = 1; /* show history substitutions done with fc */ #endif TRACE(("pid %d, evaltree(%p: %d, %d, %p) called\n", getpid(), n, n->type, flags)); switch (n->type) { default: #ifdef DEBUG #ifndef USE_GLIBC_STDIO flushout(out1); #endif break; #endif case NNOT: evaltree(n->nnot.com, EV_TESTED); status = !exitstatus; goto setstatus; case NREDIR: expredir(n->nredir.redirect); status = redirectsafe(n->nredir.redirect, REDIR_PUSH); if (!status) { evaltree(n->nredir.n, flags & EV_TESTED); status = exitstatus; } popredir(0); goto setstatus; case NCMD: #ifdef notyet if (eflag && !(flags & EV_TESTED)) checkexit = ~0; evalcommand(n, flags, (struct backcmd *)NULL); break; #else evalfn = evalcommand; checkexit: if (eflag && !(flags & EV_TESTED)) checkexit = ~0; goto calleval; #endif case NFOR: evalfn = evalfor; goto calleval; case NWHILE: case NUNTIL: evalfn = evalloop; goto calleval; case NSUBSHELL: case NBACKGND: evalfn = evalsubshell; evalsubshell (n, flags); case NPIPE: evalfn = evalpipe; #ifdef notyet if (eflag && !(flags & EV_TESTED)) checkexit = ~0; goto calleval; #else goto checkexit; #endif case NCASE: evalfn = evalcase; goto calleval; case NAND: case NOR: case NSEMI: #if NAND + 1 != NOR #error NAND + 1 != NOR #endif #if NOR + 1 != NSEMI #error NOR + 1 != NSEMI #endif isor = n->type - NAND; evaltree( n->nbinary.ch1, (flags | ((isor >> 1) - 1)) & EV_TESTED ); if (!exitstatus == isor) break; if (!evalskip) { n = n->nbinary.ch2; evaln: evaltree(n, flags); break; calleval: evalfn(n, flags); break; } break; case NIF: evaltree(n->nif.test, EV_TESTED); if (evalskip) break; if (exitstatus == 0) { n = n->nif.ifpart; goto evaln; } else if (n->nif.elsepart) { n = n->nif.elsepart; goto evaln; } goto success; case NDEFUN: defun(n->narg.text, n->narg.next); success: status = 0; setstatus: exitstatus = status; break; } out: if ((checkexit & exitstatus) || (pendingsigs && dotrap()) || (flags & EV_EXIT)) exraise(EXEXIT); }
static void evalcommand(union node *cmd, int flags, struct backcmd *backcmd) { struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *savehandler; char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); setstackmark(&smark); arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (varflag && is_name(*p)) { do { p++; } while (is_in_name(*p)); if (*p == '=') { expandarg(argp, &varlist, EXP_VARTILDE); continue; } } expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; *varlist.lastp = NULL; expredir(cmd->ncmd.redirect); argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; argv = stalloc(sizeof (char *) * (argc + 1)); for (sp = arglist.list ; sp ; sp = sp->next) { TRACE(("evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag && funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag) { char sep = 0; const char *p; out2str(ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(' ', &errout); p = sp->text; while (*p != '=' && *p != '\0') out2c(*p++); if (*p != '\0') { out2c(*p++); out2qstr(p); } sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(' ', &errout); /* Disambiguate command looking like assignment. */ if (sp == arglist.list && strchr(sp->text, '=') != NULL && strchr(sp->text, '\'') == NULL) { out2c('\''); out2str(sp->text); out2c('\''); } else out2qstr(sp->text); sep = ' '; } outc('\n', &errout); flushout(&errout); } /* Now locate the command. */ if (argc == 0) { /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; cmdentry.special = 1; } else { static const char PATH[] = "PATH="; char *path = pathval(); /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list ; sp ; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { path = sp->text + sizeof(PATH) - 1; /* * On `PATH=... command`, we need to make * sure that the command isn't using the * non-updated hash table of the outer PATH * setting and we need to make sure that * the hash table isn't filled with items * from the temporary setting. * * It would be better to forbit using and * updating the table while this command * runs, by the command finding mechanism * is heavily integrated with hash handling, * so we just delete the hash before and after * the command runs. Partly deleting like * changepatch() does doesn't seem worth the * bookinging effort, since most such runs add * directories in front of the new PATH. */ clearcmdentry(0); do_clearcmdentry = 1; } find_command(argv[0], &cmdentry, 1, path); if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ exitstatus = 127; flushout(&errout); return; } /* implement the bltin builtin here */ if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { for (;;) { argv++; if (--argc == 0) break; if ((cmdentry.u.index = find_builtin(*argv, &cmdentry.special)) < 0) { outfmt(&errout, "%s: not found\n", *argv); exitstatus = 127; flushout(&errout); return; } if (cmdentry.u.index != BLTINCMD) break; } } } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd || (cmdentry.cmdtype == CMDNORMAL && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.index == CDCMD || cmdentry.u.index == DOTCMD || cmdentry.u.index == EVALCMD)) || (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == COMMANDCMD)) { jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); } if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ if (flags & EV_BACKCMD) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ if (cmdentry.cmdtype == CMDFUNCTION) { #ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif redirect(cmd->ncmd.redirect, REDIR_PUSH); saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; localvars = NULL; reffunc(cmdentry.u.func); INTON; savehandler = handler; if (setjmp(jmploc.loc)) { if (exception == EXSHELLPROC) freeparam(&saveparam); else { freeparam(&shellparam); shellparam = saveparam; } unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); funcnest++; exitstatus = oexitstatus; if (flags & EV_TESTED) evaltree(getfuncnode(cmdentry.u.func), EV_TESTED); else evaltree(getfuncnode(cmdentry.u.func), 0); funcnest--; INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; popredir(); INTON; if (evalskip == SKIPFUNC) { evalskip = 0; skipcount = 0; } if (flags & EV_EXIT) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } savecmdname = commandname; cmdenviron = varlist.list; e = -1; savehandler = handler; if (setjmp(jmploc.loc)) { e = exception; exitstatus = (e == EXINT)? SIGINT+128 : 2; goto cmddone; } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); if (cmdentry.special) listsetvar(cmdenviron); commandname = argv[0]; argptr = argv + 1; nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); cmddone: cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); if (e != EXSHELLPROC) { commandname = savecmdname; if (flags & EV_EXIT) { exitshell(exitstatus); } } handler = savehandler; if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.special) exraise(e); FORCEINTON; } if (cmdentry.u.index != EXECCMD) popredir(); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif clearredir(); redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, pathval(), cmdentry.u.index); /*NOTREACHED*/ } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ INTOFF; exitstatus = waitforjob(jp, &realstatus); INTON; if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { evalskip = SKIPBREAK; skipcount = loopnest; } } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) clearcmdentry(0); popstackmark(&smark); }
void evaltree(union node *n, int flags) { int do_etest; do_etest = 0; if (n == NULL) { TRACE(("evaltree(NULL) called\n")); exitstatus = 0; goto out; } #ifndef NO_HISTORY displayhist = 1; /* show history substitutions done with fc */ #endif TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); switch (n->type) { case NSEMI: evaltree(n->nbinary.ch1, flags & ~EV_EXIT); if (evalskip) goto out; evaltree(n->nbinary.ch2, flags); break; case NAND: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus != 0) { goto out; } evaltree(n->nbinary.ch2, flags); break; case NOR: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus == 0) goto out; evaltree(n->nbinary.ch2, flags); break; case NREDIR: oexitstatus = exitstatus; expredir(n->nredir.redirect); redirect(n->nredir.redirect, REDIR_PUSH); evaltree(n->nredir.n, flags); popredir(); break; case NSUBSHELL: evalsubshell(n, flags); do_etest = !(flags & EV_TESTED); break; case NBACKGND: evalsubshell(n, flags); break; case NIF: { evaltree(n->nif.test, EV_TESTED); if (evalskip) goto out; if (exitstatus == 0) evaltree(n->nif.ifpart, flags); else if (n->nif.elsepart) evaltree(n->nif.elsepart, flags); else exitstatus = 0; break; } case NWHILE: case NUNTIL: evalloop(n, flags & ~EV_EXIT); break; case NFOR: evalfor(n, flags & ~EV_EXIT); break; case NCASE: evalcase(n, flags); break; case NDEFUN: defun(n->narg.text, n->narg.next); exitstatus = 0; break; case NNOT: evaltree(n->nnot.com, EV_TESTED); exitstatus = !exitstatus; break; case NPIPE: evalpipe(n); do_etest = !(flags & EV_TESTED); break; case NCMD: evalcommand(n, flags, (struct backcmd *)NULL); do_etest = !(flags & EV_TESTED); break; default: out1fmt("Node type = %d\n", n->type); flushout(&output); break; } out: if (pendingsigs) dotrap(); if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest)) exitshell(exitstatus); }
STATIC void evalcommand(shinstance *psh, union node *cmd, int flags, struct backcmd *backcmd) { struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *volatile savehandler; char *volatile savecmdname; volatile struct shparam saveparam; struct localvar *volatile savelocalvars; volatile int e; char *lastarg; const char *path = pathval(psh); volatile int temp_path; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &argv; (void) &argc; (void) &lastarg; (void) &flags; #endif psh->vforked = 0; /* First expand the arguments. */ TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(psh, &smark); psh->back_exitstatus = 0; arglist.lastp = &arglist.list; varflag = 1; /* Expand arguments, ignoring the initial 'name=value' ones */ for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (varflag && is_name(*p)) { do { p++; } while (is_in_name(*p)); if (*p == '=') continue; } expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; expredir(psh, cmd->ncmd.redirect); /* Now do the initial 'name=value' ones we skipped above */ varlist.lastp = &varlist.list; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (!is_name(*p)) break; do p++; while (is_in_name(*p)); if (*p != '=') break; expandarg(psh, argp, &varlist, EXP_VARTILDE); } *varlist.lastp = NULL; argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; argv = stalloc(psh, sizeof (char *) * (argc + 1)); for (sp = arglist.list ; sp ; sp = sp->next) { TRACE((psh, "evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag(psh) && psh->funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag(psh)) { char sep = 0; out2str(psh, ps4val(psh)); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } outc('\n', &psh->errout); flushout(&psh->errout); } /* Now locate the command. */ if (argc == 0) { cmdentry.cmdtype = CMDSPLBLTIN; cmdentry.u.bltin = bltincmd; } else { static const char PATH[] = "PATH="; int cmd_flags = DO_ERR; /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list; sp; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) path = sp->text + sizeof(PATH) - 1; do { int argsused, use_syspath; find_command(psh, argv[0], &cmdentry, cmd_flags, path); if (cmdentry.cmdtype == CMDUNKNOWN) { psh->exitstatus = 127; flushout(&psh->errout); goto out; } /* implement the 'command' builtin here */ if (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.bltin != bltincmd) break; cmd_flags |= DO_NOFUNC; argsused = parse_command_args(psh, argc, argv, &use_syspath); if (argsused == 0) { /* use 'type' builting to display info */ cmdentry.u.bltin = typecmd; break; } argc -= argsused; argv += argsused; if (use_syspath) path = syspath(psh) + 5; } while (argc != 0); if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) /* posix mandates that 'command <splbltin>' act as if <splbltin> was a normal builtin */ cmdentry.cmdtype = CMDBUILTIN; } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) || ((flags & EV_BACKCMD) != 0 && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) || cmdentry.u.bltin == dotcmd || cmdentry.u.bltin == evalcmd))) { INTOFF; jp = makejob(psh, cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (sh_pipe(psh, pip) < 0) error(psh, "Pipe call failed"); } #ifdef DO_SHAREDVFORK /* It is essential that if DO_SHAREDVFORK is defined that the * child's address space is actually shared with the parent as * we rely on this. */ if (cmdentry.cmdtype == CMDNORMAL) { pid_t pid; savelocalvars = psh->localvars; psh->localvars = NULL; psh->vforked = 1; switch (pid = vfork()) { case -1: TRACE((psh, "Vfork failed, errno=%d\n", errno)); INTON; error(psh, "Cannot vfork"); break; case 0: /* Make sure that exceptions only unwind to * after the vfork(2) */ if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { /* We can't progress with the vfork, * so, set vforked = 2 so the parent * knows, and _exit(); */ psh->vforked = 2; sh__exit(psh, 0); } else { sh__exit(psh, psh->exerrno); } } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, VEXPORT | VNOFUNC); forkchild(psh, jp, cmd, mode, psh->vforked); break; default: psh->handler = savehandler; /* restore from vfork(2) */ poplocalvars(psh); psh->localvars = savelocalvars; if (psh->vforked == 2) { psh->vforked = 0; (void)sh_waitpid(psh, pid, NULL, 0); /* We need to progress in a normal fork fashion */ goto normal_fork; } psh->vforked = 0; forkparent(psh, jp, cmd, mode, pid); goto parent; } } else { normal_fork: #endif if (forkshell(psh, jp, cmd, mode) != 0) goto parent; /* at end of routine */ FORCEINTON; #ifdef DO_SHAREDVFORK } #endif if (flags & EV_BACKCMD) { if (!psh->vforked) { FORCEINTON; } shfile_close(&psh->fdtab, pip[0]); if (pip[1] != 1) { movefd(psh, pip[1], 1); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ switch (cmdentry.cmdtype) { case CMDFUNCTION: #ifdef DEBUG trputs(psh, "Shell function: "); trargs(psh, argv); #endif redirect(psh, cmd->ncmd.redirect, REDIR_PUSH); saveparam = psh->shellparam; psh->shellparam.malloc = 0; psh->shellparam.reset = 1; psh->shellparam.nparam = argc - 1; psh->shellparam.p = argv + 1; psh->shellparam.optnext = NULL; INTOFF; savelocalvars = psh->localvars; psh->localvars = NULL; INTON; if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { freeparam(psh, (volatile struct shparam *) &saveparam); } else { freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; } poplocalvars(psh); psh->localvars = savelocalvars; psh->handler = savehandler; longjmp(psh->handler->loc, 1); } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, 0); /* stop shell blowing its stack */ if (++psh->funcnest > 1000) error(psh, "too many nested function calls"); evaltree(psh, cmdentry.u.func, flags & EV_TESTED); psh->funcnest--; INTOFF; poplocalvars(psh); psh->localvars = savelocalvars; freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; psh->handler = savehandler; popredir(psh); INTON; if (psh->evalskip == SKIPFUNC) { psh->evalskip = 0; psh->skipcount = 0; } if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); break; case CMDBUILTIN: case CMDSPLBLTIN: #ifdef DEBUG trputs(psh, "builtin command: "); trargs(psh, argv); #endif mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { psh->memout.nleft = 0; psh->memout.nextc = psh->memout.buf; psh->memout.bufsize = 64; mode |= REDIR_BACKQ; } e = -1; savehandler = psh->handler; savecmdname = psh->commandname; psh->handler = &jmploc; if (!setjmp(jmploc.loc)) { /* We need to ensure the command hash table isn't * corruped by temporary PATH assignments. * However we must ensure the 'local' command works! */ if (path != pathval(psh) && (cmdentry.u.bltin == hashcmd || cmdentry.u.bltin == typecmd)) { savelocalvars = psh->localvars; psh->localvars = 0; mklocal(psh, path - 5 /* PATH= */, 0); temp_path = 1; } else temp_path = 0; redirect(psh, cmd->ncmd.redirect, mode); /* exec is a special builtin, but needs this list... */ psh->cmdenviron = varlist.list; /* we must check 'readonly' flag for all builtins */ listsetvar(psh, varlist.list, cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); psh->commandname = argv[0]; /* initialize nextopt */ psh->argptr = argv + 1; psh->optptr = NULL; /* and getopt */ #if 0 /** @todo fix getop usage! */ #if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__) optreset = 1; optind = 1; #else optind = 0; /* init */ #endif #endif psh->exitstatus = cmdentry.u.bltin(psh, argc, argv); } else { e = psh->exception; psh->exitstatus = e == EXINT ? SIGINT + 128 : e == EXEXEC ? psh->exerrno : 2; } psh->handler = savehandler; output_flushall(psh); psh->out1 = &psh->output; psh->out2 = &psh->errout; freestdout(psh); if (temp_path) { poplocalvars(psh); psh->localvars = savelocalvars; } psh->cmdenviron = NULL; if (e != EXSHELLPROC) { psh->commandname = savecmdname; if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); } if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.cmdtype == CMDSPLBLTIN) exraise(psh, e); FORCEINTON; } if (cmdentry.u.bltin != execcmd) popredir(psh); if (flags == EV_BACKCMD) { backcmd->buf = psh->memout.buf; backcmd->nleft = (int)(psh->memout.nextc - psh->memout.buf); psh->memout.buf = NULL; } break; default: #ifdef DEBUG trputs(psh, "normal command: "); trargs(psh, argv); #endif clearredir(psh, psh->vforked); redirect(psh, cmd->ncmd.redirect, psh->vforked ? REDIR_VFORK : 0); if (!psh->vforked) for (sp = varlist.list ; sp ; sp = sp->next) setvareq(psh, sp->text, VEXPORT|VSTACK); envp = environment(psh); shellexec(psh, argv, envp, path, cmdentry.u.index, psh->vforked); break; } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ psh->exitstatus = waitforjob(psh, jp); } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; shfile_close(&psh->fdtab, pip[1]); backcmd->jp = jp; } FORCEINTON; out: if (lastarg) /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ setvar(psh, "_", lastarg, 0); popstackmark(psh, &smark); if (eflag(psh) && psh->exitstatus && !(flags & EV_TESTED)) exitshell(psh, psh->exitstatus); }
void evaltree(shinstance *psh, union node *n, int flags) { if (n == NULL) { TRACE((psh, "evaltree(NULL) called\n")); psh->exitstatus = 0; goto out; } #ifndef SMALL psh->displayhist = 1; /* show history substitutions done with fc */ #endif TRACE((psh, "pid %d, evaltree(%p: %d, %d) called\n", sh_getpid(psh), n, n->type, flags)); switch (n->type) { case NSEMI: evaltree(psh, n->nbinary.ch1, flags & EV_TESTED); if (psh->evalskip) goto out; evaltree(psh, n->nbinary.ch2, flags); break; case NAND: evaltree(psh, n->nbinary.ch1, EV_TESTED); if (psh->evalskip || psh->exitstatus != 0) goto out; evaltree(psh, n->nbinary.ch2, flags); break; case NOR: evaltree(psh, n->nbinary.ch1, EV_TESTED); if (psh->evalskip || psh->exitstatus == 0) goto out; evaltree(psh, n->nbinary.ch2, flags); break; case NREDIR: expredir(psh, n->nredir.redirect); redirect(psh, n->nredir.redirect, REDIR_PUSH); evaltree(psh, n->nredir.n, flags); popredir(psh); break; case NSUBSHELL: evalsubshell(psh, n, flags); break; case NBACKGND: evalsubshell(psh, n, flags); break; case NIF: { evaltree(psh, n->nif.test, EV_TESTED); if (psh->evalskip) goto out; if (psh->exitstatus == 0) evaltree(psh, n->nif.ifpart, flags); else if (n->nif.elsepart) evaltree(psh, n->nif.elsepart, flags); else psh->exitstatus = 0; break; } case NWHILE: case NUNTIL: evalloop(psh, n, flags); break; case NFOR: evalfor(psh, n, flags); break; case NCASE: evalcase(psh, n, flags); break; case NDEFUN: defun(psh, n->narg.text, n->narg.next); psh->exitstatus = 0; break; case NNOT: evaltree(psh, n->nnot.com, EV_TESTED); psh->exitstatus = !psh->exitstatus; break; case NPIPE: evalpipe(psh, n); break; case NCMD: evalcommand(psh, n, flags, (struct backcmd *)NULL); break; default: out1fmt(psh, "Node type = %d\n", n->type); flushout(&psh->output); break; } out: if (psh->pendingsigs) dotrap(psh); if ((flags & EV_EXIT) != 0) exitshell(psh, psh->exitstatus); }
void evaltree(union node *n, int flags) { bool do_etest; do_etest = false; if (n == NULL || nflag) { TRACE(("evaltree(%s) called\n", n == NULL ? "NULL" : "-n")); if (nflag == 0) exitstatus = 0; goto out; } #ifndef SMALL displayhist = 1; /* show history substitutions done with fc */ #endif #ifdef NODETYPENAME TRACE(("pid %d, evaltree(%p: %s(%d), %#x) called\n", getpid(), n, NODETYPENAME(n->type), n->type, flags)); #else TRACE(("pid %d, evaltree(%p: %d, %#x) called\n", getpid(), n, n->type, flags)); #endif switch (n->type) { case NSEMI: evaltree(n->nbinary.ch1, flags & EV_TESTED); if (nflag || evalskip) goto out; evaltree(n->nbinary.ch2, flags); break; case NAND: evaltree(n->nbinary.ch1, EV_TESTED); if (nflag || evalskip || exitstatus != 0) goto out; evaltree(n->nbinary.ch2, flags); break; case NOR: evaltree(n->nbinary.ch1, EV_TESTED); if (nflag || evalskip || exitstatus == 0) goto out; evaltree(n->nbinary.ch2, flags); break; case NREDIR: expredir(n->nredir.redirect); redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP); evaltree(n->nredir.n, flags); popredir(); break; case NSUBSHELL: evalsubshell(n, flags); do_etest = !(flags & EV_TESTED); break; case NBACKGND: evalsubshell(n, flags); break; case NIF: { evaltree(n->nif.test, EV_TESTED); if (nflag || evalskip) goto out; if (exitstatus == 0) evaltree(n->nif.ifpart, flags); else if (n->nif.elsepart) evaltree(n->nif.elsepart, flags); else exitstatus = 0; break; } case NWHILE: case NUNTIL: evalloop(n, flags); break; case NFOR: evalfor(n, flags); break; case NCASE: evalcase(n, flags); break; case NDEFUN: defun(n->narg.text, n->narg.next); exitstatus = 0; break; case NNOT: evaltree(n->nnot.com, EV_TESTED); exitstatus = !exitstatus; break; case NPIPE: evalpipe(n); do_etest = !(flags & EV_TESTED); break; case NCMD: evalcommand(n, flags, NULL); do_etest = !(flags & EV_TESTED); break; default: #ifdef NODETYPENAME out1fmt("Node type = %d(%s)\n", n->type, NODETYPENAME(n->type)); #else out1fmt("Node type = %d\n", n->type); #endif flushout(&output); break; } out: if (pendingsigs) dotrap(); if ((flags & EV_EXIT) != 0 || (eflag && exitstatus != 0 && do_etest)) exitshell(exitstatus); }