* SUCH DAMAGE.
*
* @(#)eval.c 8.9 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/eval.c,v 1.53 2006/06/15 07:57:05 stefanf Exp $
- * $DragonFly: src/bin/sh/eval.c,v 1.12 2007/02/04 19:45:24 corecode Exp $
+ * $FreeBSD: src/bin/sh/eval.c,v 1.110 2011/06/16 21:50:28 jilles Exp $
*/
#include <sys/time.h>
#endif
-/* flags in argument to evaltree */
-#define EV_EXIT 01 /* exit after evaluating tree */
-#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
-#define EV_BACKCMD 04 /* command executing within back quotes */
-
int evalskip; /* set if we are skipping commands */
-STATIC int skipcount; /* number of levels to skip */
+static int skipcount; /* number of levels to skip */
MKINIT int loopnest; /* current loop nesting level */
int funcnest; /* depth of function calls */
+static int builtin_flags; /* evalcommand flags for builtins */
const char *commandname;
int oexitstatus; /* saved exit status */
-STATIC void evalloop(union node *, int);
-STATIC void evalfor(union node *, int);
-STATIC void evalcase(union node *, int);
-STATIC void evalsubshell(union node *, int);
-STATIC void expredir(union node *);
-STATIC void evalpipe(union node *);
-STATIC void evalcommand(union node *, int, struct backcmd *);
-STATIC void prehash(union node *);
+static void evalloop(union node *, int);
+static void evalfor(union node *, int);
+static void evalcase(union node *, int);
+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 *);
/*
loopnest = 0;
funcnest = 0;
}
-
-SHELLPROC {
- exitstatus = 0;
-}
#endif
STARTSTACKSTR(concat);
ap = argv + 2;
for (;;) {
- while (*p)
- STPUTC(*p++, concat);
+ STPUTS(p, concat);
if ((p = *ap++) == NULL)
break;
STPUTC(' ', concat);
STPUTC('\0', concat);
p = grabstackstr(concat);
}
- evalstring(p);
- }
+ evalstring(p, builtin_flags);
+ } else
+ exitstatus = 0;
return exitstatus;
}
*/
void
-evalstring(char *s)
+evalstring(char *s, int flags)
{
union node *n;
struct stackmark smark;
+ int flags_exit;
+ int any;
+ flags_exit = flags & EV_EXIT;
+ flags &= ~EV_EXIT;
+ any = 0;
setstackmark(&smark);
setinputstring(s, 1);
while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, 0);
+ if (n != NULL && !nflag) {
+ if (flags_exit && preadateof())
+ evaltree(n, flags | EV_EXIT);
+ else
+ evaltree(n, flags);
+ any = 1;
+ }
popstackmark(&smark);
}
popfile();
popstackmark(&smark);
+ if (!any)
+ exitstatus = 0;
+ if (flags_exit)
+ exraise(EXEXIT);
}
-
/*
* Evaluate a parse tree. The value is left in the global variable
* exitstatus.
evaltree(union node *n, int flags)
{
int do_etest;
+ union node *next;
do_etest = 0;
if (n == NULL) {
exitstatus = 0;
goto out;
}
+ do {
+ next = NULL;
#ifndef NO_HISTORY
- displayhist = 1; /* show history substitutions done with fc */
+ 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;
+ 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;
+ next = n->nbinary.ch2;
+ break;
+ case NAND:
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip || exitstatus != 0) {
+ goto out;
+ }
+ next = n->nbinary.ch2;
+ break;
+ case NOR:
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip || exitstatus == 0)
+ goto out;
+ next = n->nbinary.ch2;
+ break;
+ case NREDIR:
+ evalredir(n, flags);
+ 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)
+ next = n->nif.ifpart;
+ else if (n->nif.elsepart)
+ next = n->nif.elsepart;
+ else
+ exitstatus = 0;
+ break;
}
- 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:
- 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
+ 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 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, NULL);
- do_etest = !(flags & EV_TESTED);
- break;
- default:
- out1fmt("Node type = %d\n", n->type);
- flushout(&output);
- break;
- }
+ 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:
+ out1fmt("Node type = %d\n", n->type);
+ flushout(&output);
+ break;
+ }
+ n = next;
+ } while (n != NULL);
out:
if (pendingsigs)
dotrap();
- if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest))
+ if (eflag && exitstatus != 0 && do_etest)
exitshell(exitstatus);
+ if (flags & EV_EXIT)
+ exraise(EXEXIT);
}
-STATIC void
+static void
evalloop(union node *n, int flags)
{
int status;
}
if (evalskip == SKIPBREAK && --skipcount <= 0)
evalskip = 0;
+ if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
+ status = exitstatus;
break;
}
if (n->type == NWHILE) {
-STATIC void
+static void
evalfor(union node *n, int flags)
{
struct arglist arglist;
-STATIC void
+static void
evalcase(union node *n, int flags)
{
union node *cp;
setstackmark(&smark);
arglist.lastp = &arglist.list;
oexitstatus = exitstatus;
+ exitstatus = 0;
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
* Kick off a subshell to evaluate a tree.
*/
-STATIC void
+static void
evalsubshell(union node *n, int flags)
{
struct job *jp;
int backgnd = (n->type == NBACKGND);
+ oexitstatus = exitstatus;
expredir(n->nredir.redirect);
- jp = makejob(n, 1);
- if (forkshell(jp, n, backgnd) == 0) {
+ 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 */
- }
- if (! backgnd) {
+ } else if (!backgnd) {
INTOFF;
exitstatus = waitforjob(jp, NULL);
INTON;
- }
+ } else
+ exitstatus = 0;
}
+/*
+ * Evaluate a redirected compound command.
+ */
+
+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;
+}
+
/*
* Compute the names of the files in a redirection list.
*/
-STATIC void
+static void
expredir(union node *n)
{
union node *redir;
for (redir = n ; redir ; redir = redir->nfile.next) {
struct arglist fn;
fn.lastp = &fn.list;
- oexitstatus = exitstatus;
switch (redir->type) {
case NFROM:
case NTO:
case NFROMFD:
case NTOFD:
if (redir->ndup.vname) {
- expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+ expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
fixredir(redir, fn.list->text, 1);
}
break;
* of all the rest.)
*/
-STATIC void
+static void
evalpipe(union node *n)
{
struct job *jp;
if (prevfd >= 0)
close(prevfd);
prevfd = pip[0];
- close(pip[1]);
+ if (pip[1] != -1)
+ close(pip[1]);
}
INTON;
if (n->npipe.backgnd == 0) {
exitstatus = waitforjob(jp, NULL);
TRACE(("evalpipe: job done exit status %d\n", exitstatus));
INTON;
- }
+ } else
+ exitstatus = 0;
}
+static int
+is_valid_fast_cmdsubst(union node *n)
+{
+
+ return (n->type == NCMD);
+}
+
/*
* 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
int pip[2];
struct job *jp;
struct stackmark smark; /* unnecessary */
+ struct jmploc jmploc;
+ struct jmploc *savehandler;
+ struct localvar *savelocalvars;
setstackmark(&smark);
result->fd = -1;
exitstatus = 0;
goto out;
}
- if (n->type == NCMD) {
+ if (is_valid_fast_cmdsubst(n)) {
exitstatus = oexitstatus;
- evalcommand(n, EV_BACKCMD, result);
+ savelocalvars = localvars;
+ localvars = NULL;
+ forcelocal++;
+ savehandler = handler;
+ if (setjmp(jmploc.loc)) {
+ if (exception == EXERROR || exception == EXEXEC)
+ exitstatus = 2;
+ else if (exception != 0) {
+ handler = savehandler;
+ forcelocal--;
+ poplocalvars();
+ localvars = savelocalvars;
+ longjmp(handler->loc, 1);
+ }
+ } else {
+ handler = &jmploc;
+ evalcommand(n, EV_BACKCMD, result);
+ }
+ handler = savehandler;
+ forcelocal--;
+ poplocalvars();
+ localvars = savelocalvars;
} else {
exitstatus = 0;
if (pipe(pip) < 0)
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
+static void
evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
{
struct stackmark smark;
int mode;
int pip[2];
struct cmdentry cmdentry;
- struct job *jp;
+ struct job *volatile jp;
struct jmploc jmploc;
- struct jmploc *volatile savehandler = NULL;
- const char *volatile savecmdname;
- volatile struct shparam saveparam;
- struct localvar *volatile savelocalvars;
+ struct jmploc *savehandler;
+ const char *savecmdname;
+ struct shparam saveparam;
+ struct localvar *savelocalvars;
+ struct parsefile *savetopfile;
volatile int e;
char *volatile lastarg;
int realstatus;
volatile 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) {
- 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;
- }
+ if (varflag && isassignment(argp->narg.text)) {
+ expandarg(argp, &varlist, EXP_VARTILDE);
+ continue;
}
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
varflag = 0;
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));
/* Print the command if xflag is set. */
if (xflag) {
char sep = 0;
- out2str(ps4val());
+ const char *p, *ps4;
+ ps4 = expandstr(ps4val());
+ out2str(ps4 != NULL ? ps4 : ps4val());
for (sp = varlist.list ; sp ; sp = sp->next) {
if (sep != 0)
- outc(' ', &errout);
- out2str(sp->text);
+ 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)
- outc(' ', &errout);
- out2str(sp->text);
+ 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 = ' ';
}
- outc('\n', &errout);
+ out2c('\n');
flushout(&errout);
}
/* Variable assignment(s) without command */
cmdentry.cmdtype = CMDBUILTIN;
cmdentry.u.index = BLTINCMD;
- cmdentry.special = 1;
+ cmdentry.special = 0;
} else {
static const char PATH[] = "PATH=";
- const char *path = pathval();
+ int cmd_flags = 0, bltinonly = 0;
/*
* Modify the command lookup path, if a PATH= assignment
* bookinging effort, since most such runs add
* directories in front of the new PATH.
*/
- clearcmdentry(0);
+ clearcmdentry();
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)
+ for (;;) {
+ if (bltinonly) {
+ cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
+ if (cmdentry.u.index < 0) {
+ cmdentry.u.index = BLTINCMD;
+ argv--;
+ argc++;
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)
+ } 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 (cmd->ncmd.backgnd
- || (cmdentry.cmdtype == CMDNORMAL
- && ((flags & EV_EXIT) == 0 || Tflag))
+ || ((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 &&
- cmdentry.u.index == COMMANDCMD)) {
+ && (cmdentry.cmdtype != CMDBUILTIN ||
+ !safe_builtin(cmdentry.u.index, argc, argv)))) {
jp = makejob(cmd, 1);
mode = cmd->ncmd.backgnd;
if (flags & EV_BACKCMD) {
dup2(pip[1], 1);
close(pip[1]);
}
+ flags &= ~EV_BACKCMD;
}
flags |= EV_EXIT;
}
#ifdef DEBUG
trputs("Shell function: "); trargs(argv);
#endif
- redirect(cmd->ncmd.redirect, REDIR_PUSH);
saveparam = shellparam;
shellparam.malloc = 0;
shellparam.reset = 1;
INTOFF;
savelocalvars = localvars;
localvars = NULL;
- INTON;
+ reffunc(cmdentry.u.func);
+ savehandler = handler;
if (setjmp(jmploc.loc)) {
- if (exception == EXSHELLPROC)
- freeparam(&saveparam);
- else {
- freeparam(&shellparam);
- shellparam = saveparam;
- }
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ popredir();
+ unreffunc(cmdentry.u.func);
poplocalvars();
localvars = savelocalvars;
+ funcnest--;
handler = savehandler;
longjmp(handler->loc, 1);
}
- savehandler = handler;
handler = &jmploc;
+ funcnest++;
+ redirect(cmd->ncmd.redirect, REDIR_PUSH);
+ INTON;
for (sp = varlist.list ; sp ; sp = sp->next)
mklocal(sp->text);
- funcnest++;
- if (flags & EV_TESTED)
- evaltree(cmdentry.u.func, EV_TESTED);
- else
- evaltree(cmdentry.u.func, 0);
- funcnest--;
+ 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 (flags & EV_EXIT)
+ if (jp)
exitshell(exitstatus);
} else if (cmdentry.cmdtype == CMDBUILTIN) {
#ifdef DEBUG
memout.nextc = memout.buf;
memout.bufsize = 64;
mode |= REDIR_BACKQ;
+ cmdentry.special = 0;
}
savecmdname = commandname;
+ savetopfile = getcurrentfile();
cmdenviron = varlist.list;
e = -1;
+ savehandler = handler;
if (setjmp(jmploc.loc)) {
e = exception;
- exitstatus = (e == EXINT)? SIGINT+128 : 2;
+ if (e == EXINT)
+ exitstatus = SIGINT+128;
+ else if (e != EXEXIT)
+ exitstatus = 2;
goto cmddone;
}
- savehandler = handler;
handler = &jmploc;
redirect(cmd->ncmd.redirect, mode);
- if (cmdentry.special)
- listsetvar(cmdenviron);
+ /*
+ * If there is no command word, redirection errors should
+ * not be fatal but assignment errors should.
+ */
+ if (argc == 0 && !(flags & EV_BACKCMD))
+ cmdentry.special = 1;
+ listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
+ if (argc > 0)
+ bltinsetlocale();
commandname = argv[0];
argptr = argv + 1;
- optptr = NULL; /* initialize nextopt */
- optind = 1; /* and getopt */
- optreset = 1;
+ nextopt_optptr = NULL; /* initialize nextopt */
+ builtin_flags = flags;
exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
flushall();
cmddone:
+ if (argc > 0)
+ bltinunsetlocale();
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();
+ 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
- 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);
+ shellexec(argv, envp, path, cmdentry.u.index);
/*NOTREACHED*/
}
goto out;
parent: /* parent process gets here (if we forked) */
- if (mode == 0) { /* argument to fork */
+ if (mode == FORK_FG) { /* argument to fork */
INTOFF;
exitstatus = waitforjob(jp, &realstatus);
INTON;
evalskip = SKIPBREAK;
skipcount = loopnest;
}
- } else if (mode == 2) {
+ } else if (mode == FORK_NOJOB) {
backcmd->fd = pip[0];
close(pip[1]);
backcmd->jp = jp;
- }
+ } else
+ exitstatus = 0;
out:
if (lastarg)
setvar("_", lastarg, 0);
if (do_clearcmdentry)
- clearcmdentry(0);
+ clearcmdentry();
popstackmark(&smark);
}
* check that the name will not be subject to expansion.
*/
-STATIC void
+static void
prehash(union node *n)
{
struct cmdentry entry;
*/
/*
- * No command given, or a bltin command with no arguments.
+ * No command given, a bltin command with no arguments, or a bltin command
+ * with an invalid name.
*/
int
-bltincmd(int argc __unused, char **argv __unused)
+bltincmd(int argc, char **argv)
{
+ if (argc > 1) {
+ out2fmt_flush("%s: not found\n", argv[1]);
+ return 127;
+ }
/*
* Preserve exitstatus of a previous possible redirection
* as POSIX mandates
int
commandcmd(int argc, char **argv)
{
- static char stdpath[] = _PATH_STDPATH;
- struct jmploc loc, *old;
- struct strlist *sp;
- const char *volatile path;
+ const char *path;
int ch;
int cmd = -1;
- for (sp = cmdenviron; sp ; sp = sp->next)
- setvareq(sp->text, VEXPORT|VSTACK);
- path = pathval();
+ path = bltinlookup("PATH", 1);
optind = optreset = 1;
opterr = 0;
while ((ch = getopt(argc, argv, "pvV")) != -1) {
switch (ch) {
case 'p':
- path = stdpath;
+ path = _PATH_STDPATH;
break;
case 'v':
cmd = TYPECMD_SMALLV;
if (cmd != -1) {
if (argc != 1)
error("wrong number of arguments");
- return typecmd_impl(2, argv - 1, cmd);
- }
- if (argc != 0) {
- old = handler;
- handler = &loc;
- if (setjmp(handler->loc) == 0)
- shellexec(argv, environment(), path, 0);
- handler = old;
- if (exception == EXEXEC)
- exit(exerrno);
- exraise(exception);
+ return typecmd_impl(2, argv - 1, cmd, path);
}
+ if (argc != 0)
+ error("commandcmd bad call");
/*
* Do nothing successfully if no command was specified;
* ksh also does this.
*/
- exit(0);
+ return(0);
}
int
execcmd(int argc, char **argv)
{
+ /*
+ * Because we have historically not supported any options,
+ * only treat "--" specially.
+ */
+ if (argc > 1 && strcmp(argv[1], "--") == 0)
+ argc--, argv++;
if (argc > 1) {
struct strlist *sp;