eval.c revision 211287
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/eval.c 211287 2010-08-13 20:29:43Z jilles $");
401556Srgrimes
4117987Speter#include <paths.h>
42149017Sstefanf#include <signal.h>
43209337Sjilles#include <stdlib.h>
4417987Speter#include <unistd.h>
451556Srgrimes#include <sys/resource.h>
461556Srgrimes#include <sys/wait.h> /* For WIFSIGNALED(status) */
471556Srgrimes#include <errno.h>
481556Srgrimes
491556Srgrimes/*
501556Srgrimes * Evaluate a command.
511556Srgrimes */
521556Srgrimes
531556Srgrimes#include "shell.h"
541556Srgrimes#include "nodes.h"
551556Srgrimes#include "syntax.h"
561556Srgrimes#include "expand.h"
571556Srgrimes#include "parser.h"
5817987Speter#include "jobs.h"
5959436Scracauer#include "eval.h"
60214304Sjilles#include "builtins.h"
6117987Speter#include "options.h"
621556Srgrimes#include "exec.h"
6317987Speter#include "redir.h"
641556Srgrimes#include "input.h"
651556Srgrimes#include "output.h"
661556Srgrimes#include "trap.h"
671556Srgrimes#include "var.h"
681556Srgrimes#include "memalloc.h"
69142845Sobrien#include "error.h"
70142845Sobrien#include "show.h"
711556Srgrimes#include "mystring.h"
72214709Sjilles#ifndef NO_HISTORY
73214709Sjilles#include "myhistedit.h"
74214709Sjilles#endif
75214709Sjilles
76214709Sjilles
771556Srgrimesint evalskip;			/* set if we are skipping commands */
7817987SpeterSTATIC int skipcount;		/* number of levels to skip */
791556SrgrimesMKINIT int loopnest;		/* current loop nesting level */
801556Srgrimesint funcnest;			/* depth of function calls */
811556SrgrimesSTATIC int builtin_flags;	/* evalcommand flags for builtins */
821556Srgrimes
831556Srgrimes
841556Srgrimeschar *commandname;
851556Srgrimesstruct strlist *cmdenviron;
861556Srgrimesint exitstatus;			/* exit status of last command */
871556Srgrimesint oexitstatus;		/* saved exit status */
881556Srgrimes
89206145Sjilles
90206145SjillesSTATIC void evalloop(union node *, int);
91206145SjillesSTATIC void evalfor(union node *, int);
92206145SjillesSTATIC void evalcase(union node *, int);
931556SrgrimesSTATIC void evalsubshell(union node *, int);
941556SrgrimesSTATIC void evalredir(union node *, int);
95213760SobrienSTATIC void expredir(union node *);
96213760SobrienSTATIC void evalpipe(union node *);
97213760SobrienSTATIC void evalcommand(union node *, int, struct backcmd *);
98213760SobrienSTATIC void prehash(union node *);
991556Srgrimes
100213760Sobrien
1011556Srgrimes/*
102213760Sobrien * Called to reset things after an exception.
103213760Sobrien */
104213760Sobrien
105213760Sobrien#ifdef mkinit
106213760SobrienINCLUDE "eval.h"
107213760Sobrien
108213760SobrienRESET {
1091556Srgrimes	evalskip = 0;
1101556Srgrimes	loopnest = 0;
111214525Sjilles	funcnest = 0;
112213811Sobrien}
113213811Sobrien
114213811SobrienSHELLPROC {
115213811Sobrien	exitstatus = 0;
116213811Sobrien}
117213811Sobrien#endif
118213811Sobrien
119213811Sobrien
120213811Sobrien
121213811Sobrien/*
122213811Sobrien * The eval command.
123213811Sobrien */
124213811Sobrien
125213811Sobrienint
126213811Sobrienevalcmd(int argc, char **argv)
1271556Srgrimes{
12817987Speter        char *p;
129213811Sobrien        char *concat;
130206145Sjilles        char **ap;
131206145Sjilles
132206145Sjilles        if (argc > 1) {
133206145Sjilles                p = argv[1];
134206145Sjilles                if (argc > 2) {
135206145Sjilles                        STARTSTACKSTR(concat);
136206145Sjilles                        ap = argv + 2;
137206145Sjilles                        for (;;) {
138206145Sjilles                                while (*p)
139206145Sjilles                                        STPUTC(*p++, concat);
140206145Sjilles                                if ((p = *ap++) == NULL)
141206145Sjilles                                        break;
142206145Sjilles                                STPUTC(' ', concat);
143206145Sjilles                        }
144206145Sjilles                        STPUTC('\0', concat);
145213811Sobrien                        p = grabstackstr(concat);
146206145Sjilles                }
147206145Sjilles                evalstring(p, builtin_flags & EV_TESTED);
148206145Sjilles        } else
149206145Sjilles                exitstatus = 0;
150206145Sjilles        return exitstatus;
151206145Sjilles}
152206145Sjilles
153206145Sjilles
154206145Sjilles/*
155206145Sjilles * Execute a command or commands contained in a string.
156206145Sjilles */
157206145Sjilles
158206145Sjillesvoid
159206145Sjillesevalstring(char *s, int flags)
160213811Sobrien{
161206145Sjilles	union node *n;
162206145Sjilles	struct stackmark smark;
163206145Sjilles	int flags_exit;
164206145Sjilles	int any;
165206145Sjilles
166206145Sjilles	flags_exit = flags & EV_EXIT;
167206145Sjilles	flags &= ~EV_EXIT;
168206145Sjilles	any = 0;
169206145Sjilles	setstackmark(&smark);
170206145Sjilles	setinputstring(s, 1);
171206145Sjilles	while ((n = parsecmd(0)) != NEOF) {
172206145Sjilles		if (n != NULL) {
173206145Sjilles			if (flags_exit && preadateof())
174206145Sjilles				evaltree(n, flags | EV_EXIT);
175206145Sjilles			else
176206145Sjilles				evaltree(n, flags);
177206145Sjilles			any = 1;
178206145Sjilles		}
179206145Sjilles		popstackmark(&smark);
180213811Sobrien	}
181206145Sjilles	popfile();
182206145Sjilles	popstackmark(&smark);
183206145Sjilles	if (!any)
184206145Sjilles		exitstatus = 0;
185206145Sjilles	if (flags_exit)
186206145Sjilles		exitshell(exitstatus);
187206145Sjilles}
188206145Sjilles
189206145Sjilles
190206145Sjilles/*
191206145Sjilles * Evaluate a parse tree.  The value is left in the global variable
192206145Sjilles * exitstatus.
193206145Sjilles */
194206145Sjilles
195206145Sjillesvoid
1961556Srgrimesevaltree(union node *n, int flags)
1971556Srgrimes{
1981556Srgrimes	int do_etest;
1991556Srgrimes
2001556Srgrimes	do_etest = 0;
2011556Srgrimes	if (n == NULL) {
20290111Simp		TRACE(("evaltree(NULL) called\n"));
20317987Speter		exitstatus = 0;
2041556Srgrimes		goto out;
2051556Srgrimes	}
206206145Sjilles#ifndef NO_HISTORY
207206145Sjilles	displayhist = 1;	/* show history substitutions done with fc */
208206145Sjilles#endif
209206145Sjilles	TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
210208656Sjilles	switch (n->type) {
211206145Sjilles	case NSEMI:
21260593Scracauer		evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
2131556Srgrimes		if (evalskip)
2141556Srgrimes			goto out;
2151556Srgrimes		evaltree(n->nbinary.ch2, flags);
2161556Srgrimes		break;
2171556Srgrimes	case NAND:
2181556Srgrimes		evaltree(n->nbinary.ch1, EV_TESTED);
2191556Srgrimes		if (evalskip || exitstatus != 0) {
2201556Srgrimes			goto out;
2211556Srgrimes		}
2221556Srgrimes		evaltree(n->nbinary.ch2, flags);
2231556Srgrimes		break;
2241556Srgrimes	case NOR:
225214531Sjilles		evaltree(n->nbinary.ch1, EV_TESTED);
2261556Srgrimes		if (evalskip || exitstatus == 0)
2271556Srgrimes			goto out;
2281556Srgrimes		evaltree(n->nbinary.ch2, flags);
229213811Sobrien		break;
230214525Sjilles	case NREDIR:
23117987Speter		evalredir(n, flags);
232214599Sjilles		break;
23317987Speter	case NSUBSHELL:
2341556Srgrimes		evalsubshell(n, flags);
235214709Sjilles		do_etest = !(flags & EV_TESTED);
236214531Sjilles		break;
2371556Srgrimes	case NBACKGND:
238214599Sjilles		evalsubshell(n, flags);
2391556Srgrimes		break;
24017987Speter	case NIF: {
24117987Speter		evaltree(n->nif.test, EV_TESTED);
24217987Speter		if (evalskip)
24317987Speter			goto out;
24417987Speter		if (exitstatus == 0)
24517987Speter			evaltree(n->nif.ifpart, flags);
24617987Speter		else if (n->nif.elsepart)
24717987Speter			evaltree(n->nif.elsepart, flags);
24817987Speter		else
24917987Speter			exitstatus = 0;
25017987Speter		break;
25117987Speter	}
25217987Speter	case NWHILE:
25317987Speter	case NUNTIL:
25417987Speter		evalloop(n, flags & ~EV_EXIT);
255214599Sjilles		break;
256214599Sjilles	case NFOR:
257214599Sjilles		evalfor(n, flags & ~EV_EXIT);
258214599Sjilles		break;
259214599Sjilles	case NCASE:
260214599Sjilles		evalcase(n, flags);
261214599Sjilles		break;
262214599Sjilles	case NDEFUN:
26317987Speter		defun(n->narg.text, n->narg.next);
26417987Speter		exitstatus = 0;
26517987Speter		break;
26617987Speter	case NNOT:
267214599Sjilles		evaltree(n->nnot.com, EV_TESTED);
26817987Speter		exitstatus = !exitstatus;
269214599Sjilles		break;
27017987Speter
27117987Speter	case NPIPE:
27217987Speter		evalpipe(n);
27313882Sjoerg		do_etest = !(flags & EV_TESTED);
27417987Speter		break;
27517987Speter	case NCMD:
276102410Scharnier		evalcommand(n, flags, (struct backcmd *)NULL);
2771556Srgrimes		do_etest = !(flags & EV_TESTED);
27817987Speter		break;
27917987Speter	default:
28017987Speter		out1fmt("Node type = %d\n", n->type);
281214599Sjilles		flushout(&output);
282210488Sjilles		break;
283210488Sjilles	}
284214599Sjillesout:
28517987Speter	if (pendingsigs)
28617987Speter		dotrap();
28717987Speter	if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest))
288214709Sjilles		exitshell(exitstatus);
289214531Sjilles}
290214599Sjilles
2911556Srgrimes
2921556SrgrimesSTATIC void
2931556Srgrimesevalloop(union node *n, int flags)
2941556Srgrimes{
2951556Srgrimes	int status;
2961556Srgrimes
297214599Sjilles	loopnest++;
2981556Srgrimes	status = 0;
299214525Sjilles	for (;;) {
3001556Srgrimes		evaltree(n->nbinary.ch1, EV_TESTED);
3011556Srgrimes		if (evalskip) {
302214599Sjillesskipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
3031556Srgrimes				evalskip = 0;
3041556Srgrimes				continue;
3051556Srgrimes			}
3061556Srgrimes			if (evalskip == SKIPBREAK && --skipcount <= 0)
3071556Srgrimes				evalskip = 0;
3081556Srgrimes			break;
309213811Sobrien		}
31090111Simp		if (n->type == NWHILE) {
31190111Simp			if (exitstatus != 0)
3121556Srgrimes				break;
3131556Srgrimes		} else {
3141556Srgrimes			if (exitstatus == 0)
3151556Srgrimes				break;
3161556Srgrimes		}
3171556Srgrimes		evaltree(n->nbinary.ch2, flags);
3181556Srgrimes		status = exitstatus;
3191556Srgrimes		if (evalskip)
3201556Srgrimes			goto skipping;
3211556Srgrimes	}
3221556Srgrimes	loopnest--;
3231556Srgrimes	exitstatus = status;
3241556Srgrimes}
3251556Srgrimes
3261556Srgrimes
3271556Srgrimes
3281556SrgrimesSTATIC void
3291556Srgrimesevalfor(union node *n, int flags)
3301556Srgrimes{
3311556Srgrimes	struct arglist arglist;
3321556Srgrimes	union node *argp;
3331556Srgrimes	struct strlist *sp;
3341556Srgrimes	struct stackmark smark;
3351556Srgrimes
336213811Sobrien	setstackmark(&smark);
33790111Simp	arglist.lastp = &arglist.list;
33890111Simp	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
33975336Sbrian		oexitstatus = exitstatus;
3401556Srgrimes		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
341214281Sjilles		if (evalskip)
3421556Srgrimes			goto out;
34375336Sbrian	}
344214709Sjilles	*arglist.lastp = NULL;
3451556Srgrimes
34675336Sbrian	exitstatus = 0;
34775336Sbrian	loopnest++;
34875336Sbrian	for (sp = arglist.list ; sp ; sp = sp->next) {
3491556Srgrimes		setvar(n->nfor.var, sp->text, 0);
3501556Srgrimes		evaltree(n->nfor.body, flags);
3511556Srgrimes		if (evalskip) {
3521556Srgrimes			if (evalskip == SKIPCONT && --skipcount <= 0) {
3531556Srgrimes				evalskip = 0;
3541556Srgrimes				continue;
3551556Srgrimes			}
3561556Srgrimes			if (evalskip == SKIPBREAK && --skipcount <= 0)
3571556Srgrimes				evalskip = 0;
3581556Srgrimes			break;
3591556Srgrimes		}
360214709Sjilles	}
361214281Sjilles	loopnest--;
362214281Sjillesout:
363214281Sjilles	popstackmark(&smark);
364214281Sjilles}
365214281Sjilles
366214281Sjilles
3671556Srgrimes
3681556SrgrimesSTATIC void
3691556Srgrimesevalcase(union node *n, int flags)
3701556Srgrimes{
3711556Srgrimes	union node *cp;
3721556Srgrimes	union node *patp;
37375336Sbrian	struct arglist arglist;
37475336Sbrian	struct stackmark smark;
37575336Sbrian
37675336Sbrian	setstackmark(&smark);
37775336Sbrian	arglist.lastp = &arglist.list;
37875336Sbrian	oexitstatus = exitstatus;
37975336Sbrian	exitstatus = 0;
3801556Srgrimes	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
3811556Srgrimes	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
3821556Srgrimes		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
3831556Srgrimes			if (casematch(patp, arglist.list->text)) {
384213811Sobrien				if (evalskip == 0) {
38590111Simp					evaltree(cp->nclist.body, flags);
38690111Simp				}
3871556Srgrimes				goto out;
3881556Srgrimes			}
3891556Srgrimes		}
3901556Srgrimes	}
391214281Sjillesout:
392218325Sjilles	popstackmark(&smark);
3931556Srgrimes}
394214709Sjilles
395218325Sjilles
39617987Speter
39717987Speter/*
3981556Srgrimes * Kick off a subshell to evaluate a tree.
39920425Ssteve */
4001556Srgrimes
4011556SrgrimesSTATIC void
4021556Srgrimesevalsubshell(union node *n, int flags)
4031556Srgrimes{
4041556Srgrimes	struct job *jp;
4051556Srgrimes	int backgnd = (n->type == NBACKGND);
4061556Srgrimes
4071556Srgrimes	expredir(n->nredir.redirect);
4081556Srgrimes	if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
4091556Srgrimes			forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
4101556Srgrimes		if (backgnd)
4111556Srgrimes			flags &=~ EV_TESTED;
412214525Sjilles		redirect(n->nredir.redirect, 0);
413104554Stjr		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
4141556Srgrimes	} else if (! backgnd) {
4151556Srgrimes		INTOFF;
416214525Sjilles		exitstatus = waitforjob(jp, (int *)NULL);
4171556Srgrimes		INTON;
4181556Srgrimes	}
4191556Srgrimes}
4201556Srgrimes
4211556Srgrimes
422214525Sjilles/*
423104554Stjr * Evaluate a redirected compound command.
4241556Srgrimes */
4251556Srgrimes
426214525SjillesSTATIC void
4271556Srgrimesevalredir(union node *n, int flags)
4281556Srgrimes{
429214525Sjilles	struct jmploc jmploc;
4301556Srgrimes	struct jmploc *savehandler;
4311556Srgrimes	volatile int in_redirect = 1;
4321556Srgrimes
4331556Srgrimes	expredir(n->nredir.redirect);
4341556Srgrimes	savehandler = handler;
4351556Srgrimes	if (setjmp(jmploc.loc)) {
436214709Sjilles		int e;
4371556Srgrimes
4381556Srgrimes		handler = savehandler;
4391556Srgrimes		e = exception;
4401556Srgrimes		if (e == EXERROR || e == EXEXEC) {
4411556Srgrimes			popredir();
4421556Srgrimes			if (in_redirect) {
443214525Sjilles				exitstatus = 2;
444104554Stjr				return;
4451556Srgrimes			}
4461556Srgrimes		}
4471556Srgrimes		longjmp(handler->loc, 1);
4481556Srgrimes	} else {
449214525Sjilles		INTOFF;
4501556Srgrimes		handler = &jmploc;
4511556Srgrimes		redirect(n->nredir.redirect, REDIR_PUSH);
452214709Sjilles		in_redirect = 0;
4531556Srgrimes		INTON;
4541556Srgrimes		evaltree(n->nredir.n, flags);
4551556Srgrimes	}
4561556Srgrimes	INTOFF;
4571556Srgrimes	handler = savehandler;
4581556Srgrimes	popredir();
4591556Srgrimes	INTON;
4601556Srgrimes}
461199282Sjilles
462199282Sjilles
463199282Sjilles/*
4641556Srgrimes * Compute the names of the files in a redirection list.
4651556Srgrimes */
4661556Srgrimes
4671556SrgrimesSTATIC void
4681556Srgrimesexpredir(union node *n)
4691556Srgrimes{
4701556Srgrimes	union node *redir;
4711556Srgrimes
4721556Srgrimes	for (redir = n ; redir ; redir = redir->nfile.next) {
4731556Srgrimes		struct arglist fn;
4741556Srgrimes		fn.lastp = &fn.list;
4751556Srgrimes		oexitstatus = exitstatus;
4761556Srgrimes		switch (redir->type) {
4771556Srgrimes		case NFROM:
478149096Sstefanf		case NTO:
479149096Sstefanf		case NFROMTO:
480149096Sstefanf		case NAPPEND:
4811556Srgrimes		case NCLOBBER:
4821556Srgrimes			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
483149096Sstefanf			redir->nfile.expfname = fn.list->text;
4841556Srgrimes			break;
4851556Srgrimes		case NFROMFD:
4861556Srgrimes		case NTOFD:
4871556Srgrimes			if (redir->ndup.vname) {
4881556Srgrimes				expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
4891556Srgrimes				fixredir(redir, fn.list->text, 1);
4901556Srgrimes			}
4911556Srgrimes			break;
4921556Srgrimes		}
4931556Srgrimes	}
494214709Sjilles}
4951556Srgrimes
4961556Srgrimes
4971556Srgrimes
4981556Srgrimes/*
4991556Srgrimes * Evaluate a pipeline.  All the processes in the pipeline are children
5001556Srgrimes * of the process creating the pipeline.  (This differs from some versions
501214525Sjilles * of the shell, which make the last process in a pipeline the parent
5021556Srgrimes * of all the rest.)
5031556Srgrimes */
504214709Sjilles
5051556SrgrimesSTATIC void
5061556Srgrimesevalpipe(union node *n)
5071556Srgrimes{
5081556Srgrimes	struct job *jp;
5091556Srgrimes	struct nodelist *lp;
5101556Srgrimes	int pipelen;
5111556Srgrimes	int prevfd;
5121556Srgrimes	int pip[2];
5131556Srgrimes
5141556Srgrimes	TRACE(("evalpipe(%p) called\n", (void *)n));
5151556Srgrimes	pipelen = 0;
5161556Srgrimes	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
5171556Srgrimes		pipelen++;
5181556Srgrimes	INTOFF;
5191556Srgrimes	jp = makejob(n, pipelen);
520214709Sjilles	prevfd = -1;
521104202Stjr	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
5221556Srgrimes		prehash(lp->n);
5231556Srgrimes		pip[1] = -1;
5241556Srgrimes		if (lp->next) {
525104207Stjr			if (pipe(pip) < 0) {
526104207Stjr				close(prevfd);
5271556Srgrimes				error("Pipe call failed: %s", strerror(errno));
5281556Srgrimes			}
5291556Srgrimes		}
5301556Srgrimes		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
5311556Srgrimes			INTON;
532214709Sjilles			if (prevfd > 0) {
533214709Sjilles				dup2(prevfd, 0);
5341556Srgrimes				close(prevfd);
5351556Srgrimes			}
5362760Ssef			if (pip[1] >= 0) {
5371556Srgrimes				if (!(prevfd >= 0 && pip[0] == 0))
5381556Srgrimes					close(pip[0]);
5391556Srgrimes				if (pip[1] != 1) {
540214709Sjilles					dup2(pip[1], 1);
541214525Sjilles					close(pip[1]);
5422760Ssef				}
543214709Sjilles			}
5442760Ssef			evaltree(lp->n, EV_EXIT);
5452760Ssef		}
546214709Sjilles		if (prevfd >= 0)
5472760Ssef			close(prevfd);
548214709Sjilles		prevfd = pip[0];
5492760Ssef		close(pip[1]);
5501556Srgrimes	}
551104202Stjr	INTON;
5521556Srgrimes	if (n->npipe.backgnd == 0) {
553214709Sjilles		INTOFF;
5541556Srgrimes		exitstatus = waitforjob(jp, (int *)NULL);
5551556Srgrimes		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
5561556Srgrimes		INTON;
5571556Srgrimes	}
558214525Sjilles}
5591556Srgrimes
5601556Srgrimes
5611556Srgrimes
562214709Sjilles/*
563218325Sjilles * Execute a command inside back quotes.  If it's a builtin command, we
5641556Srgrimes * want to save its output in a block obtained from malloc.  Otherwise
5651556Srgrimes * we fork off a subprocess and get the output of the command via a pipe.
566214525Sjilles * Should be called with interrupts off.
5671556Srgrimes */
5681556Srgrimes
569214709Sjillesvoid
5701556Srgrimesevalbackcmd(union node *n, struct backcmd *result)
5711556Srgrimes{
572210221Sjilles	int pip[2];
57317987Speter	struct job *jp;
574101662Stjr	struct stackmark smark;		/* unnecessary */
575101662Stjr
57617987Speter	setstackmark(&smark);
57717987Speter	result->fd = -1;
57817987Speter	result->buf = NULL;
57917987Speter	result->nleft = 0;
58017987Speter	result->jp = NULL;
58117987Speter	if (n == NULL) {
5821556Srgrimes		exitstatus = 0;
58310399Sjoerg		goto out;
5841556Srgrimes	}
58517987Speter	if (n->type == NCMD) {
5861556Srgrimes		exitstatus = oexitstatus;
58775160Sbrian		evalcommand(n, EV_BACKCMD, result);
588214281Sjilles	} else {
5891556Srgrimes		exitstatus = 0;
5901556Srgrimes		if (pipe(pip) < 0)
5911556Srgrimes			error("Pipe call failed: %s", strerror(errno));
5921556Srgrimes		jp = makejob(n, 1);
5931556Srgrimes		if (forkshell(jp, n, FORK_NOJOB) == 0) {
5941556Srgrimes			FORCEINTON;
5951556Srgrimes			close(pip[0]);
5961556Srgrimes			if (pip[1] != 1) {
5971556Srgrimes				dup2(pip[1], 1);
5981556Srgrimes				close(pip[1]);
5991556Srgrimes			}
6001556Srgrimes			evaltree(n, EV_EXIT);
6011556Srgrimes		}
602218325Sjilles		close(pip[1]);
6031556Srgrimes		result->fd = pip[0];
6041556Srgrimes		result->jp = jp;
6051556Srgrimes	}
6061556Srgrimesout:
6071556Srgrimes	popstackmark(&smark);
6081556Srgrimes	TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
6091556Srgrimes		result->fd, result->buf, result->nleft, result->jp));
61075160Sbrian}
611214281Sjilles
6121556Srgrimes
6131556Srgrimes
6141556Srgrimes/*
615213811Sobrien * Execute a simple command.
61690111Simp */
61790111Simp
6181556SrgrimesSTATIC void
6191556Srgrimesevalcommand(union node *cmd, int flags, struct backcmd *backcmd)
620210087Sjilles{
621214304Sjilles	struct stackmark smark;
6221556Srgrimes	union node *argp;
6231556Srgrimes	struct arglist arglist;
6241556Srgrimes	struct arglist varlist;
6251556Srgrimes	char **argv;
6261556Srgrimes	int argc;
6271556Srgrimes	char **envp;
6281556Srgrimes	int varflag;
6291556Srgrimes	struct strlist *sp;
6308855Srgrimes	int mode;
6311556Srgrimes	int pip[2];
6321556Srgrimes	struct cmdentry cmdentry;
6338855Srgrimes	struct job *jp;
6341556Srgrimes	struct jmploc jmploc;
6351556Srgrimes	struct jmploc *savehandler;
6361556Srgrimes	char *savecmdname;
6371556Srgrimes	struct shparam saveparam;
6381556Srgrimes	struct localvar *savelocalvars;
6391556Srgrimes	struct parsefile *savetopfile;
6401556Srgrimes	volatile int e;
6411556Srgrimes	char *lastarg;
6421556Srgrimes	int realstatus;
6431556Srgrimes	int do_clearcmdentry;
6441556Srgrimes	const char *path = pathval();
6451556Srgrimes
6461556Srgrimes	/* First expand the arguments. */
6471556Srgrimes	TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
6481556Srgrimes	setstackmark(&smark);
6491556Srgrimes	arglist.lastp = &arglist.list;
6501556Srgrimes	varlist.lastp = &varlist.list;
6511556Srgrimes	varflag = 1;
6521556Srgrimes	do_clearcmdentry = 0;
6531556Srgrimes	oexitstatus = exitstatus;
654179022Sstefanf	exitstatus = 0;
655214291Sjilles	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
656214291Sjilles		char *p = argp->narg.text;
657214291Sjilles		if (varflag && is_name(*p)) {
658214534Sjilles			do {
659214534Sjilles				p++;
660214291Sjilles			} while (is_in_name(*p));
661214291Sjilles			if (*p == '=') {
662214534Sjilles				expandarg(argp, &varlist, EXP_VARTILDE);
663214534Sjilles				continue;
664214534Sjilles			}
6651556Srgrimes		}
666214291Sjilles		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
667214304Sjilles		varflag = 0;
668214304Sjilles	}
669214304Sjilles	*arglist.lastp = NULL;
6701556Srgrimes	*varlist.lastp = NULL;
6711556Srgrimes	expredir(cmd->ncmd.redirect);
672179022Sstefanf	argc = 0;
673210087Sjilles	for (sp = arglist.list ; sp ; sp = sp->next)
6741556Srgrimes		argc++;
6751556Srgrimes	argv = stalloc(sizeof (char *) * (argc + 1));
6761556Srgrimes
6771556Srgrimes	for (sp = arglist.list ; sp ; sp = sp->next) {
6781556Srgrimes		TRACE(("evalcommand arg: %s\n", sp->text));
6791556Srgrimes		*argv++ = sp->text;
6801556Srgrimes	}
6811556Srgrimes	*argv = NULL;
6821556Srgrimes	lastarg = NULL;
6831556Srgrimes	if (iflag && funcnest == 0 && argc > 0)
6841556Srgrimes		lastarg = argv[-1];
6851556Srgrimes	argv -= argc;
686210087Sjilles
6871556Srgrimes	/* Print the command if xflag is set. */
6881556Srgrimes	if (xflag) {
689213811Sobrien		char sep = 0;
69090111Simp		const char *p;
69190111Simp		out2str(ps4val());
69217987Speter		for (sp = varlist.list ; sp ; sp = sp->next) {
6931556Srgrimes			if (sep != 0)
69417987Speter				out2c(' ');
69517987Speter			p = sp->text;
69617987Speter			while (*p != '=' && *p != '\0')
69717987Speter				out2c(*p++);
69817987Speter			if (*p != '\0') {
69917987Speter				out2c(*p++);
70017987Speter				out2qstr(p);
70117987Speter			}
702213760Sobrien			sep = ' ';
703213760Sobrien		}
70490111Simp		for (sp = arglist.list ; sp ; sp = sp->next) {
70517987Speter			if (sep != 0)
70617987Speter				out2c(' ');
70717987Speter			/* Disambiguate command looking like assignment. */
70817987Speter			if (sp == arglist.list &&
70917987Speter					strchr(sp->text, '=') != NULL &&
71017987Speter					strchr(sp->text, '\'') == NULL) {
71117987Speter				out2c('\'');
71217987Speter				out2str(sp->text);
71317987Speter				out2c('\'');
71420425Ssteve			} else
71517987Speter				out2qstr(sp->text);
71617987Speter			sep = ' ';
71717987Speter		}
71817987Speter		out2c('\n');
71917987Speter		flushout(&errout);
72017987Speter	}
72117987Speter
72217987Speter	/* Now locate the command. */
723213811Sobrien	if (argc == 0) {
72490111Simp		/* Variable assignment(s) without command */
72590111Simp		cmdentry.cmdtype = CMDBUILTIN;
7261556Srgrimes		cmdentry.u.index = BLTINCMD;
7271556Srgrimes		cmdentry.special = 0;
7281556Srgrimes	} else {
7291556Srgrimes		static const char PATH[] = "PATH=";
7301556Srgrimes		int cmd_flags = 0, bltinonly = 0;
7311556Srgrimes
7321556Srgrimes		/*
7331556Srgrimes		 * Modify the command lookup path, if a PATH= assignment
7341556Srgrimes		 * is present
7351556Srgrimes		 */
7361556Srgrimes		for (sp = varlist.list ; sp ; sp = sp->next)
7371556Srgrimes			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) {
7381556Srgrimes				path = sp->text + sizeof(PATH) - 1;
7391556Srgrimes				/*
7401556Srgrimes				 * On `PATH=... command`, we need to make
7411556Srgrimes				 * sure that the command isn't using the
7421556Srgrimes				 * non-updated hash table of the outer PATH
7431556Srgrimes				 * setting and we need to make sure that
7441556Srgrimes				 * the hash table isn't filled with items
7451556Srgrimes				 * from the temporary setting.
7461556Srgrimes				 *
7471556Srgrimes				 * It would be better to forbit using and
7481556Srgrimes				 * updating the table while this command
7491556Srgrimes				 * runs, by the command finding mechanism
7501556Srgrimes				 * is heavily integrated with hash handling,
7511556Srgrimes				 * so we just delete the hash before and after
7521556Srgrimes				 * the command runs. Partly deleting like
7531556Srgrimes				 * changepatch() does doesn't seem worth the
75417987Speter				 * bookinging effort, since most such runs add
7551556Srgrimes				 * directories in front of the new PATH.
75617987Speter				 */
7571556Srgrimes				clearcmdentry(0);
7581556Srgrimes				do_clearcmdentry = 1;
7591556Srgrimes			}
7601556Srgrimes
7611556Srgrimes		for (;;) {
7621556Srgrimes			if (bltinonly) {
7631556Srgrimes				cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
7641556Srgrimes				if (cmdentry.u.index < 0) {
765213811Sobrien					cmdentry.u.index = BLTINCMD;
76690111Simp					argv--;
76790111Simp					argc++;
7681556Srgrimes					break;
7691556Srgrimes				}
7701556Srgrimes			} else
7711556Srgrimes				find_command(argv[0], &cmdentry, cmd_flags, path);
7721556Srgrimes			/* implement the bltin and command builtins here */
7731556Srgrimes			if (cmdentry.cmdtype != CMDBUILTIN)
7741556Srgrimes				break;
7751556Srgrimes			if (cmdentry.u.index == BLTINCMD) {
7761556Srgrimes				if (argc == 1)
7771556Srgrimes					break;
7781556Srgrimes				argv++;
7791556Srgrimes				argc--;
7801556Srgrimes				bltinonly = 1;
7811556Srgrimes			} else if (cmdentry.u.index == COMMANDCMD) {
7821556Srgrimes				if (argc == 1)
7831556Srgrimes					break;
7841556Srgrimes				if (!strcmp(argv[1], "-p")) {
7851556Srgrimes					if (argc == 2)
7861556Srgrimes						break;
7871556Srgrimes					if (argv[2][0] == '-') {
7881556Srgrimes						if (strcmp(argv[2], "--"))
789213811Sobrien							break;
79090111Simp						if (argc == 3)
79190111Simp							break;
7921556Srgrimes						argv += 3;
7931556Srgrimes						argc -= 3;
7941556Srgrimes					} else {
7951556Srgrimes						argv += 2;
7961556Srgrimes						argc -= 2;
7971556Srgrimes					}
7981556Srgrimes					path = _PATH_STDPATH;
799213811Sobrien					clearcmdentry(0);
80090111Simp					do_clearcmdentry = 1;
80190111Simp				} else if (!strcmp(argv[1], "--")) {
8021556Srgrimes					if (argc == 2)
8031556Srgrimes						break;
8041556Srgrimes					argv += 2;
8051556Srgrimes					argc -= 2;
8061556Srgrimes				} else if (argv[1][0] == '-')
8078855Srgrimes					break;
8081556Srgrimes				else {
8091556Srgrimes					argv++;
8101556Srgrimes					argc--;
811214709Sjilles				}
812214709Sjilles				cmd_flags |= DO_NOFUNC;
813214709Sjilles				bltinonly = 0;
814214709Sjilles			} else
815214709Sjilles				break;
816214709Sjilles		}
817214709Sjilles		/*
818214709Sjilles		 * Special builtins lose their special properties when
819214709Sjilles		 * called via 'command'.
8201556Srgrimes		 */
821214709Sjilles		if (cmd_flags & DO_NOFUNC)
822214709Sjilles			cmdentry.special = 0;
823214709Sjilles	}
824214709Sjilles
825214709Sjilles	/* Fork off a child process if necessary. */
826214709Sjilles	if (cmd->ncmd.backgnd
827214709Sjilles	 || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
828214709Sjilles	    && ((flags & EV_EXIT) == 0 || have_traps()))
82998463Sjmallett	 || ((flags & EV_BACKCMD) != 0
83020425Ssteve	    && (cmdentry.cmdtype != CMDBUILTIN
83117987Speter		 || cmdentry.u.index == CDCMD
8321556Srgrimes		 || cmdentry.u.index == DOTCMD
8331556Srgrimes		 || cmdentry.u.index == EVALCMD))) {
8341556Srgrimes		jp = makejob(cmd, 1);
8351556Srgrimes		mode = cmd->ncmd.backgnd;
8361556Srgrimes		if (flags & EV_BACKCMD) {
837214709Sjilles			mode = FORK_NOJOB;
838214709Sjilles			if (pipe(pip) < 0)
839214709Sjilles				error("Pipe call failed: %s", strerror(errno));
840214709Sjilles		}
8411556Srgrimes		if (forkshell(jp, cmd, mode) != 0)
842214709Sjilles			goto parent;	/* at end of routine */
8431556Srgrimes		if (flags & EV_BACKCMD) {
844214709Sjilles			FORCEINTON;
845214709Sjilles			close(pip[0]);
846214709Sjilles			if (pip[1] != 1) {
8471556Srgrimes				dup2(pip[1], 1);
8481556Srgrimes				close(pip[1]);
8491556Srgrimes			}
8501556Srgrimes		}
8511556Srgrimes		flags |= EV_EXIT;
8521556Srgrimes	}
8531556Srgrimes
8541556Srgrimes	/* This is the child process if a fork occurred. */
8551556Srgrimes	/* Execute the command. */
8561556Srgrimes	if (cmdentry.cmdtype == CMDFUNCTION) {
8571556Srgrimes#ifdef DEBUG
8581556Srgrimes		trputs("Shell function:  ");  trargs(argv);
8591556Srgrimes#endif
8601556Srgrimes		saveparam = shellparam;
8611556Srgrimes		shellparam.malloc = 0;
8621556Srgrimes		shellparam.reset = 1;
8631556Srgrimes		shellparam.nparam = argc - 1;
8641556Srgrimes		shellparam.p = argv + 1;
8651556Srgrimes		shellparam.optnext = NULL;
8661556Srgrimes		INTOFF;
8671556Srgrimes		savelocalvars = localvars;
8681556Srgrimes		localvars = NULL;
8691556Srgrimes		reffunc(cmdentry.u.func);
8701556Srgrimes		savehandler = handler;
8711556Srgrimes		if (setjmp(jmploc.loc)) {
8721556Srgrimes			if (exception == EXSHELLPROC)
8731556Srgrimes				freeparam(&saveparam);
8741556Srgrimes			else {
8751556Srgrimes				freeparam(&shellparam);
8761556Srgrimes				shellparam = saveparam;
877213811Sobrien				if (exception == EXERROR || exception == EXEXEC)
87890111Simp					popredir();
87990111Simp			}
88025230Ssteve			unreffunc(cmdentry.u.func);
8811556Srgrimes			poplocalvars();
8821556Srgrimes			localvars = savelocalvars;
8831556Srgrimes			funcnest--;
8841556Srgrimes			handler = savehandler;
8851556Srgrimes			longjmp(handler->loc, 1);
8861556Srgrimes		}
8871556Srgrimes		handler = &jmploc;
8881556Srgrimes		funcnest++;
8891556Srgrimes		redirect(cmd->ncmd.redirect, REDIR_PUSH);
8901556Srgrimes		INTON;
8911556Srgrimes		for (sp = varlist.list ; sp ; sp = sp->next)
8921556Srgrimes			mklocal(sp->text);
8931556Srgrimes		exitstatus = oexitstatus;
8941556Srgrimes		if (flags & EV_TESTED)
8951556Srgrimes			evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
8961556Srgrimes		else
8971556Srgrimes			evaltree(getfuncnode(cmdentry.u.func), 0);
8981556Srgrimes		INTOFF;
8991556Srgrimes		unreffunc(cmdentry.u.func);
9001556Srgrimes		poplocalvars();
9011556Srgrimes		localvars = savelocalvars;
9021556Srgrimes		freeparam(&shellparam);
9031556Srgrimes		shellparam = saveparam;
9041556Srgrimes		handler = savehandler;
9051556Srgrimes		funcnest--;
9061556Srgrimes		popredir();
9071556Srgrimes		INTON;
9081556Srgrimes		if (evalskip == SKIPFUNC) {
9091556Srgrimes			evalskip = 0;
9101556Srgrimes			skipcount = 0;
9111556Srgrimes		}
9121556Srgrimes		if (flags & EV_EXIT)
9131556Srgrimes			exitshell(exitstatus);
9141556Srgrimes	} else if (cmdentry.cmdtype == CMDBUILTIN) {
9151556Srgrimes#ifdef DEBUG
9161556Srgrimes		trputs("builtin command:  ");  trargs(argv);
9171556Srgrimes#endif
9181556Srgrimes		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
9191556Srgrimes		if (flags == EV_BACKCMD) {
9201556Srgrimes			memout.nleft = 0;
9211556Srgrimes			memout.nextc = memout.buf;
9221556Srgrimes			memout.bufsize = 64;
9231556Srgrimes			mode |= REDIR_BACKQ;
9241556Srgrimes			cmdentry.special = 0;
9251556Srgrimes		}
9261556Srgrimes		savecmdname = commandname;
9271556Srgrimes		savetopfile = getcurrentfile();
9281556Srgrimes		cmdenviron = varlist.list;
9291556Srgrimes		e = -1;
9301556Srgrimes		savehandler = handler;
9311556Srgrimes		if (setjmp(jmploc.loc)) {
9321556Srgrimes			e = exception;
9331556Srgrimes			exitstatus = (e == EXINT)? SIGINT+128 : 2;
9341556Srgrimes			goto cmddone;
9351556Srgrimes		}
9361556Srgrimes		handler = &jmploc;
9371556Srgrimes		redirect(cmd->ncmd.redirect, mode);
9381556Srgrimes		/*
9391556Srgrimes		 * If there is no command word, redirection errors should
9401556Srgrimes		 * not be fatal but assignment errors should.
9411556Srgrimes		 */
9421556Srgrimes		if (argc == 0 && !(flags & EV_BACKCMD))
9431556Srgrimes			cmdentry.special = 1;
9441556Srgrimes		if (cmdentry.special)
9451556Srgrimes			listsetvar(cmdenviron);
946213811Sobrien		if (argc > 0)
947206145Sjilles			bltinsetlocale();
948206145Sjilles		commandname = argv[0];
949206145Sjilles		argptr = argv + 1;
950206145Sjilles		nextopt_optptr = NULL;		/* initialize nextopt */
951206145Sjilles		builtin_flags = flags;
952206145Sjilles		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
953206145Sjilles		flushall();
954206145Sjillescmddone:
955206145Sjilles		if (argc > 0)
956206145Sjilles			bltinunsetlocale();
957206145Sjilles		cmdenviron = NULL;
958206145Sjilles		out1 = &output;
959206145Sjilles		out2 = &errout;
960206145Sjilles		freestdout();
961205130Sjilles		if (e != EXSHELLPROC) {
962205130Sjilles			commandname = savecmdname;
963205130Sjilles			if (flags & EV_EXIT) {
9641556Srgrimes				exitshell(exitstatus);
965213811Sobrien			}
966205130Sjilles		}
967205130Sjilles		handler = savehandler;
968205130Sjilles		if (flags == EV_BACKCMD) {
969205130Sjilles			backcmd->buf = memout.buf;
970205130Sjilles			backcmd->nleft = memout.nextc - memout.buf;
971205130Sjilles			memout.buf = NULL;
972205130Sjilles		}
973205130Sjilles		if (cmdentry.u.index != EXECCMD &&
974205130Sjilles				(e == -1 || e == EXERROR || e == EXEXEC))
975205130Sjilles			popredir();
976205130Sjilles		if (e != -1) {
977205130Sjilles			if ((e != EXERROR && e != EXEXEC)
978205130Sjilles			    || cmdentry.special)
979208655Sjilles				exraise(e);
980208655Sjilles			popfilesupto(savetopfile);
981205130Sjilles			if (flags != EV_BACKCMD)
982205130Sjilles				FORCEINTON;
983205130Sjilles		}
984205130Sjilles	} else {
985205130Sjilles#ifdef DEBUG
986205130Sjilles		trputs("normal command:  ");  trargs(argv);
987205130Sjilles#endif
988205130Sjilles		redirect(cmd->ncmd.redirect, 0);
989208655Sjilles		for (sp = varlist.list ; sp ; sp = sp->next)
990205130Sjilles			setvareq(sp->text, VEXPORT|VSTACK);
991205130Sjilles		envp = environment();
992205130Sjilles		shellexec(argv, envp, path, cmdentry.u.index);
993205130Sjilles		/*NOTREACHED*/
994205130Sjilles	}
995205130Sjilles	goto out;
996205130Sjilles
997205130Sjillesparent:	/* parent process gets here (if we forked) */
998205130Sjilles	if (mode == 0) {	/* argument to fork */
999205130Sjilles		INTOFF;
1000205130Sjilles		exitstatus = waitforjob(jp, &realstatus);
1001205130Sjilles		INTON;
1002205130Sjilles		if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) {
1003205130Sjilles			evalskip = SKIPBREAK;
1004208655Sjilles			skipcount = loopnest;
1005205130Sjilles		}
1006205130Sjilles	} else if (mode == 2) {
1007205130Sjilles		backcmd->fd = pip[0];
1008205130Sjilles		close(pip[1]);
1009205130Sjilles		backcmd->jp = jp;
1010205130Sjilles	}
1011205130Sjilles
1012205130Sjillesout:
1013205130Sjilles	if (lastarg)
1014205130Sjilles		setvar("_", lastarg, 0);
1015205130Sjilles	if (do_clearcmdentry)
1016205130Sjilles		clearcmdentry(0);
1017205130Sjilles	popstackmark(&smark);
1018205130Sjilles}
1019205130Sjilles
1020205130Sjilles
1021215783Sjilles
1022205130Sjilles/*
1023205130Sjilles * Search for a command.  This is called before we fork so that the
1024205130Sjilles * location of the command will be available in the parent as well as
1025205130Sjilles * the child.  The check for "goodname" is an overly conservative
1026205130Sjilles * check that the name will not be subject to expansion.
1027205130Sjilles */
1028205130Sjilles
1029205130SjillesSTATIC void
1030205130Sjillesprehash(union node *n)
1031205130Sjilles{
1032205130Sjilles	struct cmdentry entry;
1033205130Sjilles
1034205130Sjilles	if (n && n->type == NCMD && n->ncmd.args)
1035205130Sjilles		if (goodname(n->ncmd.args->narg.text))
1036215783Sjilles			find_command(n->ncmd.args->narg.text, &entry, 0,
1037205130Sjilles				     pathval());
1038205130Sjilles}
1039205130Sjilles
1040205130Sjilles
1041205130Sjilles
1042205130Sjilles/*
1043215783Sjilles * Builtin commands.  Builtin commands whose functions are closely
1044205130Sjilles * tied to evaluation are implemented here.
1045205130Sjilles */
1046205130Sjilles
1047205130Sjilles/*
1048205130Sjilles * No command given, a bltin command with no arguments, or a bltin command
1049205130Sjilles * with an invalid name.
1050205130Sjilles */
1051205130Sjilles
1052205130Sjillesint
1053205130Sjillesbltincmd(int argc, char **argv)
1054205130Sjilles{
1055205130Sjilles	if (argc > 1) {
1056205130Sjilles		out2fmt_flush("%s: not found\n", argv[1]);
1057205130Sjilles		return 127;
1058205130Sjilles	}
1059215783Sjilles	/*
1060205130Sjilles	 * Preserve exitstatus of a previous possible redirection
1061205130Sjilles	 * as POSIX mandates
1062215783Sjilles	 */
1063205130Sjilles	return exitstatus;
1064205130Sjilles}
1065205130Sjilles
1066205130Sjilles
1067205130Sjilles/*
1068205130Sjilles * Handle break and continue commands.  Break, continue, and return are
1069205130Sjilles * all handled by setting the evalskip flag.  The evaluation routines
1070205130Sjilles * above all check this flag, and if it is set they start skipping
1071205130Sjilles * commands rather than executing them.  The variable skipcount is
1072205130Sjilles * the number of loops to break/continue, or the number of function
1073205130Sjilles * levels to return.  (The latter is always 1.)  It should probably
1074205130Sjilles * be an error to break out of more loops than exist, but it isn't
1075205130Sjilles * in the standard shell so we don't make it one here.
1076205130Sjilles */
1077205130Sjilles
1078205130Sjillesint
1079205130Sjillesbreakcmd(int argc, char **argv)
1080205130Sjilles{
1081214525Sjilles	int n = argc > 1 ? number(argv[1]) : 1;
1082205130Sjilles
1083205130Sjilles	if (n > loopnest)
1084205130Sjilles		n = loopnest;
1085205130Sjilles	if (n > 0) {
1086205130Sjilles		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1087205130Sjilles		skipcount = n;
1088205130Sjilles	}
1089205130Sjilles	return 0;
1090205130Sjilles}
1091205130Sjilles
1092205130Sjilles/*
1093205130Sjilles * The `command' command.
1094205130Sjilles */
1095205130Sjillesint
1096205130Sjillescommandcmd(int argc, char **argv)
1097205130Sjilles{
1098205130Sjilles	const char *path;
1099205130Sjilles	int ch;
1100216706Sjilles	int cmd = -1;
1101208655Sjilles
1102205130Sjilles	path = bltinlookup("PATH", 1);
1103205130Sjilles
1104205130Sjilles	optind = optreset = 1;
1105205130Sjilles	opterr = 0;
1106205130Sjilles	while ((ch = getopt(argc, argv, "pvV")) != -1) {
1107205130Sjilles		switch (ch) {
1108205130Sjilles		case 'p':
1109205130Sjilles			path = _PATH_STDPATH;
1110205130Sjilles			break;
1111205130Sjilles		case 'v':
1112208655Sjilles			cmd = TYPECMD_SMALLV;
1113208655Sjilles			break;
1114208655Sjilles		case 'V':
1115208655Sjilles			cmd = TYPECMD_BIGV;
1116208655Sjilles			break;
1117208655Sjilles		case '?':
1118208655Sjilles		default:
1119205130Sjilles			error("unknown option: -%c", optopt);
1120208655Sjilles		}
1121205130Sjilles	}
1122205130Sjilles	argc -= optind;
1123205130Sjilles	argv += optind;
1124205130Sjilles
1125205130Sjilles	if (cmd != -1) {
1126205130Sjilles		if (argc != 1)
1127205130Sjilles			error("wrong number of arguments");
1128205130Sjilles		return typecmd_impl(2, argv - 1, cmd, path);
11291556Srgrimes	}
1130221513Sjilles	if (argc != 0)
1131221513Sjilles		error("commandcmd() called while it should not be");
1132221513Sjilles
1133221513Sjilles	/*
1134221513Sjilles	 * Do nothing successfully if no command was specified;
1135221513Sjilles	 * ksh also does this.
1136221513Sjilles	 */
1137221513Sjilles	return 0;
1138221513Sjilles}
1139221513Sjilles
1140221513Sjilles
1141221513Sjilles/*
1142221513Sjilles * The return command.
1143221513Sjilles */
1144221513Sjilles
1145221513Sjillesint
1146221513Sjillesreturncmd(int argc, char **argv)
1147221513Sjilles{
1148221513Sjilles	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1149221513Sjilles
1150221513Sjilles	if (funcnest) {
1151221513Sjilles		evalskip = SKIPFUNC;
1152221513Sjilles		skipcount = 1;
1153221513Sjilles	} else {
1154221513Sjilles		/* skip the rest of the file */
1155221513Sjilles		evalskip = SKIPFILE;
1156221513Sjilles		skipcount = 1;
1157221513Sjilles	}
1158221513Sjilles	return ret;
1159221513Sjilles}
1160221513Sjilles
1161221513Sjilles
1162221513Sjillesint
1163221513Sjillesfalsecmd(int argc __unused, char **argv __unused)
1164221513Sjilles{
1165221513Sjilles	return 1;
1166221513Sjilles}
1167221513Sjilles
1168221513Sjilles
1169221513Sjillesint
1170221513Sjillestruecmd(int argc __unused, char **argv __unused)
1171221513Sjilles{
1172221513Sjilles	return 0;
1173221513Sjilles}
1174221513Sjilles
1175221513Sjilles
1176221513Sjillesint
1177221513Sjillesexeccmd(int argc, char **argv)
1178221513Sjilles{
1179221513Sjilles	/*
1180221513Sjilles	 * Because we have historically not supported any options,
1181221513Sjilles	 * only treat "--" specially.
1182221513Sjilles	 */
1183221513Sjilles	if (argc > 1 && strcmp(argv[1], "--") == 0)
1184221513Sjilles		argc--, argv++;
1185221513Sjilles	if (argc > 1) {
1186221513Sjilles		struct strlist *sp;
1187221513Sjilles
1188221513Sjilles		iflag = 0;		/* exit on error */
1189221513Sjilles		mflag = 0;
1190221513Sjilles		optschanged();
1191221513Sjilles		for (sp = cmdenviron; sp ; sp = sp->next)
1192221513Sjilles			setvareq(sp->text, VEXPORT|VSTACK);
1193221513Sjilles		shellexec(argv + 1, environment(), pathval(), 0);
1194221513Sjilles
1195221513Sjilles	}
1196221513Sjilles	return 0;
1197221513Sjilles}
1198221513Sjilles
1199221513Sjilles
1200221513Sjillesint
1201221513Sjillestimescmd(int argc __unused, char **argv __unused)
1202221513Sjilles{
1203221513Sjilles	struct rusage ru;
1204221513Sjilles	long shumins, shsmins, chumins, chsmins;
1205221513Sjilles	double shusecs, shssecs, chusecs, chssecs;
1206221513Sjilles
1207221513Sjilles	if (getrusage(RUSAGE_SELF, &ru) < 0)
1208221513Sjilles		return 1;
1209221513Sjilles	shumins = ru.ru_utime.tv_sec / 60;
1210221513Sjilles	shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1211221513Sjilles	shsmins = ru.ru_stime.tv_sec / 60;
1212221513Sjilles	shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1213221513Sjilles	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1214221513Sjilles		return 1;
1215221513Sjilles	chumins = ru.ru_utime.tv_sec / 60;
1216221513Sjilles	chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1217221513Sjilles	chsmins = ru.ru_stime.tv_sec / 60;
1218221513Sjilles	chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1219221513Sjilles	out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
1220221513Sjilles	    shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
1221221513Sjilles	return 0;
1222221669Sjilles}
1223221669Sjilles