blob: d10be38653b9c9bfe8a7e3c7d192afdf607f0678 [file] [log] [blame]
/*-
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1997-2005
* Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
/*
* Evaluate a command.
*/
#include "init.h"
#include "main.h"
#include "shell.h"
#include "nodes.h"
#include "syntax.h"
#include "expand.h"
#include "parser.h"
#include "jobs.h"
#include "eval.h"
#include "builtins.h"
#include "options.h"
#include "exec.h"
#include "redir.h"
#include "input.h"
#include "output.h"
#include "trap.h"
#include "var.h"
#include "memalloc.h"
#include "error.h"
#include "show.h"
#include "mystring.h"
#ifndef SMALL
#include "myhistedit.h"
#endif
int evalskip; /* set if we are skipping commands */
STATIC int skipcount; /* number of levels to skip */
MKINIT int loopnest; /* current loop nesting level */
static int funcline; /* starting line number of current function, or 0 if not in a function */
char *commandname;
int exitstatus; /* exit status of last command */
int back_exitstatus; /* exit status of backquoted command */
int savestatus = -1; /* exit status of last command outside traps */
/* Prevent PS4 nesting. */
MKINIT int inps4;
#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
STATIC
#endif
void evaltreenr(union node *, int) __attribute__ ((__noreturn__));
STATIC int evalloop(union node *, int);
STATIC int evalfor(union node *, int);
STATIC int evalcase(union node *, int);
STATIC int evalsubshell(union node *, int);
STATIC void expredir(union node *);
STATIC int evalpipe(union node *, int);
#ifdef notyet
STATIC int evalcommand(union node *, int, struct backcmd *);
#else
STATIC int evalcommand(union node *, int);
#endif
STATIC int evalbltin(const struct builtincmd *, int, char **, int);
STATIC int evalfun(struct funcnode *, int, char **, int);
STATIC void prehash(union node *);
STATIC int eprintlist(struct output *, struct strlist *, int);
STATIC int bltincmd(int, char **);
STATIC const struct builtincmd bltin = {
.name = nullstr,
.builtin = bltincmd,
.flags = BUILTIN_REGULAR,
};
/*
* Called to reset things after an exception.
*/
#ifdef mkinit
INCLUDE "eval.h"
EXITRESET {
if (savestatus >= 0) {
if (exception == EXEXIT || evalskip == SKIPFUNCDEF)
exitstatus = savestatus;
savestatus = -1;
}
evalskip = 0;
loopnest = 0;
inps4 = 0;
}
#endif
/*
* The eval commmand.
*/
static int evalcmd(int argc, char **argv, int flags)
{
char *p;
char *concat;
char **ap;
if (argc > 1) {
p = argv[1];
if (argc > 2) {
STARTSTACKSTR(concat);
ap = argv + 2;
for (;;) {
concat = stputs(p, concat);
if ((p = *ap++) == NULL)
break;
STPUTC(' ', concat);
}
STPUTC('\0', concat);
p = grabstackstr(concat);
}
return evalstring(p, flags & EV_TESTED);
}
return 0;
}
/*
* Execute a command or commands contained in a string.
*/
int
evalstring(char *s, int flags)
{
union node *n;
struct stackmark smark;
int status;
s = sstrdup(s);
setinputstring(s);
setstackmark(&smark);
status = 0;
for (; (n = parsecmd(0)) != NEOF; popstackmark(&smark)) {
int i;
i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
if (n)
status = i;
if (evalskip)
break;
}
popstackmark(&smark);
popfile();
stunalloc(s);
return status;
}
/*
* Evaluate a parse tree. The value is left in the global variable
* exitstatus.
*/
int
evaltree(union node *n, int flags)
{
int checkexit = 0;
int (*evalfn)(union node *, int);
struct stackmark smark;
unsigned isor;
int status = 0;
setstackmark(&smark);
if (n == NULL) {
TRACE(("evaltree(NULL) called\n"));
goto out;
}
dotrap();
#ifndef SMALL
displayhist = 1; /* show history substitutions done with fc */
#endif
TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
getpid(), n, n->type, flags));
switch (n->type) {
default:
#ifdef DEBUG
out1fmt("Node type = %d\n", n->type);
#ifndef USE_GLIBC_STDIO
flushout(out1);
#endif
break;
#endif
case NNOT:
status = !evaltree(n->nnot.com, EV_TESTED);
goto setstatus;
case NREDIR:
errlinno = lineno = n->nredir.linno;
if (funcline)
lineno -= funcline - 1;
expredir(n->nredir.redirect);
pushredir(n->nredir.redirect);
status = redirectsafe(n->nredir.redirect, REDIR_PUSH) ?:
evaltree(n->nredir.n, flags & EV_TESTED);
if (n->nredir.redirect)
popredir(0);
goto setstatus;
case NCMD:
#ifdef notyet
if (eflag && !(flags & EV_TESTED))
checkexit = ~0;
status = evalcommand(n, flags, (struct backcmd *)NULL);
goto setstatus;
#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;
goto checkexit;
case NPIPE:
evalfn = evalpipe;
goto checkexit;
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;
status = evaltree(n->nbinary.ch1,
(flags | ((isor >> 1) - 1)) & EV_TESTED);
if ((!status) == isor || evalskip)
break;
n = n->nbinary.ch2;
evaln:
evalfn = evaltree;
calleval:
status = evalfn(n, flags);
goto setstatus;
case NIF:
status = evaltree(n->nif.test, EV_TESTED);
if (evalskip)
break;
if (!status) {
n = n->nif.ifpart;
goto evaln;
} else if (n->nif.elsepart) {
n = n->nif.elsepart;
goto evaln;
}
status = 0;
goto setstatus;
case NDEFUN:
defun(n);
setstatus:
exitstatus = status;
break;
}
out:
dotrap();
if (checkexit & status)
goto exexit;
if (flags & EV_EXIT) {
exexit:
exraise(EXEND);
}
popstackmark(&smark);
return exitstatus;
}
#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
STATIC
#endif
void evaltreenr(union node *n, int flags)
#ifdef HAVE_ATTRIBUTE_ALIAS
__attribute__ ((alias("evaltree")));
#else
{
evaltree(n, flags);
abort();
}
#endif
static int skiploop(void)
{
int skip = evalskip;
switch (skip) {
case 0:
break;
case SKIPBREAK:
case SKIPCONT:
if (likely(--skipcount <= 0)) {
evalskip = 0;
break;
}
skip = SKIPBREAK;
break;
}
return skip;
}
STATIC int
evalloop(union node *n, int flags)
{
int skip;
int status;
loopnest++;
status = 0;
flags &= EV_TESTED;
do {
int i;
i = evaltree(n->nbinary.ch1, EV_TESTED);
skip = skiploop();
if (skip == SKIPFUNC)
status = i;
if (skip)
continue;
if (n->type != NWHILE)
i = !i;
if (i != 0)
break;
status = evaltree(n->nbinary.ch2, flags);
skip = skiploop();
} while (!(skip & ~SKIPCONT));
loopnest--;
return status;
}
STATIC int
evalfor(union node *n, int flags)
{
struct arglist arglist;
union node *argp;
struct strlist *sp;
int status;
errlinno = lineno = n->nfor.linno;
if (funcline)
lineno -= funcline - 1;
arglist.lastp = &arglist.list;
for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
}
*arglist.lastp = NULL;
status = 0;
loopnest++;
flags &= EV_TESTED;
for (sp = arglist.list ; sp ; sp = sp->next) {
setvar(n->nfor.var, sp->text, 0);
status = evaltree(n->nfor.body, flags);
if (skiploop() & ~SKIPCONT)
break;
}
loopnest--;
return status;
}
STATIC int
evalcase(union node *n, int flags)
{
union node *cp;
union node *patp;
struct arglist arglist;
int status = 0;
errlinno = lineno = n->ncase.linno;
if (funcline)
lineno -= funcline - 1;
arglist.lastp = &arglist.list;
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) {
if (casematch(patp, arglist.list->text)) {
/* Ensure body is non-empty as otherwise
* EV_EXIT may prevent us from setting the
* exit status.
*/
if (evalskip == 0 && cp->nclist.body) {
status = evaltree(cp->nclist.body,
flags);
}
goto out;
}
}
}
out:
return status;
}
/*
* Kick off a subshell to evaluate a tree.
*/
STATIC int
evalsubshell(union node *n, int flags)
{
struct job *jp;
int backgnd = (n->type == NBACKGND);
int status;
errlinno = lineno = n->nredir.linno;
if (funcline)
lineno -= funcline - 1;
expredir(n->nredir.redirect);
INTOFF;
if (!backgnd && flags & EV_EXIT && !have_traps()) {
forkreset();
goto nofork;
}
jp = makejob(n, 1);
if (forkshell(jp, n, backgnd) == 0) {
flags |= EV_EXIT;
if (backgnd)
flags &=~ EV_TESTED;
nofork:
INTON;
redirect(n->nredir.redirect, 0);
evaltreenr(n->nredir.n, flags);
/* never returns */
}
status = 0;
if (! backgnd)
status = waitforjob(jp);
INTON;
return status;
}
/*
* Compute the names of the files in a redirection list.
*/
STATIC void
expredir(union node *n)
{
union node *redir;
for (redir = n ; redir ; redir = redir->nfile.next) {
struct arglist fn;
fn.lastp = &fn.list;
switch (redir->type) {
case NFROMTO:
case NFROM:
case NTO:
case NCLOBBER:
case NAPPEND:
expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
redir->nfile.expfname = fn.list->text;
break;
case NFROMFD:
case NTOFD:
if (redir->ndup.vname) {
expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
fixredir(redir, fn.list->text, 1);
}
break;
}
}
}
/*
* Evaluate a pipeline. All the processes in the pipeline are children
* of the process creating the pipeline. (This differs from some versions
* of the shell, which make the last process in a pipeline the parent
* of all the rest.)
*/
STATIC int
evalpipe(union node *n, int flags)
{
struct job *jp;
struct nodelist *lp;
int pipelen;
int prevfd;
int pip[2];
int status = 0;
TRACE(("evalpipe(0x%lx) called\n", (long)n));
pipelen = 0;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
pipelen++;
flags |= EV_EXIT;
INTOFF;
jp = makejob(n, pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
prehash(lp->n);
pip[1] = -1;
if (lp->next) {
if (pipe(pip) < 0) {
close(prevfd);
sh_error("Pipe call failed");
}
}
if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
INTON;
if (pip[1] >= 0) {
close(pip[0]);
}
if (prevfd > 0) {
dup2(prevfd, 0);
close(prevfd);
}
if (pip[1] > 1) {
dup2(pip[1], 1);
close(pip[1]);
}
evaltreenr(lp->n, flags);
/* never returns */
}
if (prevfd >= 0)
close(prevfd);
prevfd = pip[0];
close(pip[1]);
}
if (n->npipe.backgnd == 0) {
status = waitforjob(jp);
TRACE(("evalpipe: job done exit status %d\n", status));
}
INTON;
return status;
}
/*
* 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
* we fork off a subprocess and get the output of the command via a pipe.
* Should be called with interrupts off.
*/
void
evalbackcmd(union node *n, struct backcmd *result)
{
int pip[2];
struct job *jp;
result->fd = -1;
result->buf = NULL;
result->nleft = 0;
result->jp = NULL;
if (n == NULL) {
goto out;
}
if (pipe(pip) < 0)
sh_error("Pipe call failed");
jp = makejob(n, 1);
if (forkshell(jp, n, FORK_NOJOB) == 0) {
FORCEINTON;
close(pip[0]);
if (pip[1] != 1) {
dup2(pip[1], 1);
close(pip[1]);
}
ifsfree();
evaltreenr(n, EV_EXIT);
/* NOTREACHED */
}
close(pip[1]);
result->fd = pip[0];
result->jp = jp;
out:
TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
result->fd, result->buf, result->nleft, result->jp));
}
static struct strlist *fill_arglist(struct arglist *arglist,
union node **argpp)
{
struct strlist **lastp = arglist->lastp;
union node *argp;
while ((argp = *argpp)) {
expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
*argpp = argp->narg.next;
if (*lastp)
break;
}
return *lastp;
}
static int parse_command_args(struct arglist *arglist, union node **argpp,
const char **path)
{
struct strlist *sp = arglist->list;
char *cp, c;
for (;;) {
sp = unlikely(sp->next) ? sp->next :
fill_arglist(arglist, argpp);
if (!sp)
return 0;
cp = sp->text;
if (*cp++ != '-')
break;
if (!(c = *cp++))
break;
if (c == '-' && !*cp) {
if (likely(!sp->next) && !fill_arglist(arglist, argpp))
return 0;
sp = sp->next;
break;
}
do {
switch (c) {
case 'p':
*path = defpath;
break;
default:
/* run 'typecmd' for other options */
return 0;
}
} while ((c = *cp++));
}
arglist->list = sp;
return DO_NOFUNC;
}
/*
* Execute a simple command.
*/
STATIC int
#ifdef notyet
evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
#else
evalcommand(union node *cmd, int flags)
#endif
{
struct localvar_list *localvar_stop;
struct parsefile *file_stop;
struct redirtab *redir_stop;
union node *argp;
struct arglist arglist;
struct arglist varlist;
char **argv;
int argc;
struct strlist *osp;
struct strlist *sp;
#ifdef notyet
int pip[2];
#endif
struct cmdentry cmdentry;
struct job *jp;
char *lastarg;
const char *path;
int spclbltin;
int cmd_flag;
int execcmd;
int status;
char **nargv;
int vflags;
int vlocal;
errlinno = lineno = cmd->ncmd.linno;
if (funcline)
lineno -= funcline - 1;
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
file_stop = parsefile;
back_exitstatus = 0;
cmdentry.cmdtype = CMDBUILTIN;
cmdentry.u.cmd = &bltin;
varlist.lastp = &varlist.list;
*varlist.lastp = NULL;
arglist.lastp = &arglist.list;
*arglist.lastp = NULL;
cmd_flag = 0;
execcmd = 0;
spclbltin = -1;
vflags = 0;
vlocal = 0;
path = NULL;
argc = 0;
argp = cmd->ncmd.args;
if ((osp = fill_arglist(&arglist, &argp))) {
int pseudovarflag = 0;
for (;;) {
find_command(arglist.list->text, &cmdentry,
cmd_flag | DO_REGBLTIN, pathval());
vlocal++;
/* implement bltin and command here */
if (cmdentry.cmdtype != CMDBUILTIN)
break;
pseudovarflag = cmdentry.u.cmd->flags & BUILTIN_ASSIGN;
if (likely(spclbltin < 0)) {
spclbltin =
cmdentry.u.cmd->flags &
BUILTIN_SPECIAL
;
vlocal = spclbltin ^ BUILTIN_SPECIAL;
}
execcmd = cmdentry.u.cmd == EXECCMD;
if (likely(cmdentry.u.cmd != COMMANDCMD))
break;
cmd_flag = parse_command_args(&arglist, &argp, &path);
if (!cmd_flag)
break;
}
for (; argp; argp = argp->narg.next)
expandarg(argp, &arglist,
pseudovarflag &&
isassignment(argp->narg.text) ?
EXP_VARTILDE : EXP_FULL | EXP_TILDE);
for (sp = arglist.list; sp; sp = sp->next)
argc++;
if (execcmd && argc > 1)
vflags = VEXPORT;
}
localvar_stop = pushlocalvars(vlocal);
/* Reserve one extra spot at the front for shellexec. */
nargv = stalloc(sizeof (char *) * (argc + 2));
argv = ++nargv;
for (sp = arglist.list ; sp ; sp = sp->next) {
TRACE(("evalcommand arg: %s\n", sp->text));
*nargv++ = sp->text;
}
*nargv = NULL;
lastarg = NULL;
if (iflag && funcline == 0 && argc > 0)
lastarg = nargv[-1];
preverrout.fd = 2;
expredir(cmd->ncmd.redirect);
redir_stop = pushredir(cmd->ncmd.redirect);
status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
if (unlikely(status)) {
bail:
exitstatus = status;
/* We have a redirection error. */
if (spclbltin > 0)
exraise(EXERROR);
goto out;
}
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
struct strlist **spp;
spp = varlist.lastp;
expandarg(argp, &varlist, EXP_VARTILDE);
if (vlocal)
mklocal((*spp)->text, VEXPORT);
else
setvareq((*spp)->text, vflags);
}
/* Print the command if xflag is set. */
if (xflag && !inps4) {
struct output *out;
int sep;
out = &preverrout;
inps4 = 1;
outstr(expandstr(ps4val()), out);
inps4 = 0;
sep = 0;
sep = eprintlist(out, varlist.list, sep);
eprintlist(out, osp, sep);
outcslow('\n', out);
#ifdef FLUSHERR
flushout(out);
#endif
}
/* Now locate the command. */
if (cmdentry.cmdtype != CMDBUILTIN ||
!(cmdentry.u.cmd->flags & BUILTIN_REGULAR)) {
path = unlikely(path) ? path : pathval();
find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
}
jp = NULL;
/* Execute the command. */
switch (cmdentry.cmdtype) {
case CMDUNKNOWN:
status = 127;
#ifdef FLUSHERR
flushout(&errout);
#endif
goto bail;
default:
/* Fork off a child process if necessary. */
if (!(flags & EV_EXIT) || have_traps()) {
INTOFF;
jp = vforkexec(cmd, argv, path, cmdentry.u.index);
break;
}
shellexec(argv, path, cmdentry.u.index);
/* NOTREACHED */
case CMDBUILTIN:
if (evalbltin(cmdentry.u.cmd, argc, argv, flags) &&
!(exception == EXERROR && spclbltin <= 0)) {
raise:
longjmp(handler->loc, 1);
}
break;
case CMDFUNCTION:
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
break;
}
status = waitforjob(jp);
FORCEINTON;
out:
if (cmd->ncmd.redirect)
popredir(execcmd);
unwindredir(redir_stop);
unwindfiles(file_stop);
unwindlocalvars(localvar_stop);
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("_", lastarg, 0);
return status;
}
STATIC int
evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
{
char *volatile savecmdname;
struct jmploc *volatile savehandler;
struct jmploc jmploc;
int status;
int i;
savecmdname = commandname;
savehandler = handler;
if ((i = setjmp(jmploc.loc)))
goto cmddone;
handler = &jmploc;
commandname = argv[0];
argptr = argv + 1;
optptr = NULL; /* initialize nextopt */
if (cmd == EVALCMD)
status = evalcmd(argc, argv, flags);
else
status = (*cmd->builtin)(argc, argv);
flushall();
if (outerr(out1))
sh_warnx("%s: I/O error", commandname);
status |= outerr(out1);
exitstatus = status;
cmddone:
freestdout();
commandname = savecmdname;
handler = savehandler;
return i;
}
STATIC int
evalfun(struct funcnode *func, int argc, char **argv, int flags)
{
volatile struct shparam saveparam;
struct jmploc *volatile savehandler;
struct jmploc jmploc;
int e;
int savefuncline;
int saveloopnest;
saveparam = shellparam;
savefuncline = funcline;
saveloopnest = loopnest;
savehandler = handler;
if ((e = setjmp(jmploc.loc))) {
goto funcdone;
}
INTOFF;
handler = &jmploc;
shellparam.malloc = 0;
func->count++;
funcline = func->n.ndefun.linno;
loopnest = 0;
INTON;
shellparam.nparam = argc - 1;
shellparam.p = argv + 1;
shellparam.optind = 1;
shellparam.optoff = -1;
evaltree(func->n.ndefun.body, flags & EV_TESTED);
funcdone:
INTOFF;
loopnest = saveloopnest;
funcline = savefuncline;
freefunc(func);
freeparam(&shellparam);
shellparam = saveparam;
handler = savehandler;
INTON;
evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
return e;
}
/*
* Search for a command. This is called before we fork so that the
* location of the command will be available in the parent as well as
* the child. The check for "goodname" is an overly conservative
* check that the name will not be subject to expansion.
*/
STATIC void
prehash(union node *n)
{
struct cmdentry entry;
if (n->type == NCMD && n->ncmd.args)
if (goodname(n->ncmd.args->narg.text))
find_command(n->ncmd.args->narg.text, &entry, 0,
pathval());
}
/*
* Builtin commands. Builtin commands whose functions are closely
* tied to evaluation are implemented here.
*/
/*
* No command given.
*/
STATIC int
bltincmd(int argc, char **argv)
{
/*
* Preserve exitstatus of a previous possible redirection
* as POSIX mandates
*/
return back_exitstatus;
}
/*
* Handle break and continue commands. Break, continue, and return are
* all handled by setting the evalskip flag. The evaluation routines
* above all check this flag, and if it is set they start skipping
* commands rather than executing them. The variable skipcount is
* the number of loops to break/continue, or the number of function
* levels to return. (The latter is always 1.) It should probably
* be an error to break out of more loops than exist, but it isn't
* in the standard shell so we don't make it one here.
*/
int
breakcmd(int argc, char **argv)
{
int n = argc > 1 ? number(argv[1]) : 1;
if (n <= 0)
badnum(argv[1]);
if (n > loopnest)
n = loopnest;
if (n > 0) {
evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
skipcount = n;
}
return 0;
}
/*
* The return command.
*/
int
returncmd(int argc, char **argv)
{
int skip;
int status;
/*
* If called outside a function, do what ksh does;
* skip the rest of the file.
*/
if (argv[1]) {
skip = SKIPFUNC;
status = number(argv[1]);
} else {
skip = SKIPFUNCDEF;
status = exitstatus;
}
evalskip = skip;
return status;
}
int
falsecmd(int argc, char **argv)
{
return 1;
}
int
truecmd(int argc, char **argv)
{
return 0;
}
int
execcmd(int argc, char **argv)
{
if (argc > 1) {
iflag = 0; /* exit on error */
mflag = 0;
optschanged();
shellexec(argv + 1, pathval(), 0);
}
return 0;
}
STATIC int
eprintlist(struct output *out, struct strlist *sp, int sep)
{
while (sp) {
const char *p;
p = " %s";
p += (1 - sep);
sep |= 1;
outfmt(out, p, sp->text);
sp = sp->next;
}
return sep;
}