eval.c revision 259065
1145516Sdarrenr/*-
2145516Sdarrenr * Copyright (c) 1993
3145516Sdarrenr *	The Regents of the University of California.  All rights reserved.
4145516Sdarrenr *
5145516Sdarrenr * This code is derived from software contributed to Berkeley by
6145516Sdarrenr * Kenneth Almquist.
7145516Sdarrenr *
8145516Sdarrenr * Redistribution and use in source and binary forms, with or without
9145516Sdarrenr * modification, are permitted provided that the following conditions
10172776Sdarrenr * are met:
11145516Sdarrenr * 1. Redistributions of source code must retain the above copyright
12145516Sdarrenr *    notice, this list of conditions and the following disclaimer.
13145516Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
14145516Sdarrenr *    notice, this list of conditions and the following disclaimer in the
15145516Sdarrenr *    documentation and/or other materials provided with the distribution.
16145516Sdarrenr * 4. Neither the name of the University nor the names of its contributors
17145516Sdarrenr *    may be used to endorse or promote products derived from this software
18145516Sdarrenr *    without specific prior written permission.
19145516Sdarrenr *
20145516Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21145516Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22145516Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23145516Sdarrenr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24145516Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25145516Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26145516Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27145516Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28145516Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29145516Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30145516Sdarrenr * SUCH DAMAGE.
31145516Sdarrenr */
32145516Sdarrenr
33145516Sdarrenr#ifndef lint
34145516Sdarrenr#if 0
35145516Sdarrenrstatic char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
36145516Sdarrenr#endif
37145516Sdarrenr#endif /* not lint */
38145516Sdarrenr#include <sys/cdefs.h>
39145516Sdarrenr__FBSDID("$FreeBSD: releng/10.0/bin/sh/eval.c 255215 2013-09-04 22:10:16Z jilles $");
40145516Sdarrenr
41145516Sdarrenr#include <paths.h>
42145516Sdarrenr#include <signal.h>
43145516Sdarrenr#include <stdlib.h>
44145516Sdarrenr#include <unistd.h>
45145516Sdarrenr#include <sys/resource.h>
46145516Sdarrenr#include <sys/wait.h> /* For WIFSIGNALED(status) */
47145516Sdarrenr#include <errno.h>
48145516Sdarrenr
49145516Sdarrenr/*
50145516Sdarrenr * Evaluate a command.
51145516Sdarrenr */
52145516Sdarrenr
53145516Sdarrenr#include "shell.h"
54145516Sdarrenr#include "nodes.h"
55145516Sdarrenr#include "syntax.h"
56145516Sdarrenr#include "expand.h"
57145516Sdarrenr#include "parser.h"
58145516Sdarrenr#include "jobs.h"
59145516Sdarrenr#include "eval.h"
60170268Sdarrenr#include "builtins.h"
61170268Sdarrenr#include "options.h"
62170268Sdarrenr#include "exec.h"
63170268Sdarrenr#include "redir.h"
64170268Sdarrenr#include "input.h"
65181803Sbz#include "output.h"
66181803Sbz#include "trap.h"
67181803Sbz#include "var.h"
68181803Sbz#include "memalloc.h"
69181803Sbz#include "error.h"
70181803Sbz#include "show.h"
71145516Sdarrenr#include "mystring.h"
72145516Sdarrenr#ifndef NO_HISTORY
73145516Sdarrenr#include "myhistedit.h"
74145516Sdarrenr#endif
75170268Sdarrenr
76170268Sdarrenr
77170268Sdarrenrint evalskip;			/* set if we are skipping commands */
78145516Sdarrenrint skipcount;			/* number of levels to skip */
79145516Sdarrenrstatic int loopnest;		/* current loop nesting level */
80145516Sdarrenrint funcnest;			/* depth of function calls */
81145516Sdarrenrstatic int builtin_flags;	/* evalcommand flags for builtins */
82145516Sdarrenr
83145516Sdarrenr
84145516Sdarrenrchar *commandname;
85145516Sdarrenrstruct strlist *cmdenviron;
86145516Sdarrenrint exitstatus;			/* exit status of last command */
87145516Sdarrenrint oexitstatus;		/* saved exit status */
88145516Sdarrenr
89145516Sdarrenr
90145516Sdarrenrstatic void evalloop(union node *, int);
91145516Sdarrenrstatic void evalfor(union node *, int);
92145516Sdarrenrstatic union node *evalcase(union node *);
93145516Sdarrenrstatic void evalsubshell(union node *, int);
94145516Sdarrenrstatic void evalredir(union node *, int);
95145516Sdarrenrstatic void exphere(union node *, struct arglist *);
96145516Sdarrenrstatic void expredir(union node *);
97145516Sdarrenrstatic void evalpipe(union node *);
98145516Sdarrenrstatic int is_valid_fast_cmdsubst(union node *n);
99145516Sdarrenrstatic void evalcommand(union node *, int, struct backcmd *);
100145516Sdarrenrstatic void prehash(union node *);
101145516Sdarrenr
102145516Sdarrenr
103145516Sdarrenr/*
104145516Sdarrenr * Called to reset things after an exception.
105145516Sdarrenr */
106145516Sdarrenr
107145516Sdarrenrvoid
108145516Sdarrenrreseteval(void)
109145516Sdarrenr{
110145516Sdarrenr	evalskip = 0;
111145516Sdarrenr	loopnest = 0;
112145516Sdarrenr}
113145516Sdarrenr
114145516Sdarrenr
115145516Sdarrenr/*
116145516Sdarrenr * The eval command.
117145516Sdarrenr */
118145516Sdarrenr
119145516Sdarrenrint
120145516Sdarrenrevalcmd(int argc, char **argv)
121145516Sdarrenr{
122145516Sdarrenr        char *p;
123145516Sdarrenr        char *concat;
124145516Sdarrenr        char **ap;
125145516Sdarrenr
126145516Sdarrenr        if (argc > 1) {
127145516Sdarrenr                p = argv[1];
128145516Sdarrenr                if (argc > 2) {
129151897Srwatson                        STARTSTACKSTR(concat);
130145516Sdarrenr                        ap = argv + 2;
131145516Sdarrenr                        for (;;) {
132145516Sdarrenr                                STPUTS(p, concat);
133145516Sdarrenr                                if ((p = *ap++) == NULL)
134145516Sdarrenr                                        break;
135145516Sdarrenr                                STPUTC(' ', concat);
136145516Sdarrenr                        }
137145516Sdarrenr                        STPUTC('\0', concat);
138145516Sdarrenr                        p = grabstackstr(concat);
139145516Sdarrenr                }
140145516Sdarrenr                evalstring(p, builtin_flags);
141145516Sdarrenr        } else
142170268Sdarrenr                exitstatus = 0;
143145516Sdarrenr        return exitstatus;
144145516Sdarrenr}
145145516Sdarrenr
146145516Sdarrenr
147145516Sdarrenr/*
148145516Sdarrenr * Execute a command or commands contained in a string.
149145516Sdarrenr */
150161356Sguido
151145516Sdarrenrvoid
152145516Sdarrenrevalstring(char *s, int flags)
153145516Sdarrenr{
154145516Sdarrenr	union node *n;
155145516Sdarrenr	struct stackmark smark;
156145516Sdarrenr	int flags_exit;
157145516Sdarrenr	int any;
158145516Sdarrenr
159145516Sdarrenr	flags_exit = flags & EV_EXIT;
160145516Sdarrenr	flags &= ~EV_EXIT;
161145516Sdarrenr	any = 0;
162145516Sdarrenr	setstackmark(&smark);
163145516Sdarrenr	setinputstring(s, 1);
164145516Sdarrenr	while ((n = parsecmd(0)) != NEOF) {
165153876Sguido		if (n != NULL && !nflag) {
166153876Sguido			if (flags_exit && preadateof())
167153876Sguido				evaltree(n, flags | EV_EXIT);
168153876Sguido			else
169153876Sguido				evaltree(n, flags);
170153876Sguido			any = 1;
171153876Sguido		}
172153876Sguido		popstackmark(&smark);
173153876Sguido		setstackmark(&smark);
174153876Sguido	}
175153876Sguido	popfile();
176153876Sguido	popstackmark(&smark);
177153876Sguido	if (!any)
178145516Sdarrenr		exitstatus = 0;
179145516Sdarrenr	if (flags_exit)
180145516Sdarrenr		exraise(EXEXIT);
181145516Sdarrenr}
182145516Sdarrenr
183145516Sdarrenr
184145516Sdarrenr/*
185145516Sdarrenr * Evaluate a parse tree.  The value is left in the global variable
186145516Sdarrenr * exitstatus.
187145516Sdarrenr */
188145516Sdarrenr
189145516Sdarrenrvoid
190145516Sdarrenrevaltree(union node *n, int flags)
191145516Sdarrenr{
192145516Sdarrenr	int do_etest;
193145516Sdarrenr	union node *next;
194145516Sdarrenr	struct stackmark smark;
195145516Sdarrenr
196145516Sdarrenr	setstackmark(&smark);
197145516Sdarrenr	do_etest = 0;
198145516Sdarrenr	if (n == NULL) {
199145516Sdarrenr		TRACE(("evaltree(NULL) called\n"));
200145516Sdarrenr		exitstatus = 0;
201145516Sdarrenr		goto out;
202145516Sdarrenr	}
203145516Sdarrenr	do {
204145516Sdarrenr		next = NULL;
205145516Sdarrenr#ifndef NO_HISTORY
206145516Sdarrenr		displayhist = 1;	/* show history substitutions done with fc */
207145516Sdarrenr#endif
208145516Sdarrenr		TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
209170268Sdarrenr		switch (n->type) {
210145516Sdarrenr		case NSEMI:
211145516Sdarrenr			evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
212145516Sdarrenr			if (evalskip)
213145516Sdarrenr				goto out;
214145516Sdarrenr			next = n->nbinary.ch2;
215145516Sdarrenr			break;
216145516Sdarrenr		case NAND:
217145516Sdarrenr			evaltree(n->nbinary.ch1, EV_TESTED);
218145516Sdarrenr			if (evalskip || exitstatus != 0) {
219145516Sdarrenr				goto out;
220145516Sdarrenr			}
221145516Sdarrenr			next = n->nbinary.ch2;
222145516Sdarrenr			break;
223145516Sdarrenr		case NOR:
224170268Sdarrenr			evaltree(n->nbinary.ch1, EV_TESTED);
225145516Sdarrenr			if (evalskip || exitstatus == 0)
226145516Sdarrenr				goto out;
227145516Sdarrenr			next = n->nbinary.ch2;
228145516Sdarrenr			break;
229145516Sdarrenr		case NREDIR:
230145516Sdarrenr			evalredir(n, flags);
231145516Sdarrenr			break;
232145516Sdarrenr		case NSUBSHELL:
233145516Sdarrenr			evalsubshell(n, flags);
234145516Sdarrenr			do_etest = !(flags & EV_TESTED);
235145516Sdarrenr			break;
236145516Sdarrenr		case NBACKGND:
237145516Sdarrenr			evalsubshell(n, flags);
238161356Sguido			break;
239145516Sdarrenr		case NIF: {
240145516Sdarrenr			evaltree(n->nif.test, EV_TESTED);
241145516Sdarrenr			if (evalskip)
242145516Sdarrenr				goto out;
243181803Sbz			if (exitstatus == 0)
244145516Sdarrenr				next = n->nif.ifpart;
245145516Sdarrenr			else if (n->nif.elsepart)
246145516Sdarrenr				next = n->nif.elsepart;
247145516Sdarrenr			else
248145516Sdarrenr				exitstatus = 0;
249145516Sdarrenr			break;
250145516Sdarrenr		}
251145516Sdarrenr		case NWHILE:
252145516Sdarrenr		case NUNTIL:
253145516Sdarrenr			evalloop(n, flags & ~EV_EXIT);
254145516Sdarrenr			break;
255145516Sdarrenr		case NFOR:
256145516Sdarrenr			evalfor(n, flags & ~EV_EXIT);
257145516Sdarrenr			break;
258145516Sdarrenr		case NCASE:
259145516Sdarrenr			next = evalcase(n);
260170268Sdarrenr			break;
261145516Sdarrenr		case NCLIST:
262145516Sdarrenr			next = n->nclist.body;
263145516Sdarrenr			break;
264145516Sdarrenr		case NCLISTFALLTHRU:
265145516Sdarrenr			if (n->nclist.body) {
266181803Sbz				evaltree(n->nclist.body, flags & ~EV_EXIT);
267145516Sdarrenr				if (evalskip)
268145516Sdarrenr					goto out;
269145516Sdarrenr			}
270145516Sdarrenr			next = n->nclist.next;
271145516Sdarrenr			break;
272145516Sdarrenr		case NDEFUN:
273145516Sdarrenr			defun(n->narg.text, n->narg.next);
274145516Sdarrenr			exitstatus = 0;
275145516Sdarrenr			break;
276145516Sdarrenr		case NNOT:
277145516Sdarrenr			evaltree(n->nnot.com, EV_TESTED);
278145516Sdarrenr			if (evalskip)
279145516Sdarrenr				goto out;
280145516Sdarrenr			exitstatus = !exitstatus;
281145516Sdarrenr			break;
282145516Sdarrenr
283145516Sdarrenr		case NPIPE:
284145516Sdarrenr			evalpipe(n);
285145516Sdarrenr			do_etest = !(flags & EV_TESTED);
286145516Sdarrenr			break;
287145516Sdarrenr		case NCMD:
288145516Sdarrenr			evalcommand(n, flags, (struct backcmd *)NULL);
289145516Sdarrenr			do_etest = !(flags & EV_TESTED);
290145516Sdarrenr			break;
291145516Sdarrenr		default:
292145516Sdarrenr			out1fmt("Node type = %d\n", n->type);
293145516Sdarrenr			flushout(&output);
294145516Sdarrenr			break;
295170268Sdarrenr		}
296145516Sdarrenr		n = next;
297145516Sdarrenr		popstackmark(&smark);
298145516Sdarrenr		setstackmark(&smark);
299145516Sdarrenr	} while (n != NULL);
300145516Sdarrenrout:
301145516Sdarrenr	popstackmark(&smark);
302145516Sdarrenr	if (pendingsig)
303145516Sdarrenr		dotrap();
304145516Sdarrenr	if (eflag && exitstatus != 0 && do_etest)
305145516Sdarrenr		exitshell(exitstatus);
306145516Sdarrenr	if (flags & EV_EXIT)
307145516Sdarrenr		exraise(EXEXIT);
308145516Sdarrenr}
309145516Sdarrenr
310145516Sdarrenr
311145516Sdarrenrstatic void
312145516Sdarrenrevalloop(union node *n, int flags)
313170268Sdarrenr{
314170268Sdarrenr	int status;
315170268Sdarrenr
316170268Sdarrenr	loopnest++;
317170268Sdarrenr	status = 0;
318145516Sdarrenr	for (;;) {
319145516Sdarrenr		evaltree(n->nbinary.ch1, EV_TESTED);
320170268Sdarrenr		if (evalskip) {
321145516Sdarrenrskipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
322145516Sdarrenr				evalskip = 0;
323145516Sdarrenr				continue;
324145516Sdarrenr			}
325145516Sdarrenr			if (evalskip == SKIPBREAK && --skipcount <= 0)
326145516Sdarrenr				evalskip = 0;
327145516Sdarrenr			if (evalskip == SKIPRETURN)
328145516Sdarrenr				status = exitstatus;
329145516Sdarrenr			break;
330145516Sdarrenr		}
331145516Sdarrenr		if (n->type == NWHILE) {
332145516Sdarrenr			if (exitstatus != 0)
333145516Sdarrenr				break;
334170268Sdarrenr		} else {
335170268Sdarrenr			if (exitstatus == 0)
336145516Sdarrenr				break;
337145516Sdarrenr		}
338153876Sguido		evaltree(n->nbinary.ch2, flags);
339145516Sdarrenr		status = exitstatus;
340145516Sdarrenr		if (evalskip)
341145516Sdarrenr			goto skipping;
342145516Sdarrenr	}
343145516Sdarrenr	loopnest--;
344145516Sdarrenr	exitstatus = status;
345145516Sdarrenr}
346145516Sdarrenr
347145516Sdarrenr
348145516Sdarrenr
349145516Sdarrenrstatic void
350173181Sdarrenrevalfor(union node *n, int flags)
351145516Sdarrenr{
352145516Sdarrenr	struct arglist arglist;
353145516Sdarrenr	union node *argp;
354145516Sdarrenr	struct strlist *sp;
355145516Sdarrenr	int status;
356145516Sdarrenr
357170268Sdarrenr	arglist.lastp = &arglist.list;
358145516Sdarrenr	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
359145516Sdarrenr		oexitstatus = exitstatus;
360145516Sdarrenr		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
361145516Sdarrenr	}
362145516Sdarrenr	*arglist.lastp = NULL;
363145516Sdarrenr
364161356Sguido	loopnest++;
365145516Sdarrenr	status = 0;
366145516Sdarrenr	for (sp = arglist.list ; sp ; sp = sp->next) {
367145516Sdarrenr		setvar(n->nfor.var, sp->text, 0);
368145516Sdarrenr		evaltree(n->nfor.body, flags);
369145516Sdarrenr		status = exitstatus;
370145516Sdarrenr		if (evalskip) {
371145516Sdarrenr			if (evalskip == SKIPCONT && --skipcount <= 0) {
372145516Sdarrenr				evalskip = 0;
373145516Sdarrenr				continue;
374145516Sdarrenr			}
375145516Sdarrenr			if (evalskip == SKIPBREAK && --skipcount <= 0)
376145516Sdarrenr				evalskip = 0;
377145516Sdarrenr			break;
378145516Sdarrenr		}
379145516Sdarrenr	}
380145516Sdarrenr	loopnest--;
381145516Sdarrenr	exitstatus = status;
382145516Sdarrenr}
383145516Sdarrenr
384145516Sdarrenr
385145516Sdarrenr/*
386145516Sdarrenr * Evaluate a case statement, returning the selected tree.
387145516Sdarrenr *
388145516Sdarrenr * The exit status needs care to get right.
389145516Sdarrenr */
390145516Sdarrenr
391145516Sdarrenrstatic union node *
392145516Sdarrenrevalcase(union node *n)
393145516Sdarrenr{
394145516Sdarrenr	union node *cp;
395145516Sdarrenr	union node *patp;
396145516Sdarrenr	struct arglist arglist;
397145516Sdarrenr
398145516Sdarrenr	arglist.lastp = &arglist.list;
399145516Sdarrenr	oexitstatus = exitstatus;
400145516Sdarrenr	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
401145516Sdarrenr	for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
402145516Sdarrenr		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
403145516Sdarrenr			if (casematch(patp, arglist.list->text)) {
404145516Sdarrenr				while (cp->nclist.next &&
405145516Sdarrenr				    cp->type == NCLISTFALLTHRU &&
406145516Sdarrenr				    cp->nclist.body == NULL)
407145516Sdarrenr					cp = cp->nclist.next;
408145516Sdarrenr				if (cp->nclist.next &&
409145516Sdarrenr				    cp->type == NCLISTFALLTHRU)
410145516Sdarrenr					return (cp);
411145516Sdarrenr				if (cp->nclist.body == NULL)
412145516Sdarrenr					exitstatus = 0;
413145516Sdarrenr				return (cp->nclist.body);
414145516Sdarrenr			}
415145516Sdarrenr		}
416145516Sdarrenr	}
417145516Sdarrenr	exitstatus = 0;
418145516Sdarrenr	return (NULL);
419145516Sdarrenr}
420145516Sdarrenr
421145516Sdarrenr
422145516Sdarrenr
423145516Sdarrenr/*
424145516Sdarrenr * Kick off a subshell to evaluate a tree.
425145516Sdarrenr */
426145516Sdarrenr
427145516Sdarrenrstatic void
428145516Sdarrenrevalsubshell(union node *n, int flags)
429145516Sdarrenr{
430145516Sdarrenr	struct job *jp;
431145516Sdarrenr	int backgnd = (n->type == NBACKGND);
432145516Sdarrenr
433145516Sdarrenr	oexitstatus = exitstatus;
434145516Sdarrenr	expredir(n->nredir.redirect);
435145516Sdarrenr	if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
436145516Sdarrenr			forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
437145516Sdarrenr		if (backgnd)
438145516Sdarrenr			flags &=~ EV_TESTED;
439145516Sdarrenr		redirect(n->nredir.redirect, 0);
440145516Sdarrenr		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
441145516Sdarrenr	} else if (! backgnd) {
442145516Sdarrenr		INTOFF;
443145516Sdarrenr		exitstatus = waitforjob(jp, (int *)NULL);
444145516Sdarrenr		INTON;
445145516Sdarrenr	} else
446145516Sdarrenr		exitstatus = 0;
447145516Sdarrenr}
448145516Sdarrenr
449145516Sdarrenr
450145516Sdarrenr/*
451145516Sdarrenr * Evaluate a redirected compound command.
452145516Sdarrenr */
453145516Sdarrenr
454145516Sdarrenrstatic void
455145516Sdarrenrevalredir(union node *n, int flags)
456145516Sdarrenr{
457145516Sdarrenr	struct jmploc jmploc;
458145516Sdarrenr	struct jmploc *savehandler;
459145516Sdarrenr	volatile int in_redirect = 1;
460145516Sdarrenr
461145516Sdarrenr	oexitstatus = exitstatus;
462145516Sdarrenr	expredir(n->nredir.redirect);
463145516Sdarrenr	savehandler = handler;
464145516Sdarrenr	if (setjmp(jmploc.loc)) {
465145516Sdarrenr		int e;
466145516Sdarrenr
467145516Sdarrenr		handler = savehandler;
468145516Sdarrenr		e = exception;
469145516Sdarrenr		popredir();
470145516Sdarrenr		if (e == EXERROR || e == EXEXEC) {
471145516Sdarrenr			if (in_redirect) {
472145516Sdarrenr				exitstatus = 2;
473145516Sdarrenr				return;
474145516Sdarrenr			}
475145516Sdarrenr		}
476145516Sdarrenr		longjmp(handler->loc, 1);
477145516Sdarrenr	} else {
478145516Sdarrenr		INTOFF;
479145516Sdarrenr		handler = &jmploc;
480145516Sdarrenr		redirect(n->nredir.redirect, REDIR_PUSH);
481145516Sdarrenr		in_redirect = 0;
482145516Sdarrenr		INTON;
483145516Sdarrenr		evaltree(n->nredir.n, flags);
484145516Sdarrenr	}
485145516Sdarrenr	INTOFF;
486145516Sdarrenr	handler = savehandler;
487161356Sguido	popredir();
488145516Sdarrenr	INTON;
489170268Sdarrenr}
490170268Sdarrenr
491170268Sdarrenr
492161356Sguidostatic void
493161356Sguidoexphere(union node *redir, struct arglist *fn)
494161356Sguido{
495145516Sdarrenr	struct jmploc jmploc;
496161356Sguido	struct jmploc *savehandler;
497145516Sdarrenr	struct localvar *savelocalvars;
498145516Sdarrenr	int need_longjmp = 0;
499145516Sdarrenr
500145516Sdarrenr	redir->nhere.expdoc = nullstr;
501161356Sguido	savelocalvars = localvars;
502145516Sdarrenr	localvars = NULL;
503145516Sdarrenr	forcelocal++;
504145516Sdarrenr	savehandler = handler;
505145516Sdarrenr	if (setjmp(jmploc.loc))
506145516Sdarrenr		need_longjmp = exception != EXERROR && exception != EXEXEC;
507145516Sdarrenr	else {
508145516Sdarrenr		handler = &jmploc;
509145516Sdarrenr		expandarg(redir->nhere.doc, fn, 0);
510145516Sdarrenr		redir->nhere.expdoc = fn->list->text;
511145516Sdarrenr		INTOFF;
512145516Sdarrenr	}
513145516Sdarrenr	handler = savehandler;
514145516Sdarrenr	forcelocal--;
515145516Sdarrenr	poplocalvars();
516145516Sdarrenr	localvars = savelocalvars;
517145516Sdarrenr	if (need_longjmp)
518145516Sdarrenr		longjmp(handler->loc, 1);
519145516Sdarrenr	INTON;
520145516Sdarrenr}
521145516Sdarrenr
522145516Sdarrenr
523145516Sdarrenr/*
524145516Sdarrenr * Compute the names of the files in a redirection list.
525145516Sdarrenr */
526145516Sdarrenr
527145516Sdarrenrstatic void
528170268Sdarrenrexpredir(union node *n)
529170268Sdarrenr{
530170268Sdarrenr	union node *redir;
531145516Sdarrenr
532145516Sdarrenr	for (redir = n ; redir ; redir = redir->nfile.next) {
533145516Sdarrenr		struct arglist fn;
534145516Sdarrenr		fn.lastp = &fn.list;
535145516Sdarrenr		switch (redir->type) {
536145516Sdarrenr		case NFROM:
537145516Sdarrenr		case NTO:
538145516Sdarrenr		case NFROMTO:
539145516Sdarrenr		case NAPPEND:
540145516Sdarrenr		case NCLOBBER:
541145516Sdarrenr			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
542145516Sdarrenr			redir->nfile.expfname = fn.list->text;
543145516Sdarrenr			break;
544145516Sdarrenr		case NFROMFD:
545145516Sdarrenr		case NTOFD:
546145516Sdarrenr			if (redir->ndup.vname) {
547145516Sdarrenr				expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
548145516Sdarrenr				fixredir(redir, fn.list->text, 1);
549145516Sdarrenr			}
550145516Sdarrenr			break;
551145516Sdarrenr		case NXHERE:
552145516Sdarrenr			exphere(redir, &fn);
553145516Sdarrenr			break;
554145516Sdarrenr		}
555145516Sdarrenr	}
556145516Sdarrenr}
557145516Sdarrenr
558145516Sdarrenr
559145516Sdarrenr
560145516Sdarrenr/*
561145516Sdarrenr * Evaluate a pipeline.  All the processes in the pipeline are children
562145516Sdarrenr * of the process creating the pipeline.  (This differs from some versions
563145516Sdarrenr * of the shell, which make the last process in a pipeline the parent
564145516Sdarrenr * of all the rest.)
565145516Sdarrenr */
566145516Sdarrenr
567145516Sdarrenrstatic void
568145516Sdarrenrevalpipe(union node *n)
569145516Sdarrenr{
570145516Sdarrenr	struct job *jp;
571145516Sdarrenr	struct nodelist *lp;
572145516Sdarrenr	int pipelen;
573145516Sdarrenr	int prevfd;
574145516Sdarrenr	int pip[2];
575145516Sdarrenr
576145516Sdarrenr	TRACE(("evalpipe(%p) called\n", (void *)n));
577145516Sdarrenr	pipelen = 0;
578145516Sdarrenr	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
579145516Sdarrenr		pipelen++;
580145516Sdarrenr	INTOFF;
581145516Sdarrenr	jp = makejob(n, pipelen);
582145516Sdarrenr	prevfd = -1;
583145516Sdarrenr	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
584145516Sdarrenr		prehash(lp->n);
585145516Sdarrenr		pip[1] = -1;
586145516Sdarrenr		if (lp->next) {
587145516Sdarrenr			if (pipe(pip) < 0) {
588145516Sdarrenr				if (prevfd >= 0)
589145516Sdarrenr					close(prevfd);
590145516Sdarrenr				error("Pipe call failed: %s", strerror(errno));
591145516Sdarrenr			}
592145516Sdarrenr		}
593145516Sdarrenr		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
594145516Sdarrenr			INTON;
595145516Sdarrenr			if (prevfd > 0) {
596145516Sdarrenr				dup2(prevfd, 0);
597145516Sdarrenr				close(prevfd);
598145516Sdarrenr			}
599145516Sdarrenr			if (pip[1] >= 0) {
600145516Sdarrenr				if (!(prevfd >= 0 && pip[0] == 0))
601145516Sdarrenr					close(pip[0]);
602145516Sdarrenr				if (pip[1] != 1) {
603145516Sdarrenr					dup2(pip[1], 1);
604145516Sdarrenr					close(pip[1]);
605145516Sdarrenr				}
606145516Sdarrenr			}
607145516Sdarrenr			evaltree(lp->n, EV_EXIT);
608145516Sdarrenr		}
609145516Sdarrenr		if (prevfd >= 0)
610145516Sdarrenr			close(prevfd);
611145516Sdarrenr		prevfd = pip[0];
612145516Sdarrenr		if (pip[1] != -1)
613145516Sdarrenr			close(pip[1]);
614145516Sdarrenr	}
615145516Sdarrenr	INTON;
616145516Sdarrenr	if (n->npipe.backgnd == 0) {
617145516Sdarrenr		INTOFF;
618145516Sdarrenr		exitstatus = waitforjob(jp, (int *)NULL);
619145516Sdarrenr		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
620145516Sdarrenr		INTON;
621145516Sdarrenr	} else
622145516Sdarrenr		exitstatus = 0;
623145516Sdarrenr}
624145516Sdarrenr
625145516Sdarrenr
626145516Sdarrenr
627145516Sdarrenrstatic int
628145516Sdarrenris_valid_fast_cmdsubst(union node *n)
629145516Sdarrenr{
630145516Sdarrenr
631145516Sdarrenr	return (n->type == NCMD);
632145516Sdarrenr}
633145516Sdarrenr
634145516Sdarrenr/*
635145516Sdarrenr * Execute a command inside back quotes.  If it's a builtin command, we
636145516Sdarrenr * want to save its output in a block obtained from malloc.  Otherwise
637145516Sdarrenr * we fork off a subprocess and get the output of the command via a pipe.
638145516Sdarrenr * Should be called with interrupts off.
639145516Sdarrenr */
640145516Sdarrenr
641145516Sdarrenrvoid
642145516Sdarrenrevalbackcmd(union node *n, struct backcmd *result)
643145516Sdarrenr{
644145516Sdarrenr	int pip[2];
645145516Sdarrenr	struct job *jp;
646145516Sdarrenr	struct stackmark smark;
647145516Sdarrenr	struct jmploc jmploc;
648145516Sdarrenr	struct jmploc *savehandler;
649145516Sdarrenr	struct localvar *savelocalvars;
650145516Sdarrenr
651145516Sdarrenr	setstackmark(&smark);
652145516Sdarrenr	result->fd = -1;
653145516Sdarrenr	result->buf = NULL;
654145516Sdarrenr	result->nleft = 0;
655145516Sdarrenr	result->jp = NULL;
656145516Sdarrenr	if (n == NULL) {
657145516Sdarrenr		exitstatus = 0;
658145516Sdarrenr		goto out;
659145516Sdarrenr	}
660145516Sdarrenr	exitstatus = oexitstatus;
661181803Sbz	if (is_valid_fast_cmdsubst(n)) {
662145516Sdarrenr		savelocalvars = localvars;
663145516Sdarrenr		localvars = NULL;
664145516Sdarrenr		forcelocal++;
665181803Sbz		savehandler = handler;
666145516Sdarrenr		if (setjmp(jmploc.loc)) {
667145516Sdarrenr			if (exception == EXERROR || exception == EXEXEC)
668145516Sdarrenr				exitstatus = 2;
669145516Sdarrenr			else if (exception != 0) {
670145516Sdarrenr				handler = savehandler;
671145516Sdarrenr				forcelocal--;
672145516Sdarrenr				poplocalvars();
673145516Sdarrenr				localvars = savelocalvars;
674145516Sdarrenr				longjmp(handler->loc, 1);
675145516Sdarrenr			}
676145516Sdarrenr		} else {
677145516Sdarrenr			handler = &jmploc;
678145516Sdarrenr			evalcommand(n, EV_BACKCMD, result);
679145516Sdarrenr		}
680145516Sdarrenr		handler = savehandler;
681145516Sdarrenr		forcelocal--;
682145516Sdarrenr		poplocalvars();
683145516Sdarrenr		localvars = savelocalvars;
684145516Sdarrenr	} else {
685145516Sdarrenr		if (pipe(pip) < 0)
686145516Sdarrenr			error("Pipe call failed: %s", strerror(errno));
687145516Sdarrenr		jp = makejob(n, 1);
688145516Sdarrenr		if (forkshell(jp, n, FORK_NOJOB) == 0) {
689145516Sdarrenr			FORCEINTON;
690145516Sdarrenr			close(pip[0]);
691145516Sdarrenr			if (pip[1] != 1) {
692145516Sdarrenr				dup2(pip[1], 1);
693145516Sdarrenr				close(pip[1]);
694145516Sdarrenr			}
695145516Sdarrenr			evaltree(n, EV_EXIT);
696145516Sdarrenr		}
697145516Sdarrenr		close(pip[1]);
698145516Sdarrenr		result->fd = pip[0];
699145516Sdarrenr		result->jp = jp;
700145516Sdarrenr	}
701145516Sdarrenrout:
702145516Sdarrenr	popstackmark(&smark);
703145516Sdarrenr	TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
704145516Sdarrenr		result->fd, result->buf, result->nleft, result->jp));
705145516Sdarrenr}
706145516Sdarrenr
707145516Sdarrenrstatic int
708145516Sdarrenrmustexpandto(const char *argtext, const char *mask)
709145516Sdarrenr{
710145516Sdarrenr	for (;;) {
711145516Sdarrenr		if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
712145516Sdarrenr			argtext++;
713145516Sdarrenr			continue;
714145516Sdarrenr		}
715145516Sdarrenr		if (*argtext == CTLESC)
716145516Sdarrenr			argtext++;
717145516Sdarrenr		else if (BASESYNTAX[(int)*argtext] == CCTL)
718172776Sdarrenr			return (0);
719145516Sdarrenr		if (*argtext != *mask)
720145516Sdarrenr			return (0);
721145516Sdarrenr		if (*argtext == '\0')
722145516Sdarrenr			return (1);
723145516Sdarrenr		argtext++;
724145516Sdarrenr		mask++;
725145516Sdarrenr	}
726145516Sdarrenr}
727145516Sdarrenr
728145516Sdarrenrstatic int
729145516Sdarrenrisdeclarationcmd(struct narg *arg)
730145516Sdarrenr{
731145516Sdarrenr	int have_command = 0;
732145516Sdarrenr
733145516Sdarrenr	if (arg == NULL)
734145516Sdarrenr		return (0);
735145516Sdarrenr	while (mustexpandto(arg->text, "command")) {
736145516Sdarrenr		have_command = 1;
737145516Sdarrenr		arg = &arg->next->narg;
738145516Sdarrenr		if (arg == NULL)
739145516Sdarrenr			return (0);
740145516Sdarrenr		/*
741145516Sdarrenr		 * To also allow "command -p" and "command --" as part of
742145516Sdarrenr		 * a declaration command, add code here.
743145516Sdarrenr		 * We do not do this, as ksh does not do it either and it
744145516Sdarrenr		 * is not required by POSIX.
745145516Sdarrenr		 */
746145516Sdarrenr	}
747145516Sdarrenr	return (mustexpandto(arg->text, "export") ||
748145516Sdarrenr	    mustexpandto(arg->text, "readonly") ||
749145516Sdarrenr	    (mustexpandto(arg->text, "local") &&
750145516Sdarrenr		(have_command || !isfunc("local"))));
751145516Sdarrenr}
752145516Sdarrenr
753145516Sdarrenr/*
754145516Sdarrenr * Check if a builtin can safely be executed in the same process,
755145516Sdarrenr * even though it should be in a subshell (command substitution).
756145516Sdarrenr * Note that jobid, jobs, times and trap can show information not
757145516Sdarrenr * available in a child process; this is deliberate.
758145516Sdarrenr * The arguments should already have been expanded.
759145516Sdarrenr */
760145516Sdarrenrstatic int
761145516Sdarrenrsafe_builtin(int idx, int argc, char **argv)
762145516Sdarrenr{
763145516Sdarrenr	if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD ||
764145516Sdarrenr	    idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD ||
765145516Sdarrenr	    idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD ||
766145516Sdarrenr	    idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD ||
767145516Sdarrenr	    idx == TYPECMD)
768145516Sdarrenr		return (1);
769145516Sdarrenr	if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
770145516Sdarrenr	    idx == UMASKCMD)
771145516Sdarrenr		return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
772145516Sdarrenr	if (idx == SETCMD)
773145516Sdarrenr		return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
774145516Sdarrenr		    argv[1][0] == '+') && argv[1][1] == 'o' &&
775145516Sdarrenr		    argv[1][2] == '\0'));
776145516Sdarrenr	return (0);
777145516Sdarrenr}
778145516Sdarrenr
779145516Sdarrenr/*
780145516Sdarrenr * Execute a simple command.
781145516Sdarrenr * Note: This may or may not return if (flags & EV_EXIT).
782145516Sdarrenr */
783145516Sdarrenr
784145516Sdarrenrstatic void
785145516Sdarrenrevalcommand(union node *cmd, int flags, struct backcmd *backcmd)
786145516Sdarrenr{
787145516Sdarrenr	union node *argp;
788145516Sdarrenr	struct arglist arglist;
789145516Sdarrenr	struct arglist varlist;
790145516Sdarrenr	char **argv;
791145516Sdarrenr	int argc;
792145516Sdarrenr	char **envp;
793145516Sdarrenr	int varflag;
794145516Sdarrenr	struct strlist *sp;
795145516Sdarrenr	int mode;
796145516Sdarrenr	int pip[2];
797145516Sdarrenr	struct cmdentry cmdentry;
798145516Sdarrenr	struct job *jp;
799145516Sdarrenr	struct jmploc jmploc;
800145516Sdarrenr	struct jmploc *savehandler;
801145516Sdarrenr	char *savecmdname;
802145516Sdarrenr	struct shparam saveparam;
803145516Sdarrenr	struct localvar *savelocalvars;
804145516Sdarrenr	struct parsefile *savetopfile;
805145516Sdarrenr	volatile int e;
806145516Sdarrenr	char *lastarg;
807145516Sdarrenr	int realstatus;
808145516Sdarrenr	int do_clearcmdentry;
809145516Sdarrenr	const char *path = pathval();
810145516Sdarrenr
811145516Sdarrenr	/* First expand the arguments. */
812145516Sdarrenr	TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
813145516Sdarrenr	arglist.lastp = &arglist.list;
814145516Sdarrenr	varlist.lastp = &varlist.list;
815145516Sdarrenr	varflag = 1;
816145516Sdarrenr	jp = NULL;
817145516Sdarrenr	do_clearcmdentry = 0;
818145516Sdarrenr	oexitstatus = exitstatus;
819145516Sdarrenr	exitstatus = 0;
820145516Sdarrenr	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
821145516Sdarrenr		if (varflag && isassignment(argp->narg.text)) {
822145516Sdarrenr			expandarg(argp, varflag == 1 ? &varlist : &arglist,
823145516Sdarrenr			    EXP_VARTILDE);
824145516Sdarrenr			continue;
825145516Sdarrenr		} else if (varflag == 1)
826145516Sdarrenr			varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
827145516Sdarrenr		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
828145516Sdarrenr	}
829145516Sdarrenr	*arglist.lastp = NULL;
830145516Sdarrenr	*varlist.lastp = NULL;
831145516Sdarrenr	expredir(cmd->ncmd.redirect);
832145516Sdarrenr	argc = 0;
833145516Sdarrenr	for (sp = arglist.list ; sp ; sp = sp->next)
834145516Sdarrenr		argc++;
835145516Sdarrenr	/* Add one slot at the beginning for tryexec(). */
836145516Sdarrenr	argv = stalloc(sizeof (char *) * (argc + 2));
837145516Sdarrenr	argv++;
838145516Sdarrenr
839145516Sdarrenr	for (sp = arglist.list ; sp ; sp = sp->next) {
840145516Sdarrenr		TRACE(("evalcommand arg: %s\n", sp->text));
841145516Sdarrenr		*argv++ = sp->text;
842145516Sdarrenr	}
843145516Sdarrenr	*argv = NULL;
844145516Sdarrenr	lastarg = NULL;
845145516Sdarrenr	if (iflag && funcnest == 0 && argc > 0)
846145516Sdarrenr		lastarg = argv[-1];
847145516Sdarrenr	argv -= argc;
848145516Sdarrenr
849145516Sdarrenr	/* Print the command if xflag is set. */
850145516Sdarrenr	if (xflag) {
851145516Sdarrenr		char sep = 0;
852145516Sdarrenr		const char *p, *ps4;
853145516Sdarrenr		ps4 = expandstr(ps4val());
854145516Sdarrenr		out2str(ps4 != NULL ? ps4 : ps4val());
855145516Sdarrenr		for (sp = varlist.list ; sp ; sp = sp->next) {
856145516Sdarrenr			if (sep != 0)
857145516Sdarrenr				out2c(' ');
858145516Sdarrenr			p = strchr(sp->text, '=');
859145516Sdarrenr			if (p != NULL) {
860145516Sdarrenr				p++;
861145516Sdarrenr				outbin(sp->text, p - sp->text, out2);
862145516Sdarrenr				out2qstr(p);
863145516Sdarrenr			} else
864145516Sdarrenr				out2qstr(sp->text);
865145516Sdarrenr			sep = ' ';
866145516Sdarrenr		}
867145516Sdarrenr		for (sp = arglist.list ; sp ; sp = sp->next) {
868145516Sdarrenr			if (sep != 0)
869145516Sdarrenr				out2c(' ');
870145516Sdarrenr			/* Disambiguate command looking like assignment. */
871145516Sdarrenr			if (sp == arglist.list &&
872145516Sdarrenr					strchr(sp->text, '=') != NULL &&
873145516Sdarrenr					strchr(sp->text, '\'') == NULL) {
874145516Sdarrenr				out2c('\'');
875145516Sdarrenr				out2str(sp->text);
876145516Sdarrenr				out2c('\'');
877145516Sdarrenr			} else
878145516Sdarrenr				out2qstr(sp->text);
879145516Sdarrenr			sep = ' ';
880145516Sdarrenr		}
881145516Sdarrenr		out2c('\n');
882145516Sdarrenr		flushout(&errout);
883145516Sdarrenr	}
884170268Sdarrenr
885145516Sdarrenr	/* Now locate the command. */
886145516Sdarrenr	if (argc == 0) {
887145516Sdarrenr		/* Variable assignment(s) without command */
888145516Sdarrenr		cmdentry.cmdtype = CMDBUILTIN;
889145516Sdarrenr		cmdentry.u.index = BLTINCMD;
890145516Sdarrenr		cmdentry.special = 0;
891173181Sdarrenr	} else {
892173181Sdarrenr		static const char PATH[] = "PATH=";
893173181Sdarrenr		int cmd_flags = 0, bltinonly = 0;
894173181Sdarrenr
895145516Sdarrenr		/*
896145516Sdarrenr		 * Modify the command lookup path, if a PATH= assignment
897145516Sdarrenr		 * is present
898145516Sdarrenr		 */
899145516Sdarrenr		for (sp = varlist.list ; sp ; sp = sp->next)
900145516Sdarrenr			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) {
901173181Sdarrenr				path = sp->text + sizeof(PATH) - 1;
902145516Sdarrenr				/*
903145516Sdarrenr				 * On `PATH=... command`, we need to make
904145516Sdarrenr				 * sure that the command isn't using the
905145516Sdarrenr				 * non-updated hash table of the outer PATH
906145516Sdarrenr				 * setting and we need to make sure that
907145516Sdarrenr				 * the hash table isn't filled with items
908145516Sdarrenr				 * from the temporary setting.
909145516Sdarrenr				 *
910161356Sguido				 * It would be better to forbit using and
911161356Sguido				 * updating the table while this command
912145516Sdarrenr				 * runs, by the command finding mechanism
913145516Sdarrenr				 * is heavily integrated with hash handling,
914145516Sdarrenr				 * so we just delete the hash before and after
915145516Sdarrenr				 * the command runs. Partly deleting like
916145516Sdarrenr				 * changepatch() does doesn't seem worth the
917145516Sdarrenr				 * bookinging effort, since most such runs add
918145516Sdarrenr				 * directories in front of the new PATH.
919145516Sdarrenr				 */
920145516Sdarrenr				clearcmdentry();
921145516Sdarrenr				do_clearcmdentry = 1;
922145516Sdarrenr			}
923145516Sdarrenr
924145516Sdarrenr		for (;;) {
925161356Sguido			if (bltinonly) {
926161356Sguido				cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
927145516Sdarrenr				if (cmdentry.u.index < 0) {
928145516Sdarrenr					cmdentry.u.index = BLTINCMD;
929145516Sdarrenr					argv--;
930145516Sdarrenr					argc++;
931145516Sdarrenr					break;
932145516Sdarrenr				}
933161356Sguido			} else
934145516Sdarrenr				find_command(argv[0], &cmdentry, cmd_flags, path);
935145516Sdarrenr			/* implement the bltin and command builtins here */
936145516Sdarrenr			if (cmdentry.cmdtype != CMDBUILTIN)
937145516Sdarrenr				break;
938145516Sdarrenr			if (cmdentry.u.index == BLTINCMD) {
939145516Sdarrenr				if (argc == 1)
940145516Sdarrenr					break;
941145516Sdarrenr				argv++;
942145516Sdarrenr				argc--;
943145516Sdarrenr				bltinonly = 1;
944145516Sdarrenr			} else if (cmdentry.u.index == COMMANDCMD) {
945145516Sdarrenr				if (argc == 1)
946145516Sdarrenr					break;
947145516Sdarrenr				if (!strcmp(argv[1], "-p")) {
948145516Sdarrenr					if (argc == 2)
949145516Sdarrenr						break;
950145516Sdarrenr					if (argv[2][0] == '-') {
951145516Sdarrenr						if (strcmp(argv[2], "--"))
952145516Sdarrenr							break;
953145516Sdarrenr						if (argc == 3)
954145516Sdarrenr							break;
955145516Sdarrenr						argv += 3;
956145516Sdarrenr						argc -= 3;
957145516Sdarrenr					} else {
958145516Sdarrenr						argv += 2;
959145516Sdarrenr						argc -= 2;
960145516Sdarrenr					}
961145516Sdarrenr					path = _PATH_STDPATH;
962145516Sdarrenr					clearcmdentry();
963145516Sdarrenr					do_clearcmdentry = 1;
964145516Sdarrenr				} else if (!strcmp(argv[1], "--")) {
965145516Sdarrenr					if (argc == 2)
966145516Sdarrenr						break;
967145516Sdarrenr					argv += 2;
968145516Sdarrenr					argc -= 2;
969145516Sdarrenr				} else if (argv[1][0] == '-')
970145516Sdarrenr					break;
971145516Sdarrenr				else {
972145516Sdarrenr					argv++;
973145516Sdarrenr					argc--;
974145516Sdarrenr				}
975161356Sguido				cmd_flags |= DO_NOFUNC;
976161356Sguido				bltinonly = 0;
977145516Sdarrenr			} else
978145516Sdarrenr				break;
979178888Sjulian		}
980145516Sdarrenr		/*
981145516Sdarrenr		 * Special builtins lose their special properties when
982145516Sdarrenr		 * called via 'command'.
983145516Sdarrenr		 */
984145516Sdarrenr		if (cmd_flags & DO_NOFUNC)
985145516Sdarrenr			cmdentry.special = 0;
986145516Sdarrenr	}
987145516Sdarrenr
988145516Sdarrenr	/* Fork off a child process if necessary. */
989145516Sdarrenr	if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
990145516Sdarrenr	    && ((flags & EV_EXIT) == 0 || have_traps()))
991145516Sdarrenr	 || ((flags & EV_BACKCMD) != 0
992145516Sdarrenr	    && (cmdentry.cmdtype != CMDBUILTIN ||
993145516Sdarrenr		 !safe_builtin(cmdentry.u.index, argc, argv)))) {
994145516Sdarrenr		jp = makejob(cmd, 1);
995145516Sdarrenr		mode = FORK_FG;
996145516Sdarrenr		if (flags & EV_BACKCMD) {
997145516Sdarrenr			mode = FORK_NOJOB;
998145516Sdarrenr			if (pipe(pip) < 0)
999170268Sdarrenr				error("Pipe call failed: %s", strerror(errno));
1000170268Sdarrenr		}
1001170268Sdarrenr		if (cmdentry.cmdtype == CMDNORMAL &&
1002145516Sdarrenr		    cmd->ncmd.redirect == NULL &&
1003170268Sdarrenr		    varlist.list == NULL &&
1004145516Sdarrenr		    (mode == FORK_FG || mode == FORK_NOJOB) &&
1005145516Sdarrenr		    !disvforkset() && !iflag && !mflag) {
1006145516Sdarrenr			vforkexecshell(jp, argv, environment(), path,
1007145516Sdarrenr			    cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL);
1008145516Sdarrenr			goto parent;
1009145516Sdarrenr		}
1010145516Sdarrenr		if (forkshell(jp, cmd, mode) != 0)
1011145516Sdarrenr			goto parent;	/* at end of routine */
1012170268Sdarrenr		if (flags & EV_BACKCMD) {
1013170268Sdarrenr			FORCEINTON;
1014145516Sdarrenr			close(pip[0]);
1015145516Sdarrenr			if (pip[1] != 1) {
1016145516Sdarrenr				dup2(pip[1], 1);
1017145516Sdarrenr				close(pip[1]);
1018145516Sdarrenr			}
1019145516Sdarrenr			flags &= ~EV_BACKCMD;
1020145516Sdarrenr		}
1021170268Sdarrenr		flags |= EV_EXIT;
1022145516Sdarrenr	}
1023145516Sdarrenr
1024145516Sdarrenr	/* This is the child process if a fork occurred. */
1025145516Sdarrenr	/* Execute the command. */
1026173181Sdarrenr	if (cmdentry.cmdtype == CMDFUNCTION) {
1027145516Sdarrenr#ifdef DEBUG
1028145516Sdarrenr		trputs("Shell function:  ");  trargs(argv);
1029145516Sdarrenr#endif
1030145516Sdarrenr		saveparam = shellparam;
1031145516Sdarrenr		shellparam.malloc = 0;
1032145516Sdarrenr		shellparam.reset = 1;
1033145516Sdarrenr		shellparam.nparam = argc - 1;
1034145516Sdarrenr		shellparam.p = argv + 1;
1035145516Sdarrenr		shellparam.optnext = NULL;
1036145516Sdarrenr		INTOFF;
1037145516Sdarrenr		savelocalvars = localvars;
1038145516Sdarrenr		localvars = NULL;
1039145516Sdarrenr		reffunc(cmdentry.u.func);
1040145516Sdarrenr		savehandler = handler;
1041145516Sdarrenr		if (setjmp(jmploc.loc)) {
1042145516Sdarrenr			freeparam(&shellparam);
1043145516Sdarrenr			shellparam = saveparam;
1044145516Sdarrenr			popredir();
1045145516Sdarrenr			unreffunc(cmdentry.u.func);
1046145516Sdarrenr			poplocalvars();
1047145516Sdarrenr			localvars = savelocalvars;
1048145516Sdarrenr			funcnest--;
1049145516Sdarrenr			handler = savehandler;
1050145516Sdarrenr			longjmp(handler->loc, 1);
1051145516Sdarrenr		}
1052145516Sdarrenr		handler = &jmploc;
1053145516Sdarrenr		funcnest++;
1054145516Sdarrenr		redirect(cmd->ncmd.redirect, REDIR_PUSH);
1055145516Sdarrenr		INTON;
1056145516Sdarrenr		for (sp = varlist.list ; sp ; sp = sp->next)
1057145516Sdarrenr			mklocal(sp->text);
1058145516Sdarrenr		exitstatus = oexitstatus;
1059145516Sdarrenr		evaltree(getfuncnode(cmdentry.u.func),
1060145516Sdarrenr		    flags & (EV_TESTED | EV_EXIT));
1061145516Sdarrenr		INTOFF;
1062145516Sdarrenr		unreffunc(cmdentry.u.func);
1063145516Sdarrenr		poplocalvars();
1064145516Sdarrenr		localvars = savelocalvars;
1065145516Sdarrenr		freeparam(&shellparam);
1066145516Sdarrenr		shellparam = saveparam;
1067145516Sdarrenr		handler = savehandler;
1068145516Sdarrenr		funcnest--;
1069145516Sdarrenr		popredir();
1070145516Sdarrenr		INTON;
1071145516Sdarrenr		if (evalskip == SKIPRETURN) {
1072145516Sdarrenr			evalskip = 0;
1073145516Sdarrenr			skipcount = 0;
1074145516Sdarrenr		}
1075145516Sdarrenr		if (jp)
1076145516Sdarrenr			exitshell(exitstatus);
1077145516Sdarrenr	} else if (cmdentry.cmdtype == CMDBUILTIN) {
1078145516Sdarrenr#ifdef DEBUG
1079145516Sdarrenr		trputs("builtin command:  ");  trargs(argv);
1080145516Sdarrenr#endif
1081145516Sdarrenr		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
1082145516Sdarrenr		if (flags == EV_BACKCMD) {
1083145516Sdarrenr			memout.nleft = 0;
1084145516Sdarrenr			memout.nextc = memout.buf;
1085145516Sdarrenr			memout.bufsize = 64;
1086145516Sdarrenr			mode |= REDIR_BACKQ;
1087145516Sdarrenr		}
1088145516Sdarrenr		savecmdname = commandname;
1089145516Sdarrenr		savetopfile = getcurrentfile();
1090145516Sdarrenr		cmdenviron = varlist.list;
1091145516Sdarrenr		e = -1;
1092145516Sdarrenr		savehandler = handler;
1093145516Sdarrenr		if (setjmp(jmploc.loc)) {
1094145516Sdarrenr			e = exception;
1095145516Sdarrenr			if (e == EXINT)
1096145516Sdarrenr				exitstatus = SIGINT+128;
1097161356Sguido			else if (e != EXEXIT)
1098145516Sdarrenr				exitstatus = 2;
1099145516Sdarrenr			goto cmddone;
1100145516Sdarrenr		}
1101145516Sdarrenr		handler = &jmploc;
1102145516Sdarrenr		redirect(cmd->ncmd.redirect, mode);
1103145516Sdarrenr		outclearerror(out1);
1104145516Sdarrenr		/*
1105145516Sdarrenr		 * If there is no command word, redirection errors should
1106145516Sdarrenr		 * not be fatal but assignment errors should.
1107145516Sdarrenr		 */
1108145516Sdarrenr		if (argc == 0)
1109145516Sdarrenr			cmdentry.special = 1;
1110145516Sdarrenr		listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1111145516Sdarrenr		if (argc > 0)
1112145516Sdarrenr			bltinsetlocale();
1113145516Sdarrenr		commandname = argv[0];
1114145516Sdarrenr		argptr = argv + 1;
1115145516Sdarrenr		nextopt_optptr = NULL;		/* initialize nextopt */
1116145516Sdarrenr		builtin_flags = flags;
1117145516Sdarrenr		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
1118145516Sdarrenr		flushall();
1119145516Sdarrenr		if (outiserror(out1)) {
1120145516Sdarrenr			warning("write error on stdout");
1121145516Sdarrenr			if (exitstatus == 0 || exitstatus == 1)
1122145516Sdarrenr				exitstatus = 2;
1123145516Sdarrenr		}
1124145516Sdarrenrcmddone:
1125145516Sdarrenr		if (argc > 0)
1126145516Sdarrenr			bltinunsetlocale();
1127145516Sdarrenr		cmdenviron = NULL;
1128145516Sdarrenr		out1 = &output;
1129145516Sdarrenr		out2 = &errout;
1130145516Sdarrenr		freestdout();
1131145516Sdarrenr		handler = savehandler;
1132145516Sdarrenr		commandname = savecmdname;
1133145516Sdarrenr		if (jp)
1134145516Sdarrenr			exitshell(exitstatus);
1135145516Sdarrenr		if (flags == EV_BACKCMD) {
1136161356Sguido			backcmd->buf = memout.buf;
1137145516Sdarrenr			backcmd->nleft = memout.nextc - memout.buf;
1138145516Sdarrenr			memout.buf = NULL;
1139145516Sdarrenr		}
1140145516Sdarrenr		if (cmdentry.u.index != EXECCMD)
1141145516Sdarrenr			popredir();
1142145516Sdarrenr		if (e != -1) {
1143145516Sdarrenr			if ((e != EXERROR && e != EXEXEC)
1144145516Sdarrenr			    || cmdentry.special)
1145145516Sdarrenr				exraise(e);
1146145516Sdarrenr			popfilesupto(savetopfile);
1147145516Sdarrenr			if (flags != EV_BACKCMD)
1148145516Sdarrenr				FORCEINTON;
1149145516Sdarrenr		}
1150145516Sdarrenr	} else {
1151145516Sdarrenr#ifdef DEBUG
1152145516Sdarrenr		trputs("normal command:  ");  trargs(argv);
1153145516Sdarrenr#endif
1154145516Sdarrenr		redirect(cmd->ncmd.redirect, 0);
1155145516Sdarrenr		for (sp = varlist.list ; sp ; sp = sp->next)
1156145516Sdarrenr			setvareq(sp->text, VEXPORT|VSTACK);
1157145516Sdarrenr		envp = environment();
1158145516Sdarrenr		shellexec(argv, envp, path, cmdentry.u.index);
1159145516Sdarrenr		/*NOTREACHED*/
1160145516Sdarrenr	}
1161145516Sdarrenr	goto out;
1162145516Sdarrenr
1163145516Sdarrenrparent:	/* parent process gets here (if we forked) */
1164145516Sdarrenr	if (mode == FORK_FG) {	/* argument to fork */
1165145516Sdarrenr		INTOFF;
1166145516Sdarrenr		exitstatus = waitforjob(jp, &realstatus);
1167178888Sjulian		INTON;
1168145516Sdarrenr		if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) {
1169145516Sdarrenr			evalskip = SKIPBREAK;
1170145516Sdarrenr			skipcount = loopnest;
1171145516Sdarrenr		}
1172145516Sdarrenr	} else if (mode == FORK_NOJOB) {
1173145516Sdarrenr		backcmd->fd = pip[0];
1174145516Sdarrenr		close(pip[1]);
1175145516Sdarrenr		backcmd->jp = jp;
1176145516Sdarrenr	}
1177145516Sdarrenr
1178145516Sdarrenrout:
1179145516Sdarrenr	if (lastarg)
1180145516Sdarrenr		setvar("_", lastarg, 0);
1181145516Sdarrenr	if (do_clearcmdentry)
1182145516Sdarrenr		clearcmdentry();
1183145516Sdarrenr}
1184145516Sdarrenr
1185145516Sdarrenr
1186145516Sdarrenr
1187145516Sdarrenr/*
1188145516Sdarrenr * Search for a command.  This is called before we fork so that the
1189145516Sdarrenr * location of the command will be available in the parent as well as
1190145516Sdarrenr * the child.  The check for "goodname" is an overly conservative
1191145516Sdarrenr * check that the name will not be subject to expansion.
1192145516Sdarrenr */
1193145516Sdarrenr
1194145516Sdarrenrstatic void
1195145516Sdarrenrprehash(union node *n)
1196145516Sdarrenr{
1197145516Sdarrenr	struct cmdentry entry;
1198145516Sdarrenr
1199145516Sdarrenr	if (n && n->type == NCMD && n->ncmd.args)
1200145516Sdarrenr		if (goodname(n->ncmd.args->narg.text))
1201145516Sdarrenr			find_command(n->ncmd.args->narg.text, &entry, 0,
1202145516Sdarrenr				     pathval());
1203145516Sdarrenr}
1204145516Sdarrenr
1205145516Sdarrenr
1206145516Sdarrenr
1207145516Sdarrenr/*
1208145516Sdarrenr * Builtin commands.  Builtin commands whose functions are closely
1209145516Sdarrenr * tied to evaluation are implemented here.
1210145516Sdarrenr */
1211145516Sdarrenr
1212145516Sdarrenr/*
1213145516Sdarrenr * No command given, a bltin command with no arguments, or a bltin command
1214145516Sdarrenr * with an invalid name.
1215145516Sdarrenr */
1216145516Sdarrenr
1217145516Sdarrenrint
1218145516Sdarrenrbltincmd(int argc, char **argv)
1219145516Sdarrenr{
1220145516Sdarrenr	if (argc > 1) {
1221145516Sdarrenr		out2fmt_flush("%s: not found\n", argv[1]);
1222145516Sdarrenr		return 127;
1223145516Sdarrenr	}
1224145516Sdarrenr	/*
1225145516Sdarrenr	 * Preserve exitstatus of a previous possible redirection
1226145516Sdarrenr	 * as POSIX mandates
1227145516Sdarrenr	 */
1228145516Sdarrenr	return exitstatus;
1229145516Sdarrenr}
1230145516Sdarrenr
1231145516Sdarrenr
1232145516Sdarrenr/*
1233145516Sdarrenr * Handle break and continue commands.  Break, continue, and return are
1234145516Sdarrenr * all handled by setting the evalskip flag.  The evaluation routines
1235145516Sdarrenr * above all check this flag, and if it is set they start skipping
1236145516Sdarrenr * commands rather than executing them.  The variable skipcount is
1237145516Sdarrenr * the number of loops to break/continue, or the number of function
1238145516Sdarrenr * levels to return.  (The latter is always 1.)  It should probably
1239161356Sguido * be an error to break out of more loops than exist, but it isn't
1240161356Sguido * in the standard shell so we don't make it one here.
1241161356Sguido */
1242145516Sdarrenr
1243145516Sdarrenrint
1244145516Sdarrenrbreakcmd(int argc, char **argv)
1245145516Sdarrenr{
1246145516Sdarrenr	int n = argc > 1 ? number(argv[1]) : 1;
1247145516Sdarrenr
1248145516Sdarrenr	if (n > loopnest)
1249145516Sdarrenr		n = loopnest;
1250145516Sdarrenr	if (n > 0) {
1251145516Sdarrenr		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1252145516Sdarrenr		skipcount = n;
1253145516Sdarrenr	}
1254145516Sdarrenr	return 0;
1255145516Sdarrenr}
1256145516Sdarrenr
1257145516Sdarrenr/*
1258145516Sdarrenr * The `command' command.
1259145516Sdarrenr */
1260145516Sdarrenrint
1261145516Sdarrenrcommandcmd(int argc __unused, char **argv __unused)
1262145516Sdarrenr{
1263145516Sdarrenr	const char *path;
1264145516Sdarrenr	int ch;
1265145516Sdarrenr	int cmd = -1;
1266145516Sdarrenr
1267145516Sdarrenr	path = bltinlookup("PATH", 1);
1268145516Sdarrenr
1269145516Sdarrenr	while ((ch = nextopt("pvV")) != '\0') {
1270145516Sdarrenr		switch (ch) {
1271145516Sdarrenr		case 'p':
1272145516Sdarrenr			path = _PATH_STDPATH;
1273145516Sdarrenr			break;
1274145516Sdarrenr		case 'v':
1275145516Sdarrenr			cmd = TYPECMD_SMALLV;
1276145516Sdarrenr			break;
1277145516Sdarrenr		case 'V':
1278145516Sdarrenr			cmd = TYPECMD_BIGV;
1279145516Sdarrenr			break;
1280145516Sdarrenr		}
1281145516Sdarrenr	}
1282145516Sdarrenr
1283145516Sdarrenr	if (cmd != -1) {
1284145516Sdarrenr		if (*argptr == NULL || argptr[1] != NULL)
1285145516Sdarrenr			error("wrong number of arguments");
1286145516Sdarrenr		return typecmd_impl(2, argptr - 1, cmd, path);
1287145516Sdarrenr	}
1288145516Sdarrenr	if (*argptr != NULL)
1289145516Sdarrenr		error("commandcmd bad call");
1290145516Sdarrenr
1291145516Sdarrenr	/*
1292145516Sdarrenr	 * Do nothing successfully if no command was specified;
1293145516Sdarrenr	 * ksh also does this.
1294145516Sdarrenr	 */
1295145516Sdarrenr	return 0;
1296145516Sdarrenr}
1297145516Sdarrenr
1298145516Sdarrenr
1299145516Sdarrenr/*
1300145516Sdarrenr * The return command.
1301145516Sdarrenr */
1302145516Sdarrenr
1303145516Sdarrenrint
1304145516Sdarrenrreturncmd(int argc, char **argv)
1305145516Sdarrenr{
1306145516Sdarrenr	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1307145516Sdarrenr
1308145516Sdarrenr	evalskip = SKIPRETURN;
1309145516Sdarrenr	skipcount = 1;
1310145516Sdarrenr	return ret;
1311145516Sdarrenr}
1312145516Sdarrenr
1313145516Sdarrenr
1314145516Sdarrenrint
1315145516Sdarrenrfalsecmd(int argc __unused, char **argv __unused)
1316145516Sdarrenr{
1317145516Sdarrenr	return 1;
1318145516Sdarrenr}
1319145516Sdarrenr
1320145516Sdarrenr
1321145516Sdarrenrint
1322145516Sdarrenrtruecmd(int argc __unused, char **argv __unused)
1323145516Sdarrenr{
1324145516Sdarrenr	return 0;
1325145516Sdarrenr}
1326145516Sdarrenr
1327145516Sdarrenr
1328145516Sdarrenrint
1329145516Sdarrenrexeccmd(int argc, char **argv)
1330145516Sdarrenr{
1331145516Sdarrenr	/*
1332145516Sdarrenr	 * Because we have historically not supported any options,
1333145516Sdarrenr	 * only treat "--" specially.
1334145516Sdarrenr	 */
1335145516Sdarrenr	if (argc > 1 && strcmp(argv[1], "--") == 0)
1336172776Sdarrenr		argc--, argv++;
1337172776Sdarrenr	if (argc > 1) {
1338172776Sdarrenr		struct strlist *sp;
1339145516Sdarrenr
1340145516Sdarrenr		iflag = 0;		/* exit on error */
1341145516Sdarrenr		mflag = 0;
1342145516Sdarrenr		optschanged();
1343145516Sdarrenr		for (sp = cmdenviron; sp ; sp = sp->next)
1344145516Sdarrenr			setvareq(sp->text, VEXPORT|VSTACK);
1345145516Sdarrenr		shellexec(argv + 1, environment(), pathval(), 0);
1346145516Sdarrenr
1347145516Sdarrenr	}
1348145516Sdarrenr	return 0;
1349145516Sdarrenr}
1350145516Sdarrenr
1351145516Sdarrenr
1352145516Sdarrenrint
1353145516Sdarrenrtimescmd(int argc __unused, char **argv __unused)
1354172776Sdarrenr{
1355145516Sdarrenr	struct rusage ru;
1356172776Sdarrenr	long shumins, shsmins, chumins, chsmins;
1357172776Sdarrenr	double shusecs, shssecs, chusecs, chssecs;
1358172776Sdarrenr
1359172776Sdarrenr	if (getrusage(RUSAGE_SELF, &ru) < 0)
1360145516Sdarrenr		return 1;
1361145516Sdarrenr	shumins = ru.ru_utime.tv_sec / 60;
1362145516Sdarrenr	shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1363145516Sdarrenr	shsmins = ru.ru_stime.tv_sec / 60;
1364145516Sdarrenr	shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1365145516Sdarrenr	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1366145516Sdarrenr		return 1;
1367145516Sdarrenr	chumins = ru.ru_utime.tv_sec / 60;
1368145516Sdarrenr	chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1369145516Sdarrenr	chsmins = ru.ru_stime.tv_sec / 60;
1370145516Sdarrenr	chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1371145516Sdarrenr	out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
1372145516Sdarrenr	    shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
1373145516Sdarrenr	return 0;
1374145516Sdarrenr}
1375145516Sdarrenr