diff options
Diffstat (limited to 'bin/sh/eval.c')
-rw-r--r-- | bin/sh/eval.c | 106 |
1 files changed, 74 insertions, 32 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 5bae2f2..793248b 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -94,6 +94,7 @@ static void evalsubshell(union node *, int); static void evalredir(union node *, int); static void expredir(union node *); static void evalpipe(union node *); +static int is_valid_fast_cmdsubst(union node *n); static void evalcommand(union node *, int, struct backcmd *); static void prehash(union node *); @@ -110,10 +111,6 @@ RESET { loopnest = 0; funcnest = 0; } - -SHELLPROC { - exitstatus = 0; -} #endif @@ -565,6 +562,19 @@ evalpipe(union node *n) +static int +is_valid_fast_cmdsubst(union node *n) +{ + union node *argp; + + if (n->type != NCMD) + return 0; + for (argp = n->ncmd.args ; argp ; argp = argp->narg.next) + if (expandhassideeffects(argp->narg.text)) + return 0; + return 1; +} + /* * Execute a command inside back quotes. If it's a builtin command, we * want to save its output in a block obtained from malloc. Otherwise @@ -578,6 +588,8 @@ evalbackcmd(union node *n, struct backcmd *result) int pip[2]; struct job *jp; struct stackmark smark; /* unnecessary */ + struct jmploc jmploc; + struct jmploc *savehandler; setstackmark(&smark); result->fd = -1; @@ -588,9 +600,21 @@ evalbackcmd(union node *n, struct backcmd *result) exitstatus = 0; goto out; } - if (n->type == NCMD) { + if (is_valid_fast_cmdsubst(n)) { exitstatus = oexitstatus; - evalcommand(n, EV_BACKCMD, result); + savehandler = handler; + if (setjmp(jmploc.loc)) { + if (exception == EXERROR || exception == EXEXEC) + exitstatus = 2; + else if (exception != 0) { + handler = savehandler; + longjmp(handler->loc, 1); + } + } else { + handler = &jmploc; + evalcommand(n, EV_BACKCMD, result); + } + handler = savehandler; } else { exitstatus = 0; if (pipe(pip) < 0) @@ -615,10 +639,35 @@ out: result->fd, result->buf, result->nleft, result->jp)); } - +/* + * Check if a builtin can safely be executed in the same process, + * even though it should be in a subshell (command substitution). + * Note that jobid, jobs, times and trap can show information not + * available in a child process; this is deliberate. + * The arguments should already have been expanded. + */ +static int +safe_builtin(int idx, int argc, char **argv) +{ + if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || + idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || + idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || + idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || + idx == TYPECMD) + return (1); + if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || + idx == UMASKCMD) + return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); + if (idx == SETCMD) + return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || + argv[1][0] == '+') && argv[1][1] == 'o' && + argv[1][2] == '\0')); + return (0); +} /* * Execute a simple command. + * Note: This may or may not return if (flags & EV_EXIT). */ static void @@ -655,6 +704,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; + jp = NULL; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; @@ -678,7 +728,9 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; - argv = stalloc(sizeof (char *) * (argc + 1)); + /* 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)); @@ -760,7 +812,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) * bookinging effort, since most such runs add * directories in front of the new PATH. */ - clearcmdentry(0); + clearcmdentry(); do_clearcmdentry = 1; } @@ -802,7 +854,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) argc -= 2; } path = _PATH_STDPATH; - clearcmdentry(0); + clearcmdentry(); do_clearcmdentry = 1; } else if (!strcmp(argv[1], "--")) { if (argc == 2) @@ -833,10 +885,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) && ((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 || + !safe_builtin(cmdentry.u.index, argc, argv)))) { jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { @@ -875,14 +925,10 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) reffunc(cmdentry.u.func); savehandler = handler; if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) - freeparam(&saveparam); - else { - freeparam(&shellparam); - shellparam = saveparam; - if (exception == EXERROR || exception == EXEXEC) - popredir(); - } + freeparam(&shellparam); + shellparam = saveparam; + if (exception == EXERROR || exception == EXEXEC) + popredir(); unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; @@ -915,7 +961,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) evalskip = 0; skipcount = 0; } - if (flags & EV_EXIT) + if (jp) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG @@ -947,8 +993,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) */ if (argc == 0 && !(flags & EV_BACKCMD)) cmdentry.special = 1; - if (cmdentry.special) - listsetvar(cmdenviron); + listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); if (argc > 0) bltinsetlocale(); commandname = argv[0]; @@ -964,13 +1009,10 @@ cmddone: out1 = &output; out2 = &errout; freestdout(); - if (e != EXSHELLPROC) { - commandname = savecmdname; - if (flags & EV_EXIT) { - exitshell(exitstatus); - } - } handler = savehandler; + commandname = savecmdname; + if (jp) + exitshell(exitstatus); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; @@ -1019,7 +1061,7 @@ out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) - clearcmdentry(0); + clearcmdentry(); popstackmark(&smark); } |