* Copyright (c) 1991 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)eval.c 5.1 (Berkeley) %G%";
/* 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 */
/* reasons for skipping commands (see comment on breakcmd routine) */
MKINIT
int evalskip
; /* set if we are skipping commands */
STATIC
int skipcount
; /* number of levels to skip */
MKINIT
int loopnest
; /* current loop nesting level */
int funcnest
; /* depth of function calls */
struct strlist
*cmdenviron
;
int exitstatus
; /* exit status of last command */
STATIC
void evalloop(union node
*);
STATIC
void evalfor(union node
*);
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 evalsubshell();
STATIC
void evalcommand();
* Called to reset things after an exception.
* The eval builtin. Do you want clean, straight-forward semantics for
* your eval command? If so, read on....
evalcmd(argc
, argv
) char **argv
; {
for (ap
= argv
+ 1 ; *ap
; ap
++) {
* If, on the other hand, you prefer downright bogus semantics in the
* name of compatibility, here it is...
evalcmd(argc
, argv
) char **argv
; {
* Execute a command or commands contained in a string.
while ((n
= parsecmd(0)) != NEOF
) {
* Evaluate a parse tree. The value is left in the global variable
TRACE(("evaltree(NULL) called\n"));
TRACE(("evaltree(0x%x: %d) called\n", (int)n
, n
->type
));
evaltree(n
->nbinary
.ch1
, 0);
evaltree(n
->nbinary
.ch2
, flags
);
evaltree(n
->nbinary
.ch1
, EV_TESTED
);
if (evalskip
|| exitstatus
!= 0)
evaltree(n
->nbinary
.ch2
, flags
);
evaltree(n
->nbinary
.ch1
, EV_TESTED
);
if (evalskip
|| exitstatus
== 0)
evaltree(n
->nbinary
.ch2
, flags
);
expredir(n
->nredir
.redirect
);
redirect(n
->nredir
.redirect
, REDIR_PUSH
);
evaltree(n
->nredir
.n
, flags
);
evaltree(n
->nif
.test
, EV_TESTED
);
evaltree(n
->nif
.ifpart
, flags
);
} else if (n
->nif
.elsepart
) {
evaltree(n
->nif
.elsepart
, flags
);
defun(n
->narg
.text
, n
->narg
.next
);
evalcommand(n
, flags
, (struct backcmd
*)NULL
);
out1fmt("Node type = %d\n", n
->type
);
if ((flags
& EV_EXIT
) || (eflag
&& exitstatus
&& !(flags
& EV_TESTED
)))
evaltree(n
->nbinary
.ch1
, EV_TESTED
);
skipping
: if (evalskip
== SKIPCONT
&& --skipcount
<= 0) {
if (evalskip
== SKIPBREAK
&& --skipcount
<= 0)
evaltree(n
->nbinary
.ch2
, 0);
arglist
.lastp
= &arglist
.list
;
for (argp
= n
->nfor
.args
; argp
; argp
= argp
->narg
.next
) {
expandarg(argp
, &arglist
, 1);
for (sp
= arglist
.list
; sp
; sp
= sp
->next
) {
setvar(n
->nfor
.var
, sp
->text
, 0);
evaltree(n
->nfor
.body
, 0);
if (evalskip
== SKIPCONT
&& --skipcount
<= 0) {
if (evalskip
== SKIPBREAK
&& --skipcount
<= 0)
arglist
.lastp
= &arglist
.list
;
expandarg(n
->ncase
.expr
, &arglist
, 0);
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
)) {
evaltree(cp
->nclist
.body
, flags
);
* Kick off a subshell to evaluate a tree.
int backgnd
= (n
->type
== NBACKGND
);
expredir(n
->nredir
.redirect
);
if (forkshell(jp
, n
, backgnd
) == 0) {
redirect(n
->nredir
.redirect
, 0);
evaltree(n
->nredir
.n
, flags
| EV_EXIT
); /* never returns */
exitstatus
= waitforjob(jp
);
* Compute the names of the files in a redirection list.
register union node
*redir
;
for (redir
= n
; redir
; redir
= redir
->nfile
.next
) {
|| redir
->type
== NAPPEND
) {
expandarg(redir
->nfile
.fname
, &fn
, 0);
redir
->nfile
.expfname
= fn
.list
->text
;
* 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
TRACE(("evalpipe(0x%x) called\n", (int)n
));
for (lp
= n
->npipe
.cmdlist
; lp
; lp
= lp
->next
)
jp
= makejob(n
, pipelen
);
for (lp
= n
->npipe
.cmdlist
; lp
; lp
= lp
->next
) {
error("Pipe call failed");
if (forkshell(jp
, lp
->n
, n
->npipe
.backgnd
) == 0) {
evaltree(lp
->n
, EV_EXIT
);
if (n
->npipe
.backgnd
== 0) {
exitstatus
= waitforjob(jp
);
TRACE(("evalpipe: job done exit status %d\n", exitstatus
));
* 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.
struct stackmark smark
; /* unnecessary */
evalcommand(n
, EV_BACKCMD
, result
);
error("Pipe call failed");
if (forkshell(jp
, n
, FORK_NOJOB
) == 0) {
TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
result
->fd
, result
->buf
, result
->nleft
, result
->jp
));
* Execute a simple command.
evalcommand(cmd
, flags
, backcmd
)
struct cmdentry cmdentry
;
struct jmploc
*volatile savehandler
;
char *volatile savecmdname
;
volatile struct shparam saveparam
;
struct localvar
*volatile savelocalvars
;
/* First expand the arguments. */
TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd
, flags
));
arglist
.lastp
= &arglist
.list
;
varlist
.lastp
= &varlist
.list
;
for (argp
= cmd
->ncmd
.args
; argp
; argp
= argp
->narg
.next
) {
if (varflag
&& is_name(*p
)) {
} while (is_in_name(*p
));
expandarg(argp
, &varlist
, 0);
expandarg(argp
, &arglist
, 1);
expredir(cmd
->ncmd
.redirect
);
for (sp
= arglist
.list
; sp
; sp
= sp
->next
)
argv
= stalloc(sizeof (char *) * (argc
+ 1));
for (sp
= arglist
.list
; sp
; sp
= sp
->next
)
if (iflag
&& funcnest
== 0 && argc
> 0)
/* Print the command if xflag is set. */
for (sp
= varlist
.list
; sp
; sp
= sp
->next
) {
for (sp
= arglist
.list
; sp
; sp
= sp
->next
) {
/* Now locate the command. */
cmdentry
.cmdtype
= CMDBUILTIN
;
cmdentry
.u
.index
= BLTINCMD
;
find_command(argv
[0], &cmdentry
, 1);
if (cmdentry
.cmdtype
== CMDUNKNOWN
) { /* command not found */
/* implement the bltin builtin here */
if (cmdentry
.cmdtype
== CMDBUILTIN
&& cmdentry
.u
.index
== BLTINCMD
) {
if ((cmdentry
.u
.index
= find_builtin(*argv
)) < 0) {
outfmt(&errout
, "%s: not found\n", *argv
);
if (cmdentry
.u
.index
!= BLTINCMD
)
/* Fork off a child process if necessary. */
|| cmdentry
.cmdtype
== CMDNORMAL
&& (flags
& EV_EXIT
) == 0
|| (flags
& EV_BACKCMD
) != 0
&& (cmdentry
.cmdtype
!= CMDBUILTIN
|| cmdentry
.u
.index
== DOTCMD
|| cmdentry
.u
.index
== EVALCMD
)) {
mode
= cmd
->ncmd
.backgnd
;
if (flags
& EV_BACKCMD
) {
error("Pipe call failed");
if (forkshell(jp
, cmd
, mode
) != 0)
goto parent
; /* at end of routine */
if (flags
& EV_BACKCMD
) {
/* This is the child process if a fork occurred. */
/* Execute the command. */
if (cmdentry
.cmdtype
== CMDFUNCTION
) {
trputs("Shell function: "); trargs(argv
);
redirect(cmd
->ncmd
.redirect
, REDIR_PUSH
);
shellparam
.nparam
= argc
- 1;
shellparam
.optnext
= NULL
;
savelocalvars
= localvars
;
if (setjmp(jmploc
.loc
)) {
if (exception
== EXSHELLPROC
)
freeparam((struct shparam
*)&saveparam
);
localvars
= savelocalvars
;
longjmp(handler
->loc
, 1);
for (sp
= varlist
.list
; sp
; sp
= sp
->next
)
evaltree(cmdentry
.u
.func
, 0);
localvars
= savelocalvars
;
if (evalskip
== SKIPFUNC
) {
} else if (cmdentry
.cmdtype
== CMDBUILTIN
) {
trputs("builtin command: "); trargs(argv
);
mode
= (cmdentry
.u
.index
== EXECCMD
)? 0 : REDIR_PUSH
;
if (flags
== EV_BACKCMD
) {
memout
.nextc
= memout
.buf
;
redirect(cmd
->ncmd
.redirect
, mode
);
savecmdname
= commandname
;
cmdenviron
= varlist
.list
;
if (setjmp(jmploc
.loc
)) {
exitstatus
= (e
== EXINT
)? SIGINT
+128 : 2;
optptr
= NULL
; /* initialize nextopt */
exitstatus
= (*builtinfunc
[cmdentry
.u
.index
])(argc
, argv
);
commandname
= savecmdname
;
if (e
!= EXERROR
|| cmdentry
.u
.index
== BLTINCMD
|| cmdentry
.u
.index
== DOTCMD
|| cmdentry
.u
.index
== EVALCMD
|| cmdentry
.u
.index
== EXECCMD
)
if (cmdentry
.u
.index
!= EXECCMD
)
if (flags
== EV_BACKCMD
) {
backcmd
->buf
= memout
.buf
;
backcmd
->nleft
= memout
.nextc
- memout
.buf
;
trputs("normal command: "); trargs(argv
);
redirect(cmd
->ncmd
.redirect
, 0);
p
= stalloc(strlen(pathval()) + 1);
for (sp
= varlist
.list
; sp
; sp
= sp
->next
)
setvareq(sp
->text
, VEXPORT
|VSTACK
);
shellexec(argv
, envp
, p
, cmdentry
.u
.index
);
parent
: /* parent process gets here (if we forked) */
if (mode
== 0) { /* argument to fork */
exitstatus
= waitforjob(jp
);
* 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.
if (n
->type
== NCMD
&& goodname(n
->ncmd
.args
->narg
.text
))
find_command(n
->ncmd
.args
->narg
.text
, &entry
, 0);
* Builtin commands. Builtin commands whose functions are closely
* tied to evaluation are implemented here.
* No command given, or a bltin command with no arguments. Set the
bltincmd(argc
, argv
) char **argv
; {
* 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.
breakcmd(argc
, argv
) char **argv
; {
evalskip
= (**argv
== 'c')? SKIPCONT
: SKIPBREAK
;
returncmd(argc
, argv
) char **argv
; {
truecmd(argc
, argv
) char **argv
; {
execcmd(argc
, argv
) char **argv
; {
iflag
= 0; /* exit on error */
shellexec(argv
+ 1, environment(), pathval(), 0);