eval.c revision 218306
1178825Sdfr/*-
2178825Sdfr * Copyright (c) 1993
3178825Sdfr *	The Regents of the University of California.  All rights reserved.
4178825Sdfr *
5178825Sdfr * This code is derived from software contributed to Berkeley by
6178825Sdfr * Kenneth Almquist.
7178825Sdfr *
8178825Sdfr * Redistribution and use in source and binary forms, with or without
9178825Sdfr * modification, are permitted provided that the following conditions
10178825Sdfr * are met:
11233294Sstas * 1. Redistributions of source code must retain the above copyright
12178825Sdfr *    notice, this list of conditions and the following disclaimer.
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr * 4. Neither the name of the University nor the names of its contributors
17178825Sdfr *    may be used to endorse or promote products derived from this software
18178825Sdfr *    without specific prior written permission.
19178825Sdfr *
20178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30178825Sdfr * SUCH DAMAGE.
31178825Sdfr */
32178825Sdfr
33233294Sstas#ifndef lint
34178825Sdfr#if 0
35178825Sdfrstatic char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
36178825Sdfr#endif
37178825Sdfr#endif /* not lint */
38178825Sdfr#include <sys/cdefs.h>
39178825Sdfr__FBSDID("$FreeBSD: head/bin/sh/eval.c 218306 2011-02-04 22:47:55Z jilles $");
40178825Sdfr
41178825Sdfr#include <paths.h>
42178825Sdfr#include <signal.h>
43178825Sdfr#include <stdlib.h>
44178825Sdfr#include <unistd.h>
45178825Sdfr#include <sys/resource.h>
46178825Sdfr#include <sys/wait.h> /* For WIFSIGNALED(status) */
47178825Sdfr#include <errno.h>
48178825Sdfr
49178825Sdfr/*
50178825Sdfr * Evaluate a command.
51178825Sdfr */
52178825Sdfr
53178825Sdfr#include "shell.h"
54178825Sdfr#include "nodes.h"
55178825Sdfr#include "syntax.h"
56178825Sdfr#include "expand.h"
57178825Sdfr#include "parser.h"
58178825Sdfr#include "jobs.h"
59178825Sdfr#include "eval.h"
60178825Sdfr#include "builtins.h"
61178825Sdfr#include "options.h"
62178825Sdfr#include "exec.h"
63178825Sdfr#include "redir.h"
64178825Sdfr#include "input.h"
65178825Sdfr#include "output.h"
66178825Sdfr#include "trap.h"
67178825Sdfr#include "var.h"
68178825Sdfr#include "memalloc.h"
69178825Sdfr#include "error.h"
70178825Sdfr#include "show.h"
71178825Sdfr#include "mystring.h"
72178825Sdfr#ifndef NO_HISTORY
73178825Sdfr#include "myhistedit.h"
74178825Sdfr#endif
75178825Sdfr
76178825Sdfr
77178825Sdfrint evalskip;			/* set if we are skipping commands */
78178825Sdfrstatic int skipcount;		/* number of levels to skip */
79178825SdfrMKINIT int loopnest;		/* current loop nesting level */
80178825Sdfrint funcnest;			/* depth of function calls */
81178825Sdfrstatic int builtin_flags;	/* evalcommand flags for builtins */
82178825Sdfr
83178825Sdfr
84178825Sdfrchar *commandname;
85178825Sdfrstruct strlist *cmdenviron;
86178825Sdfrint exitstatus;			/* exit status of last command */
87178825Sdfrint oexitstatus;		/* saved exit status */
88178825Sdfr
89178825Sdfr
90178825Sdfrstatic void evalloop(union node *, int);
91178825Sdfrstatic void evalfor(union node *, int);
92178825Sdfrstatic void evalcase(union node *, int);
93178825Sdfrstatic void evalsubshell(union node *, int);
94178825Sdfrstatic void evalredir(union node *, int);
95178825Sdfrstatic void expredir(union node *);
96233294Sstasstatic void evalpipe(union node *);
97233294Sstasstatic int is_valid_fast_cmdsubst(union node *n);
98178825Sdfrstatic void evalcommand(union node *, int, struct backcmd *);
99178825Sdfrstatic void prehash(union node *);
100178825Sdfr
101233294Sstas
102178825Sdfr/*
103178825Sdfr * Called to reset things after an exception.
104178825Sdfr */
105178825Sdfr
106178825Sdfr#ifdef mkinit
107178825SdfrINCLUDE "eval.h"
108178825Sdfr
109178825SdfrRESET {
110178825Sdfr	evalskip = 0;
111178825Sdfr	loopnest = 0;
112178825Sdfr	funcnest = 0;
113178825Sdfr}
114178825Sdfr#endif
115178825Sdfr
116178825Sdfr
117178825Sdfr
118178825Sdfr/*
119178825Sdfr * The eval command.
120178825Sdfr */
121178825Sdfr
122178825Sdfrint
123178825Sdfrevalcmd(int argc, char **argv)
124178825Sdfr{
125178825Sdfr        char *p;
126178825Sdfr        char *concat;
127178825Sdfr        char **ap;
128178825Sdfr
129178825Sdfr        if (argc > 1) {
130178825Sdfr                p = argv[1];
131178825Sdfr                if (argc > 2) {
132178825Sdfr                        STARTSTACKSTR(concat);
133178825Sdfr                        ap = argv + 2;
134178825Sdfr                        for (;;) {
135178825Sdfr                                STPUTS(p, concat);
136178825Sdfr                                if ((p = *ap++) == NULL)
137178825Sdfr                                        break;
138178825Sdfr                                STPUTC(' ', concat);
139178825Sdfr                        }
140178825Sdfr                        STPUTC('\0', concat);
141178825Sdfr                        p = grabstackstr(concat);
142178825Sdfr                }
143178825Sdfr                evalstring(p, builtin_flags & EV_TESTED);
144178825Sdfr        } else
145178825Sdfr                exitstatus = 0;
146178825Sdfr        return exitstatus;
147178825Sdfr}
148178825Sdfr
149178825Sdfr
150178825Sdfr/*
151178825Sdfr * Execute a command or commands contained in a string.
152178825Sdfr */
153178825Sdfr
154178825Sdfrvoid
155233294Sstasevalstring(char *s, int flags)
156233294Sstas{
157233294Sstas	union node *n;
158233294Sstas	struct stackmark smark;
159178825Sdfr	int flags_exit;
160233294Sstas	int any;
161233294Sstas
162178825Sdfr	flags_exit = flags & EV_EXIT;
163178825Sdfr	flags &= ~EV_EXIT;
164178825Sdfr	any = 0;
165178825Sdfr	setstackmark(&smark);
166178825Sdfr	setinputstring(s, 1);
167178825Sdfr	while ((n = parsecmd(0)) != NEOF) {
168178825Sdfr		if (n != NULL) {
169178825Sdfr			if (flags_exit && preadateof())
170178825Sdfr				evaltree(n, flags | EV_EXIT);
171178825Sdfr			else
172178825Sdfr				evaltree(n, flags);
173178825Sdfr			any = 1;
174178825Sdfr		}
175178825Sdfr		popstackmark(&smark);
176178825Sdfr	}
177178825Sdfr	popfile();
178178825Sdfr	popstackmark(&smark);
179178825Sdfr	if (!any)
180178825Sdfr		exitstatus = 0;
181178825Sdfr	if (flags_exit)
182178825Sdfr		exitshell(exitstatus);
183178825Sdfr}
184178825Sdfr
185178825Sdfr
186178825Sdfr/*
187178825Sdfr * Evaluate a parse tree.  The value is left in the global variable
188178825Sdfr * exitstatus.
189178825Sdfr */
190178825Sdfr
191178825Sdfrvoid
192178825Sdfrevaltree(union node *n, int flags)
193178825Sdfr{
194178825Sdfr	int do_etest;
195178825Sdfr	union node *next;
196178825Sdfr
197178825Sdfr	do_etest = 0;
198178825Sdfr	if (n == NULL) {
199178825Sdfr		TRACE(("evaltree(NULL) called\n"));
200178825Sdfr		exitstatus = 0;
201178825Sdfr		goto out;
202178825Sdfr	}
203233294Sstas	do {
204178825Sdfr		next = NULL;
205178825Sdfr#ifndef NO_HISTORY
206178825Sdfr		displayhist = 1;	/* show history substitutions done with fc */
207178825Sdfr#endif
208178825Sdfr		TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
209178825Sdfr		switch (n->type) {
210178825Sdfr		case NSEMI:
211178825Sdfr			evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
212178825Sdfr			if (evalskip)
213178825Sdfr				goto out;
214178825Sdfr			next = n->nbinary.ch2;
215178825Sdfr			break;
216178825Sdfr		case NAND:
217178825Sdfr			evaltree(n->nbinary.ch1, EV_TESTED);
218178825Sdfr			if (evalskip || exitstatus != 0) {
219178825Sdfr				goto out;
220178825Sdfr			}
221178825Sdfr			next = n->nbinary.ch2;
222178825Sdfr			break;
223178825Sdfr		case NOR:
224178825Sdfr			evaltree(n->nbinary.ch1, EV_TESTED);
225178825Sdfr			if (evalskip || exitstatus == 0)
226178825Sdfr				goto out;
227178825Sdfr			next = n->nbinary.ch2;
228178825Sdfr			break;
229178825Sdfr		case NREDIR:
230178825Sdfr			evalredir(n, flags);
231178825Sdfr			break;
232178825Sdfr		case NSUBSHELL:
233178825Sdfr			evalsubshell(n, flags);
234178825Sdfr			do_etest = !(flags & EV_TESTED);
235178825Sdfr			break;
236178825Sdfr		case NBACKGND:
237178825Sdfr			evalsubshell(n, flags);
238178825Sdfr			break;
239178825Sdfr		case NIF: {
240178825Sdfr			evaltree(n->nif.test, EV_TESTED);
241178825Sdfr			if (evalskip)
242178825Sdfr				goto out;
243178825Sdfr			if (exitstatus == 0)
244178825Sdfr				next = n->nif.ifpart;
245178825Sdfr			else if (n->nif.elsepart)
246178825Sdfr				next = n->nif.elsepart;
247178825Sdfr			else
248178825Sdfr				exitstatus = 0;
249178825Sdfr			break;
250178825Sdfr		}
251178825Sdfr		case NWHILE:
252178825Sdfr		case NUNTIL:
253178825Sdfr			evalloop(n, flags & ~EV_EXIT);
254178825Sdfr			break;
255178825Sdfr		case NFOR:
256178825Sdfr			evalfor(n, flags & ~EV_EXIT);
257178825Sdfr			break;
258178825Sdfr		case NCASE:
259178825Sdfr			evalcase(n, flags);
260178825Sdfr			break;
261178825Sdfr		case NDEFUN:
262178825Sdfr			defun(n->narg.text, n->narg.next);
263178825Sdfr			exitstatus = 0;
264178825Sdfr			break;
265178825Sdfr		case NNOT:
266178825Sdfr			evaltree(n->nnot.com, EV_TESTED);
267178825Sdfr			exitstatus = !exitstatus;
268178825Sdfr			break;
269178825Sdfr
270178825Sdfr		case NPIPE:
271178825Sdfr			evalpipe(n);
272178825Sdfr			do_etest = !(flags & EV_TESTED);
273233294Sstas			break;
274233294Sstas		case NCMD:
275178825Sdfr			evalcommand(n, flags, (struct backcmd *)NULL);
276178825Sdfr			do_etest = !(flags & EV_TESTED);
277178825Sdfr			break;
278178825Sdfr		default:
279178825Sdfr			out1fmt("Node type = %d\n", n->type);
280178825Sdfr			flushout(&output);
281178825Sdfr			break;
282178825Sdfr		}
283178825Sdfr		n = next;
284178825Sdfr	} while (n != NULL);
285178825Sdfrout:
286178825Sdfr	if (pendingsigs)
287178825Sdfr		dotrap();
288178825Sdfr	if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest))
289178825Sdfr		exitshell(exitstatus);
290178825Sdfr}
291178825Sdfr
292178825Sdfr
293178825Sdfrstatic void
294178825Sdfrevalloop(union node *n, int flags)
295178825Sdfr{
296178825Sdfr	int status;
297178825Sdfr
298178825Sdfr	loopnest++;
299178825Sdfr	status = 0;
300178825Sdfr	for (;;) {
301178825Sdfr		evaltree(n->nbinary.ch1, EV_TESTED);
302233294Sstas		if (evalskip) {
303178825Sdfrskipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
304178825Sdfr				evalskip = 0;
305178825Sdfr				continue;
306178825Sdfr			}
307178825Sdfr			if (evalskip == SKIPBREAK && --skipcount <= 0)
308178825Sdfr				evalskip = 0;
309178825Sdfr			if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
310178825Sdfr				status = exitstatus;
311178825Sdfr			break;
312178825Sdfr		}
313178825Sdfr		if (n->type == NWHILE) {
314178825Sdfr			if (exitstatus != 0)
315178825Sdfr				break;
316178825Sdfr		} else {
317178825Sdfr			if (exitstatus == 0)
318178825Sdfr				break;
319178825Sdfr		}
320178825Sdfr		evaltree(n->nbinary.ch2, flags);
321178825Sdfr		status = exitstatus;
322178825Sdfr		if (evalskip)
323178825Sdfr			goto skipping;
324178825Sdfr	}
325178825Sdfr	loopnest--;
326178825Sdfr	exitstatus = status;
327178825Sdfr}
328178825Sdfr
329178825Sdfr
330178825Sdfr
331178825Sdfrstatic void
332178825Sdfrevalfor(union node *n, int flags)
333178825Sdfr{
334178825Sdfr	struct arglist arglist;
335178825Sdfr	union node *argp;
336178825Sdfr	struct strlist *sp;
337178825Sdfr	struct stackmark smark;
338178825Sdfr
339178825Sdfr	setstackmark(&smark);
340178825Sdfr	arglist.lastp = &arglist.list;
341178825Sdfr	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
342178825Sdfr		oexitstatus = exitstatus;
343178825Sdfr		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
344178825Sdfr		if (evalskip)
345178825Sdfr			goto out;
346178825Sdfr	}
347178825Sdfr	*arglist.lastp = NULL;
348178825Sdfr
349178825Sdfr	exitstatus = 0;
350178825Sdfr	loopnest++;
351178825Sdfr	for (sp = arglist.list ; sp ; sp = sp->next) {
352178825Sdfr		setvar(n->nfor.var, sp->text, 0);
353178825Sdfr		evaltree(n->nfor.body, flags);
354178825Sdfr		if (evalskip) {
355178825Sdfr			if (evalskip == SKIPCONT && --skipcount <= 0) {
356178825Sdfr				evalskip = 0;
357178825Sdfr				continue;
358178825Sdfr			}
359178825Sdfr			if (evalskip == SKIPBREAK && --skipcount <= 0)
360178825Sdfr				evalskip = 0;
361178825Sdfr			break;
362178825Sdfr		}
363178825Sdfr	}
364178825Sdfr	loopnest--;
365178825Sdfrout:
366178825Sdfr	popstackmark(&smark);
367178825Sdfr}
368178825Sdfr
369178825Sdfr
370178825Sdfr
371178825Sdfrstatic void
372178825Sdfrevalcase(union node *n, int flags)
373178825Sdfr{
374178825Sdfr	union node *cp;
375178825Sdfr	union node *patp;
376178825Sdfr	struct arglist arglist;
377178825Sdfr	struct stackmark smark;
378178825Sdfr
379178825Sdfr	setstackmark(&smark);
380178825Sdfr	arglist.lastp = &arglist.list;
381178825Sdfr	oexitstatus = exitstatus;
382178825Sdfr	exitstatus = 0;
383178825Sdfr	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
384178825Sdfr	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
385178825Sdfr		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
386178825Sdfr			if (casematch(patp, arglist.list->text)) {
387178825Sdfr				if (evalskip == 0) {
388178825Sdfr					evaltree(cp->nclist.body, flags);
389178825Sdfr				}
390178825Sdfr				goto out;
391178825Sdfr			}
392178825Sdfr		}
393178825Sdfr	}
394178825Sdfrout:
395178825Sdfr	popstackmark(&smark);
396178825Sdfr}
397178825Sdfr
398178825Sdfr
399178825Sdfr
400178825Sdfr/*
401178825Sdfr * Kick off a subshell to evaluate a tree.
402178825Sdfr */
403178825Sdfr
404178825Sdfrstatic void
405178825Sdfrevalsubshell(union node *n, int flags)
406178825Sdfr{
407178825Sdfr	struct job *jp;
408178825Sdfr	int backgnd = (n->type == NBACKGND);
409178825Sdfr
410178825Sdfr	expredir(n->nredir.redirect);
411178825Sdfr	if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
412178825Sdfr			forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
413178825Sdfr		if (backgnd)
414178825Sdfr			flags &=~ EV_TESTED;
415178825Sdfr		redirect(n->nredir.redirect, 0);
416178825Sdfr		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
417178825Sdfr	} else if (! backgnd) {
418178825Sdfr		INTOFF;
419178825Sdfr		exitstatus = waitforjob(jp, (int *)NULL);
420178825Sdfr		INTON;
421178825Sdfr	}
422178825Sdfr}
423178825Sdfr
424178825Sdfr
425178825Sdfr/*
426178825Sdfr * Evaluate a redirected compound command.
427178825Sdfr */
428178825Sdfr
429178825Sdfrstatic void
430178825Sdfrevalredir(union node *n, int flags)
431178825Sdfr{
432178825Sdfr	struct jmploc jmploc;
433178825Sdfr	struct jmploc *savehandler;
434178825Sdfr	volatile int in_redirect = 1;
435178825Sdfr
436178825Sdfr	expredir(n->nredir.redirect);
437178825Sdfr	savehandler = handler;
438178825Sdfr	if (setjmp(jmploc.loc)) {
439178825Sdfr		int e;
440178825Sdfr
441178825Sdfr		handler = savehandler;
442178825Sdfr		e = exception;
443178825Sdfr		if (e == EXERROR || e == EXEXEC) {
444178825Sdfr			popredir();
445178825Sdfr			if (in_redirect) {
446178825Sdfr				exitstatus = 2;
447178825Sdfr				return;
448178825Sdfr			}
449178825Sdfr		}
450178825Sdfr		longjmp(handler->loc, 1);
451178825Sdfr	} else {
452178825Sdfr		INTOFF;
453178825Sdfr		handler = &jmploc;
454178825Sdfr		redirect(n->nredir.redirect, REDIR_PUSH);
455178825Sdfr		in_redirect = 0;
456178825Sdfr		INTON;
457178825Sdfr		evaltree(n->nredir.n, flags);
458178825Sdfr	}
459178825Sdfr	INTOFF;
460178825Sdfr	handler = savehandler;
461178825Sdfr	popredir();
462178825Sdfr	INTON;
463178825Sdfr}
464178825Sdfr
465178825Sdfr
466178825Sdfr/*
467178825Sdfr * Compute the names of the files in a redirection list.
468178825Sdfr */
469178825Sdfr
470178825Sdfrstatic void
471178825Sdfrexpredir(union node *n)
472178825Sdfr{
473178825Sdfr	union node *redir;
474178825Sdfr
475178825Sdfr	for (redir = n ; redir ; redir = redir->nfile.next) {
476178825Sdfr		struct arglist fn;
477178825Sdfr		fn.lastp = &fn.list;
478233294Sstas		oexitstatus = exitstatus;
479233294Sstas		switch (redir->type) {
480233294Sstas		case NFROM:
481178825Sdfr		case NTO:
482233294Sstas		case NFROMTO:
483233294Sstas		case NAPPEND:
484233294Sstas		case NCLOBBER:
485178825Sdfr			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
486233294Sstas			redir->nfile.expfname = fn.list->text;
487233294Sstas			break;
488178825Sdfr		case NFROMFD:
489233294Sstas		case NTOFD:
490233294Sstas			if (redir->ndup.vname) {
491233294Sstas				expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
492178825Sdfr				fixredir(redir, fn.list->text, 1);
493233294Sstas			}
494233294Sstas			break;
495233294Sstas		}
496178825Sdfr	}
497233294Sstas}
498233294Sstas
499233294Sstas
500233294Sstas
501233294Sstas/*
502233294Sstas * Evaluate a pipeline.  All the processes in the pipeline are children
503233294Sstas * of the process creating the pipeline.  (This differs from some versions
504233294Sstas * of the shell, which make the last process in a pipeline the parent
505233294Sstas * of all the rest.)
506233294Sstas */
507233294Sstas
508178825Sdfrstatic void
509178825Sdfrevalpipe(union node *n)
510178825Sdfr{
511178825Sdfr	struct job *jp;
512178825Sdfr	struct nodelist *lp;
513178825Sdfr	int pipelen;
514178825Sdfr	int prevfd;
515178825Sdfr	int pip[2];
516178825Sdfr
517178825Sdfr	TRACE(("evalpipe(%p) called\n", (void *)n));
518178825Sdfr	pipelen = 0;
519178825Sdfr	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
520178825Sdfr		pipelen++;
521178825Sdfr	INTOFF;
522178825Sdfr	jp = makejob(n, pipelen);
523178825Sdfr	prevfd = -1;
524178825Sdfr	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
525178825Sdfr		prehash(lp->n);
526178825Sdfr		pip[1] = -1;
527178825Sdfr		if (lp->next) {
528178825Sdfr			if (pipe(pip) < 0) {
529233294Sstas				close(prevfd);
530178825Sdfr				error("Pipe call failed: %s", strerror(errno));
531178825Sdfr			}
532178825Sdfr		}
533178825Sdfr		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
534178825Sdfr			INTON;
535178825Sdfr			if (prevfd > 0) {
536178825Sdfr				dup2(prevfd, 0);
537178825Sdfr				close(prevfd);
538178825Sdfr			}
539178825Sdfr			if (pip[1] >= 0) {
540178825Sdfr				if (!(prevfd >= 0 && pip[0] == 0))
541178825Sdfr					close(pip[0]);
542178825Sdfr				if (pip[1] != 1) {
543178825Sdfr					dup2(pip[1], 1);
544178825Sdfr					close(pip[1]);
545178825Sdfr				}
546178825Sdfr			}
547233294Sstas			evaltree(lp->n, EV_EXIT);
548233294Sstas		}
549233294Sstas		if (prevfd >= 0)
550233294Sstas			close(prevfd);
551233294Sstas		prevfd = pip[0];
552233294Sstas		close(pip[1]);
553233294Sstas	}
554233294Sstas	INTON;
555233294Sstas	if (n->npipe.backgnd == 0) {
556233294Sstas		INTOFF;
557233294Sstas		exitstatus = waitforjob(jp, (int *)NULL);
558233294Sstas		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
559233294Sstas		INTON;
560233294Sstas	}
561233294Sstas}
562233294Sstas
563233294Sstas
564233294Sstas
565233294Sstasstatic int
566233294Sstasis_valid_fast_cmdsubst(union node *n)
567233294Sstas{
568233294Sstas	union node *argp;
569233294Sstas
570233294Sstas	if (n->type != NCMD)
571233294Sstas		return 0;
572233294Sstas	for (argp = n->ncmd.args ; argp ; argp = argp->narg.next)
573233294Sstas		if (expandhassideeffects(argp->narg.text))
574233294Sstas			return 0;
575233294Sstas	return 1;
576178825Sdfr}
577178825Sdfr
578178825Sdfr/*
579178825Sdfr * Execute a command inside back quotes.  If it's a builtin command, we
580178825Sdfr * want to save its output in a block obtained from malloc.  Otherwise
581178825Sdfr * we fork off a subprocess and get the output of the command via a pipe.
582178825Sdfr * Should be called with interrupts off.
583178825Sdfr */
584178825Sdfr
585178825Sdfrvoid
586178825Sdfrevalbackcmd(union node *n, struct backcmd *result)
587178825Sdfr{
588178825Sdfr	int pip[2];
589178825Sdfr	struct job *jp;
590178825Sdfr	struct stackmark smark;		/* unnecessary */
591178825Sdfr	struct jmploc jmploc;
592178825Sdfr	struct jmploc *savehandler;
593178825Sdfr
594178825Sdfr	setstackmark(&smark);
595178825Sdfr	result->fd = -1;
596178825Sdfr	result->buf = NULL;
597178825Sdfr	result->nleft = 0;
598178825Sdfr	result->jp = NULL;
599178825Sdfr	if (n == NULL) {
600178825Sdfr		exitstatus = 0;
601178825Sdfr		goto out;
602178825Sdfr	}
603178825Sdfr	if (is_valid_fast_cmdsubst(n)) {
604178825Sdfr		exitstatus = oexitstatus;
605178825Sdfr		savehandler = handler;
606178825Sdfr		if (setjmp(jmploc.loc)) {
607178825Sdfr			if (exception == EXERROR || exception == EXEXEC)
608178825Sdfr				exitstatus = 2;
609178825Sdfr			else if (exception != 0) {
610178825Sdfr				handler = savehandler;
611178825Sdfr				longjmp(handler->loc, 1);
612178825Sdfr			}
613178825Sdfr		} else {
614178825Sdfr			handler = &jmploc;
615178825Sdfr			evalcommand(n, EV_BACKCMD, result);
616233294Sstas		}
617178825Sdfr		handler = savehandler;
618178825Sdfr	} else {
619178825Sdfr		exitstatus = 0;
620178825Sdfr		if (pipe(pip) < 0)
621178825Sdfr			error("Pipe call failed: %s", strerror(errno));
622178825Sdfr		jp = makejob(n, 1);
623178825Sdfr		if (forkshell(jp, n, FORK_NOJOB) == 0) {
624178825Sdfr			FORCEINTON;
625178825Sdfr			close(pip[0]);
626178825Sdfr			if (pip[1] != 1) {
627233294Sstas				dup2(pip[1], 1);
628178825Sdfr				close(pip[1]);
629178825Sdfr			}
630178825Sdfr			evaltree(n, EV_EXIT);
631178825Sdfr		}
632178825Sdfr		close(pip[1]);
633178825Sdfr		result->fd = pip[0];
634178825Sdfr		result->jp = jp;
635178825Sdfr	}
636178825Sdfrout:
637178825Sdfr	popstackmark(&smark);
638178825Sdfr	TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
639178825Sdfr		result->fd, result->buf, result->nleft, result->jp));
640178825Sdfr}
641178825Sdfr
642178825Sdfr/*
643178825Sdfr * Check if a builtin can safely be executed in the same process,
644178825Sdfr * even though it should be in a subshell (command substitution).
645178825Sdfr * Note that jobid, jobs, times and trap can show information not
646178825Sdfr * available in a child process; this is deliberate.
647178825Sdfr * The arguments should already have been expanded.
648178825Sdfr */
649178825Sdfrstatic int
650178825Sdfrsafe_builtin(int idx, int argc, char **argv)
651178825Sdfr{
652178825Sdfr	if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD ||
653178825Sdfr	    idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD ||
654178825Sdfr	    idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD ||
655178825Sdfr	    idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD ||
656178825Sdfr	    idx == TYPECMD)
657178825Sdfr		return (1);
658178825Sdfr	if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
659178825Sdfr	    idx == UMASKCMD)
660178825Sdfr		return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
661178825Sdfr	if (idx == SETCMD)
662178825Sdfr		return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
663178825Sdfr		    argv[1][0] == '+') && argv[1][1] == 'o' &&
664178825Sdfr		    argv[1][2] == '\0'));
665178825Sdfr	return (0);
666178825Sdfr}
667178825Sdfr
668178825Sdfr/*
669178825Sdfr * Execute a simple command.
670178825Sdfr * Note: This may or may not return if (flags & EV_EXIT).
671178825Sdfr */
672178825Sdfr
673178825Sdfrstatic void
674178825Sdfrevalcommand(union node *cmd, int flags, struct backcmd *backcmd)
675178825Sdfr{
676178825Sdfr	struct stackmark smark;
677178825Sdfr	union node *argp;
678178825Sdfr	struct arglist arglist;
679178825Sdfr	struct arglist varlist;
680178825Sdfr	char **argv;
681178825Sdfr	int argc;
682178825Sdfr	char **envp;
683178825Sdfr	int varflag;
684178825Sdfr	struct strlist *sp;
685178825Sdfr	int mode;
686178825Sdfr	int pip[2];
687178825Sdfr	struct cmdentry cmdentry;
688178825Sdfr	struct job *jp;
689178825Sdfr	struct jmploc jmploc;
690178825Sdfr	struct jmploc *savehandler;
691178825Sdfr	char *savecmdname;
692178825Sdfr	struct shparam saveparam;
693178825Sdfr	struct localvar *savelocalvars;
694178825Sdfr	struct parsefile *savetopfile;
695178825Sdfr	volatile int e;
696178825Sdfr	char *lastarg;
697178825Sdfr	int realstatus;
698178825Sdfr	int do_clearcmdentry;
699178825Sdfr	const char *path = pathval();
700178825Sdfr
701178825Sdfr	/* First expand the arguments. */
702178825Sdfr	TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
703178825Sdfr	setstackmark(&smark);
704178825Sdfr	arglist.lastp = &arglist.list;
705178825Sdfr	varlist.lastp = &varlist.list;
706178825Sdfr	varflag = 1;
707178825Sdfr	jp = NULL;
708178825Sdfr	do_clearcmdentry = 0;
709233294Sstas	oexitstatus = exitstatus;
710178825Sdfr	exitstatus = 0;
711233294Sstas	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
712178825Sdfr		char *p = argp->narg.text;
713178825Sdfr		if (varflag && is_name(*p)) {
714178825Sdfr			do {
715178825Sdfr				p++;
716178825Sdfr			} while (is_in_name(*p));
717178825Sdfr			if (*p == '=') {
718178825Sdfr				expandarg(argp, &varlist, EXP_VARTILDE);
719178825Sdfr				continue;
720178825Sdfr			}
721178825Sdfr		}
722178825Sdfr		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
723178825Sdfr		varflag = 0;
724178825Sdfr	}
725178825Sdfr	*arglist.lastp = NULL;
726178825Sdfr	*varlist.lastp = NULL;
727178825Sdfr	expredir(cmd->ncmd.redirect);
728178825Sdfr	argc = 0;
729178825Sdfr	for (sp = arglist.list ; sp ; sp = sp->next)
730178825Sdfr		argc++;
731178825Sdfr	/* Add one slot at the beginning for tryexec(). */
732178825Sdfr	argv = stalloc(sizeof (char *) * (argc + 2));
733178825Sdfr	argv++;
734178825Sdfr
735178825Sdfr	for (sp = arglist.list ; sp ; sp = sp->next) {
736178825Sdfr		TRACE(("evalcommand arg: %s\n", sp->text));
737178825Sdfr		*argv++ = sp->text;
738178825Sdfr	}
739178825Sdfr	*argv = NULL;
740178825Sdfr	lastarg = NULL;
741178825Sdfr	if (iflag && funcnest == 0 && argc > 0)
742178825Sdfr		lastarg = argv[-1];
743178825Sdfr	argv -= argc;
744178825Sdfr
745178825Sdfr	/* Print the command if xflag is set. */
746178825Sdfr	if (xflag) {
747178825Sdfr		char sep = 0;
748178825Sdfr		const char *p;
749178825Sdfr		out2str(ps4val());
750178825Sdfr		for (sp = varlist.list ; sp ; sp = sp->next) {
751178825Sdfr			if (sep != 0)
752178825Sdfr				out2c(' ');
753178825Sdfr			p = strchr(sp->text, '=');
754178825Sdfr			if (p != NULL) {
755178825Sdfr				p++;
756178825Sdfr				outbin(sp->text, p - sp->text, out2);
757178825Sdfr				out2qstr(p);
758178825Sdfr			} else
759178825Sdfr				out2qstr(sp->text);
760178825Sdfr			sep = ' ';
761178825Sdfr		}
762178825Sdfr		for (sp = arglist.list ; sp ; sp = sp->next) {
763178825Sdfr			if (sep != 0)
764178825Sdfr				out2c(' ');
765178825Sdfr			/* Disambiguate command looking like assignment. */
766178825Sdfr			if (sp == arglist.list &&
767178825Sdfr					strchr(sp->text, '=') != NULL &&
768178825Sdfr					strchr(sp->text, '\'') == NULL) {
769178825Sdfr				out2c('\'');
770178825Sdfr				out2str(sp->text);
771178825Sdfr				out2c('\'');
772178825Sdfr			} else
773178825Sdfr				out2qstr(sp->text);
774178825Sdfr			sep = ' ';
775178825Sdfr		}
776178825Sdfr		out2c('\n');
777178825Sdfr		flushout(&errout);
778178825Sdfr	}
779178825Sdfr
780178825Sdfr	/* Now locate the command. */
781178825Sdfr	if (argc == 0) {
782178825Sdfr		/* Variable assignment(s) without command */
783178825Sdfr		cmdentry.cmdtype = CMDBUILTIN;
784178825Sdfr		cmdentry.u.index = BLTINCMD;
785178825Sdfr		cmdentry.special = 0;
786178825Sdfr	} else {
787178825Sdfr		static const char PATH[] = "PATH=";
788178825Sdfr		int cmd_flags = 0, bltinonly = 0;
789178825Sdfr
790178825Sdfr		/*
791178825Sdfr		 * Modify the command lookup path, if a PATH= assignment
792178825Sdfr		 * is present
793178825Sdfr		 */
794178825Sdfr		for (sp = varlist.list ; sp ; sp = sp->next)
795178825Sdfr			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) {
796233294Sstas				path = sp->text + sizeof(PATH) - 1;
797178825Sdfr				/*
798178825Sdfr				 * On `PATH=... command`, we need to make
799178825Sdfr				 * sure that the command isn't using the
800178825Sdfr				 * non-updated hash table of the outer PATH
801233294Sstas				 * setting and we need to make sure that
802178825Sdfr				 * the hash table isn't filled with items
803178825Sdfr				 * from the temporary setting.
804178825Sdfr				 *
805178825Sdfr				 * It would be better to forbit using and
806233294Sstas				 * updating the table while this command
807178825Sdfr				 * runs, by the command finding mechanism
808178825Sdfr				 * is heavily integrated with hash handling,
809178825Sdfr				 * so we just delete the hash before and after
810178825Sdfr				 * the command runs. Partly deleting like
811233294Sstas				 * changepatch() does doesn't seem worth the
812178825Sdfr				 * bookinging effort, since most such runs add
813178825Sdfr				 * directories in front of the new PATH.
814178825Sdfr				 */
815178825Sdfr				clearcmdentry(0);
816233294Sstas				do_clearcmdentry = 1;
817178825Sdfr			}
818178825Sdfr
819178825Sdfr		for (;;) {
820178825Sdfr			if (bltinonly) {
821233294Sstas				cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
822178825Sdfr				if (cmdentry.u.index < 0) {
823178825Sdfr					cmdentry.u.index = BLTINCMD;
824178825Sdfr					argv--;
825178825Sdfr					argc++;
826233294Sstas					break;
827178825Sdfr				}
828178825Sdfr			} else
829178825Sdfr				find_command(argv[0], &cmdentry, cmd_flags, path);
830178825Sdfr			/* implement the bltin and command builtins here */
831233294Sstas			if (cmdentry.cmdtype != CMDBUILTIN)
832178825Sdfr				break;
833178825Sdfr			if (cmdentry.u.index == BLTINCMD) {
834178825Sdfr				if (argc == 1)
835178825Sdfr					break;
836233294Sstas				argv++;
837178825Sdfr				argc--;
838178825Sdfr				bltinonly = 1;
839178825Sdfr			} else if (cmdentry.u.index == COMMANDCMD) {
840178825Sdfr				if (argc == 1)
841233294Sstas					break;
842178825Sdfr				if (!strcmp(argv[1], "-p")) {
843178825Sdfr					if (argc == 2)
844178825Sdfr						break;
845178825Sdfr					if (argv[2][0] == '-') {
846233294Sstas						if (strcmp(argv[2], "--"))
847178825Sdfr							break;
848178825Sdfr						if (argc == 3)
849178825Sdfr							break;
850178825Sdfr						argv += 3;
851178825Sdfr						argc -= 3;
852233294Sstas					} else {
853178825Sdfr						argv += 2;
854178825Sdfr						argc -= 2;
855178825Sdfr					}
856178825Sdfr					path = _PATH_STDPATH;
857233294Sstas					clearcmdentry(0);
858178825Sdfr					do_clearcmdentry = 1;
859178825Sdfr				} else if (!strcmp(argv[1], "--")) {
860178825Sdfr					if (argc == 2)
861178825Sdfr						break;
862233294Sstas					argv += 2;
863178825Sdfr					argc -= 2;
864178825Sdfr				} else if (argv[1][0] == '-')
865178825Sdfr					break;
866178825Sdfr				else {
867233294Sstas					argv++;
868178825Sdfr					argc--;
869178825Sdfr				}
870178825Sdfr				cmd_flags |= DO_NOFUNC;
871178825Sdfr				bltinonly = 0;
872233294Sstas			} else
873178825Sdfr				break;
874178825Sdfr		}
875233294Sstas		/*
876178825Sdfr		 * Special builtins lose their special properties when
877178825Sdfr		 * called via 'command'.
878178825Sdfr		 */
879178825Sdfr		if (cmd_flags & DO_NOFUNC)
880178825Sdfr			cmdentry.special = 0;
881178825Sdfr	}
882178825Sdfr
883178825Sdfr	/* Fork off a child process if necessary. */
884178825Sdfr	if (cmd->ncmd.backgnd
885178825Sdfr	 || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
886178825Sdfr	    && ((flags & EV_EXIT) == 0 || have_traps()))
887178825Sdfr	 || ((flags & EV_BACKCMD) != 0
888178825Sdfr	    && (cmdentry.cmdtype != CMDBUILTIN ||
889178825Sdfr		 !safe_builtin(cmdentry.u.index, argc, argv)))) {
890178825Sdfr		jp = makejob(cmd, 1);
891178825Sdfr		mode = cmd->ncmd.backgnd;
892178825Sdfr		if (flags & EV_BACKCMD) {
893178825Sdfr			mode = FORK_NOJOB;
894178825Sdfr			if (pipe(pip) < 0)
895178825Sdfr				error("Pipe call failed: %s", strerror(errno));
896178825Sdfr		}
897178825Sdfr		if (forkshell(jp, cmd, mode) != 0)
898178825Sdfr			goto parent;	/* at end of routine */
899178825Sdfr		if (flags & EV_BACKCMD) {
900178825Sdfr			FORCEINTON;
901178825Sdfr			close(pip[0]);
902178825Sdfr			if (pip[1] != 1) {
903178825Sdfr				dup2(pip[1], 1);
904178825Sdfr				close(pip[1]);
905178825Sdfr			}
906178825Sdfr		}
907178825Sdfr		flags |= EV_EXIT;
908178825Sdfr	}
909178825Sdfr
910178825Sdfr	/* This is the child process if a fork occurred. */
911178825Sdfr	/* Execute the command. */
912178825Sdfr	if (cmdentry.cmdtype == CMDFUNCTION) {
913178825Sdfr#ifdef DEBUG
914178825Sdfr		trputs("Shell function:  ");  trargs(argv);
915178825Sdfr#endif
916178825Sdfr		saveparam = shellparam;
917178825Sdfr		shellparam.malloc = 0;
918178825Sdfr		shellparam.reset = 1;
919178825Sdfr		shellparam.nparam = argc - 1;
920178825Sdfr		shellparam.p = argv + 1;
921178825Sdfr		shellparam.optnext = NULL;
922178825Sdfr		INTOFF;
923178825Sdfr		savelocalvars = localvars;
924178825Sdfr		localvars = NULL;
925178825Sdfr		reffunc(cmdentry.u.func);
926178825Sdfr		savehandler = handler;
927178825Sdfr		if (setjmp(jmploc.loc)) {
928178825Sdfr			freeparam(&shellparam);
929178825Sdfr			shellparam = saveparam;
930178825Sdfr			if (exception == EXERROR || exception == EXEXEC)
931178825Sdfr				popredir();
932178825Sdfr			unreffunc(cmdentry.u.func);
933178825Sdfr			poplocalvars();
934178825Sdfr			localvars = savelocalvars;
935178825Sdfr			funcnest--;
936178825Sdfr			handler = savehandler;
937178825Sdfr			longjmp(handler->loc, 1);
938178825Sdfr		}
939178825Sdfr		handler = &jmploc;
940178825Sdfr		funcnest++;
941178825Sdfr		redirect(cmd->ncmd.redirect, REDIR_PUSH);
942178825Sdfr		INTON;
943178825Sdfr		for (sp = varlist.list ; sp ; sp = sp->next)
944178825Sdfr			mklocal(sp->text);
945178825Sdfr		exitstatus = oexitstatus;
946178825Sdfr		if (flags & EV_TESTED)
947178825Sdfr			evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
948178825Sdfr		else
949178825Sdfr			evaltree(getfuncnode(cmdentry.u.func), 0);
950178825Sdfr		INTOFF;
951178825Sdfr		unreffunc(cmdentry.u.func);
952178825Sdfr		poplocalvars();
953178825Sdfr		localvars = savelocalvars;
954178825Sdfr		freeparam(&shellparam);
955178825Sdfr		shellparam = saveparam;
956178825Sdfr		handler = savehandler;
957178825Sdfr		funcnest--;
958178825Sdfr		popredir();
959178825Sdfr		INTON;
960178825Sdfr		if (evalskip == SKIPFUNC) {
961178825Sdfr			evalskip = 0;
962178825Sdfr			skipcount = 0;
963178825Sdfr		}
964178825Sdfr		if (jp)
965178825Sdfr			exitshell(exitstatus);
966178825Sdfr	} else if (cmdentry.cmdtype == CMDBUILTIN) {
967178825Sdfr#ifdef DEBUG
968178825Sdfr		trputs("builtin command:  ");  trargs(argv);
969178825Sdfr#endif
970178825Sdfr		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
971178825Sdfr		if (flags == EV_BACKCMD) {
972178825Sdfr			memout.nleft = 0;
973178825Sdfr			memout.nextc = memout.buf;
974178825Sdfr			memout.bufsize = 64;
975178825Sdfr			mode |= REDIR_BACKQ;
976178825Sdfr			cmdentry.special = 0;
977178825Sdfr		}
978178825Sdfr		savecmdname = commandname;
979178825Sdfr		savetopfile = getcurrentfile();
980178825Sdfr		cmdenviron = varlist.list;
981178825Sdfr		e = -1;
982178825Sdfr		savehandler = handler;
983178825Sdfr		if (setjmp(jmploc.loc)) {
984178825Sdfr			e = exception;
985178825Sdfr			exitstatus = (e == EXINT)? SIGINT+128 : 2;
986178825Sdfr			goto cmddone;
987178825Sdfr		}
988178825Sdfr		handler = &jmploc;
989178825Sdfr		redirect(cmd->ncmd.redirect, mode);
990178825Sdfr		/*
991178825Sdfr		 * If there is no command word, redirection errors should
992178825Sdfr		 * not be fatal but assignment errors should.
993178825Sdfr		 */
994178825Sdfr		if (argc == 0 && !(flags & EV_BACKCMD))
995178825Sdfr			cmdentry.special = 1;
996178825Sdfr		listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
997178825Sdfr		if (argc > 0)
998178825Sdfr			bltinsetlocale();
999178825Sdfr		commandname = argv[0];
1000178825Sdfr		argptr = argv + 1;
1001178825Sdfr		nextopt_optptr = NULL;		/* initialize nextopt */
1002178825Sdfr		builtin_flags = flags;
1003178825Sdfr		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
1004178825Sdfr		flushall();
1005178825Sdfrcmddone:
1006178825Sdfr		if (argc > 0)
1007178825Sdfr			bltinunsetlocale();
1008178825Sdfr		cmdenviron = NULL;
1009178825Sdfr		out1 = &output;
1010178825Sdfr		out2 = &errout;
1011178825Sdfr		freestdout();
1012178825Sdfr		handler = savehandler;
1013178825Sdfr		commandname = savecmdname;
1014178825Sdfr		if (jp)
1015178825Sdfr			exitshell(exitstatus);
1016178825Sdfr		if (flags == EV_BACKCMD) {
1017178825Sdfr			backcmd->buf = memout.buf;
1018178825Sdfr			backcmd->nleft = memout.nextc - memout.buf;
1019178825Sdfr			memout.buf = NULL;
1020178825Sdfr		}
1021178825Sdfr		if (cmdentry.u.index != EXECCMD &&
1022178825Sdfr				(e == -1 || e == EXERROR || e == EXEXEC))
1023178825Sdfr			popredir();
1024178825Sdfr		if (e != -1) {
1025178825Sdfr			if ((e != EXERROR && e != EXEXEC)
1026178825Sdfr			    || cmdentry.special)
1027178825Sdfr				exraise(e);
1028178825Sdfr			popfilesupto(savetopfile);
1029178825Sdfr			if (flags != EV_BACKCMD)
1030178825Sdfr				FORCEINTON;
1031178825Sdfr		}
1032178825Sdfr	} else {
1033178825Sdfr#ifdef DEBUG
1034178825Sdfr		trputs("normal command:  ");  trargs(argv);
1035178825Sdfr#endif
1036178825Sdfr		redirect(cmd->ncmd.redirect, 0);
1037178825Sdfr		for (sp = varlist.list ; sp ; sp = sp->next)
1038178825Sdfr			setvareq(sp->text, VEXPORT|VSTACK);
1039178825Sdfr		envp = environment();
1040178825Sdfr		shellexec(argv, envp, path, cmdentry.u.index);
1041178825Sdfr		/*NOTREACHED*/
1042178825Sdfr	}
1043178825Sdfr	goto out;
1044178825Sdfr
1045178825Sdfrparent:	/* parent process gets here (if we forked) */
1046178825Sdfr	if (mode == FORK_FG) {	/* argument to fork */
1047178825Sdfr		INTOFF;
1048178825Sdfr		exitstatus = waitforjob(jp, &realstatus);
1049178825Sdfr		INTON;
1050178825Sdfr		if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) {
1051178825Sdfr			evalskip = SKIPBREAK;
1052178825Sdfr			skipcount = loopnest;
1053178825Sdfr		}
1054178825Sdfr	} else if (mode == FORK_NOJOB) {
1055178825Sdfr		backcmd->fd = pip[0];
1056178825Sdfr		close(pip[1]);
1057178825Sdfr		backcmd->jp = jp;
1058178825Sdfr	}
1059178825Sdfr
1060178825Sdfrout:
1061233294Sstas	if (lastarg)
1062178825Sdfr		setvar("_", lastarg, 0);
1063178825Sdfr	if (do_clearcmdentry)
1064178825Sdfr		clearcmdentry(0);
1065178825Sdfr	popstackmark(&smark);
1066178825Sdfr}
1067178825Sdfr
1068178825Sdfr
1069178825Sdfr
1070178825Sdfr/*
1071178825Sdfr * Search for a command.  This is called before we fork so that the
1072178825Sdfr * location of the command will be available in the parent as well as
1073178825Sdfr * the child.  The check for "goodname" is an overly conservative
1074178825Sdfr * check that the name will not be subject to expansion.
1075233294Sstas */
1076178825Sdfr
1077178825Sdfrstatic void
1078178825Sdfrprehash(union node *n)
1079178825Sdfr{
1080178825Sdfr	struct cmdentry entry;
1081178825Sdfr
1082178825Sdfr	if (n && n->type == NCMD && n->ncmd.args)
1083178825Sdfr		if (goodname(n->ncmd.args->narg.text))
1084178825Sdfr			find_command(n->ncmd.args->narg.text, &entry, 0,
1085178825Sdfr				     pathval());
1086178825Sdfr}
1087178825Sdfr
1088178825Sdfr
1089178825Sdfr
1090178825Sdfr/*
1091178825Sdfr * Builtin commands.  Builtin commands whose functions are closely
1092178825Sdfr * tied to evaluation are implemented here.
1093178825Sdfr */
1094178825Sdfr
1095178825Sdfr/*
1096178825Sdfr * No command given, a bltin command with no arguments, or a bltin command
1097178825Sdfr * with an invalid name.
1098178825Sdfr */
1099178825Sdfr
1100178825Sdfrint
1101178825Sdfrbltincmd(int argc, char **argv)
1102178825Sdfr{
1103178825Sdfr	if (argc > 1) {
1104178825Sdfr		out2fmt_flush("%s: not found\n", argv[1]);
1105178825Sdfr		return 127;
1106178825Sdfr	}
1107178825Sdfr	/*
1108178825Sdfr	 * Preserve exitstatus of a previous possible redirection
1109178825Sdfr	 * as POSIX mandates
1110178825Sdfr	 */
1111178825Sdfr	return exitstatus;
1112178825Sdfr}
1113178825Sdfr
1114178825Sdfr
1115178825Sdfr/*
1116178825Sdfr * Handle break and continue commands.  Break, continue, and return are
1117178825Sdfr * all handled by setting the evalskip flag.  The evaluation routines
1118178825Sdfr * above all check this flag, and if it is set they start skipping
1119178825Sdfr * commands rather than executing them.  The variable skipcount is
1120178825Sdfr * the number of loops to break/continue, or the number of function
1121178825Sdfr * levels to return.  (The latter is always 1.)  It should probably
1122178825Sdfr * be an error to break out of more loops than exist, but it isn't
1123178825Sdfr * in the standard shell so we don't make it one here.
1124178825Sdfr */
1125178825Sdfr
1126178825Sdfrint
1127178825Sdfrbreakcmd(int argc, char **argv)
1128178825Sdfr{
1129178825Sdfr	int n = argc > 1 ? number(argv[1]) : 1;
1130233294Sstas
1131233294Sstas	if (n > loopnest)
1132233294Sstas		n = loopnest;
1133233294Sstas	if (n > 0) {
1134233294Sstas		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1135233294Sstas		skipcount = n;
1136233294Sstas	}
1137233294Sstas	return 0;
1138178825Sdfr}
1139178825Sdfr
1140178825Sdfr/*
1141178825Sdfr * The `command' command.
1142178825Sdfr */
1143178825Sdfrint
1144178825Sdfrcommandcmd(int argc, char **argv)
1145178825Sdfr{
1146178825Sdfr	const char *path;
1147178825Sdfr	int ch;
1148178825Sdfr	int cmd = -1;
1149178825Sdfr
1150178825Sdfr	path = bltinlookup("PATH", 1);
1151178825Sdfr
1152178825Sdfr	optind = optreset = 1;
1153178825Sdfr	opterr = 0;
1154178825Sdfr	while ((ch = getopt(argc, argv, "pvV")) != -1) {
1155178825Sdfr		switch (ch) {
1156178825Sdfr		case 'p':
1157178825Sdfr			path = _PATH_STDPATH;
1158178825Sdfr			break;
1159178825Sdfr		case 'v':
1160178825Sdfr			cmd = TYPECMD_SMALLV;
1161178825Sdfr			break;
1162178825Sdfr		case 'V':
1163178825Sdfr			cmd = TYPECMD_BIGV;
1164178825Sdfr			break;
1165178825Sdfr		case '?':
1166178825Sdfr		default:
1167178825Sdfr			error("unknown option: -%c", optopt);
1168178825Sdfr		}
1169178825Sdfr	}
1170178825Sdfr	argc -= optind;
1171178825Sdfr	argv += optind;
1172178825Sdfr
1173178825Sdfr	if (cmd != -1) {
1174178825Sdfr		if (argc != 1)
1175178825Sdfr			error("wrong number of arguments");
1176178825Sdfr		return typecmd_impl(2, argv - 1, cmd, path);
1177178825Sdfr	}
1178178825Sdfr	if (argc != 0)
1179178825Sdfr		error("commandcmd bad call");
1180178825Sdfr
1181178825Sdfr	/*
1182178825Sdfr	 * Do nothing successfully if no command was specified;
1183178825Sdfr	 * ksh also does this.
1184178825Sdfr	 */
1185178825Sdfr	return 0;
1186178825Sdfr}
1187178825Sdfr
1188178825Sdfr
1189178825Sdfr/*
1190178825Sdfr * The return command.
1191178825Sdfr */
1192178825Sdfr
1193178825Sdfrint
1194178825Sdfrreturncmd(int argc, char **argv)
1195178825Sdfr{
1196178825Sdfr	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1197178825Sdfr
1198178825Sdfr	if (funcnest) {
1199178825Sdfr		evalskip = SKIPFUNC;
1200178825Sdfr		skipcount = 1;
1201178825Sdfr	} else {
1202178825Sdfr		/* skip the rest of the file */
1203178825Sdfr		evalskip = SKIPFILE;
1204178825Sdfr		skipcount = 1;
1205178825Sdfr	}
1206178825Sdfr	return ret;
1207178825Sdfr}
1208178825Sdfr
1209178825Sdfr
1210178825Sdfrint
1211178825Sdfrfalsecmd(int argc __unused, char **argv __unused)
1212178825Sdfr{
1213178825Sdfr	return 1;
1214178825Sdfr}
1215178825Sdfr
1216178825Sdfr
1217178825Sdfrint
1218178825Sdfrtruecmd(int argc __unused, char **argv __unused)
1219178825Sdfr{
1220178825Sdfr	return 0;
1221178825Sdfr}
1222178825Sdfr
1223178825Sdfr
1224178825Sdfrint
1225178825Sdfrexeccmd(int argc, char **argv)
1226178825Sdfr{
1227178825Sdfr	/*
1228233294Sstas	 * Because we have historically not supported any options,
1229178825Sdfr	 * only treat "--" specially.
1230178825Sdfr	 */
1231178825Sdfr	if (argc > 1 && strcmp(argv[1], "--") == 0)
1232178825Sdfr		argc--, argv++;
1233178825Sdfr	if (argc > 1) {
1234178825Sdfr		struct strlist *sp;
1235178825Sdfr
1236178825Sdfr		iflag = 0;		/* exit on error */
1237178825Sdfr		mflag = 0;
1238178825Sdfr		optschanged();
1239178825Sdfr		for (sp = cmdenviron; sp ; sp = sp->next)
1240178825Sdfr			setvareq(sp->text, VEXPORT|VSTACK);
1241178825Sdfr		shellexec(argv + 1, environment(), pathval(), 0);
1242178825Sdfr
1243178825Sdfr	}
1244178825Sdfr	return 0;
1245178825Sdfr}
1246178825Sdfr
1247178825Sdfr
1248178825Sdfrint
1249178825Sdfrtimescmd(int argc __unused, char **argv __unused)
1250178825Sdfr{
1251178825Sdfr	struct rusage ru;
1252178825Sdfr	long shumins, shsmins, chumins, chsmins;
1253178825Sdfr	double shusecs, shssecs, chusecs, chssecs;
1254178825Sdfr
1255178825Sdfr	if (getrusage(RUSAGE_SELF, &ru) < 0)
1256178825Sdfr		return 1;
1257178825Sdfr	shumins = ru.ru_utime.tv_sec / 60;
1258178825Sdfr	shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1259178825Sdfr	shsmins = ru.ru_stime.tv_sec / 60;
1260178825Sdfr	shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1261178825Sdfr	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1262178825Sdfr		return 1;
1263178825Sdfr	chumins = ru.ru_utime.tv_sec / 60;
1264178825Sdfr	chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1265178825Sdfr	chsmins = ru.ru_stime.tv_sec / 60;
1266178825Sdfr	chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1267178825Sdfr	out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
1268178825Sdfr	    shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
1269178825Sdfr	return 0;
1270178825Sdfr}
1271178825Sdfr