xref: /freebsd/bin/sh/eval.c (revision c38fe1d282258915a388832f0315e813a08888b9)
14b88c807SRodney W. Grimes /*-
24b88c807SRodney W. Grimes  * Copyright (c) 1993
34b88c807SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
44b88c807SRodney W. Grimes  *
54b88c807SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
64b88c807SRodney W. Grimes  * Kenneth Almquist.
74b88c807SRodney W. Grimes  *
84b88c807SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
94b88c807SRodney W. Grimes  * modification, are permitted provided that the following conditions
104b88c807SRodney W. Grimes  * are met:
114b88c807SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
124b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
134b88c807SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
144b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
154b88c807SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
16fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
174b88c807SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
184b88c807SRodney W. Grimes  *    without specific prior written permission.
194b88c807SRodney W. Grimes  *
204b88c807SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
214b88c807SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224b88c807SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234b88c807SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
244b88c807SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254b88c807SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264b88c807SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274b88c807SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284b88c807SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294b88c807SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304b88c807SRodney W. Grimes  * SUCH DAMAGE.
314b88c807SRodney W. Grimes  */
324b88c807SRodney W. Grimes 
332babaf74STim J. Robbins #include <paths.h>
34aa9caaf6SPeter Wemm #include <signal.h>
35d1d578b2SGiorgos Keramidas #include <stdlib.h>
36aa9caaf6SPeter Wemm #include <unistd.h>
371974986aSStefan Farfeleder #include <sys/resource.h>
386c48b6cfSMartin Cracauer #include <errno.h>
39aa9caaf6SPeter Wemm 
404b88c807SRodney W. Grimes /*
414b88c807SRodney W. Grimes  * Evaluate a command.
424b88c807SRodney W. Grimes  */
434b88c807SRodney W. Grimes 
444b88c807SRodney W. Grimes #include "shell.h"
454b88c807SRodney W. Grimes #include "nodes.h"
464b88c807SRodney W. Grimes #include "syntax.h"
474b88c807SRodney W. Grimes #include "expand.h"
484b88c807SRodney W. Grimes #include "parser.h"
494b88c807SRodney W. Grimes #include "jobs.h"
504b88c807SRodney W. Grimes #include "eval.h"
514b88c807SRodney W. Grimes #include "builtins.h"
524b88c807SRodney W. Grimes #include "options.h"
534b88c807SRodney W. Grimes #include "exec.h"
544b88c807SRodney W. Grimes #include "redir.h"
554b88c807SRodney W. Grimes #include "input.h"
564b88c807SRodney W. Grimes #include "output.h"
574b88c807SRodney W. Grimes #include "trap.h"
584b88c807SRodney W. Grimes #include "var.h"
594b88c807SRodney W. Grimes #include "memalloc.h"
604b88c807SRodney W. Grimes #include "error.h"
61aa9caaf6SPeter Wemm #include "show.h"
624b88c807SRodney W. Grimes #include "mystring.h"
63aa9caaf6SPeter Wemm #ifndef NO_HISTORY
644b88c807SRodney W. Grimes #include "myhistedit.h"
65aa9caaf6SPeter Wemm #endif
664b88c807SRodney W. Grimes 
674b88c807SRodney W. Grimes 
68384aedabSJilles Tjoelker int evalskip;			/* set if we are skipping commands */
6901a43bcfSJean-Sébastien Pédron int skipcount;			/* number of levels to skip */
700bdd3871SJilles Tjoelker static int loopnest;		/* current loop nesting level */
714b88c807SRodney W. Grimes int funcnest;			/* depth of function calls */
72aa7b6f82SDavid E. O'Brien static int builtin_flags;	/* evalcommand flags for builtins */
734b88c807SRodney W. Grimes 
744b88c807SRodney W. Grimes 
754b88c807SRodney W. Grimes char *commandname;
768ef0ae8aSJilles Tjoelker struct arglist *cmdenviron;
774b88c807SRodney W. Grimes int exitstatus;			/* exit status of last command */
78aa9caaf6SPeter Wemm int oexitstatus;		/* saved exit status */
794b88c807SRodney W. Grimes 
804b88c807SRodney W. Grimes 
8188328642SDavid E. O'Brien static void evalloop(union node *, int);
8288328642SDavid E. O'Brien static void evalfor(union node *, int);
8393fcb251SJilles Tjoelker static union node *evalcase(union node *);
8488328642SDavid E. O'Brien static void evalsubshell(union node *, int);
8588328642SDavid E. O'Brien static void evalredir(union node *, int);
864dc6bdd3SJilles Tjoelker static void exphere(union node *, struct arglist *);
8788328642SDavid E. O'Brien static void expredir(union node *);
8888328642SDavid E. O'Brien static void evalpipe(union node *);
89acd7984fSJilles Tjoelker static int is_valid_fast_cmdsubst(union node *n);
9088328642SDavid E. O'Brien static void evalcommand(union node *, int, struct backcmd *);
9188328642SDavid E. O'Brien static void prehash(union node *);
924b88c807SRodney W. Grimes 
934b88c807SRodney W. Grimes 
944b88c807SRodney W. Grimes /*
954b88c807SRodney W. Grimes  * Called to reset things after an exception.
964b88c807SRodney W. Grimes  */
974b88c807SRodney W. Grimes 
98338b821bSJilles Tjoelker void
reseteval(void)99338b821bSJilles Tjoelker reseteval(void)
100338b821bSJilles Tjoelker {
1014b88c807SRodney W. Grimes 	evalskip = 0;
1024b88c807SRodney W. Grimes 	loopnest = 0;
1034b88c807SRodney W. Grimes }
1044b88c807SRodney W. Grimes 
1054b88c807SRodney W. Grimes 
1064b88c807SRodney W. Grimes /*
10746be34b9SKris Kennaway  * The eval command.
1084b88c807SRodney W. Grimes  */
1094b88c807SRodney W. Grimes 
110aa9caaf6SPeter Wemm int
evalcmd(int argc,char ** argv)1115134c3f7SWarner Losh evalcmd(int argc, char **argv)
1124b88c807SRodney W. Grimes {
1134b88c807SRodney W. Grimes         char *p;
1144b88c807SRodney W. Grimes         char *concat;
1154b88c807SRodney W. Grimes         char **ap;
1164b88c807SRodney W. Grimes 
1174b88c807SRodney W. Grimes         if (argc > 1) {
1184b88c807SRodney W. Grimes                 p = argv[1];
1194b88c807SRodney W. Grimes                 if (argc > 2) {
1204b88c807SRodney W. Grimes                         STARTSTACKSTR(concat);
1214b88c807SRodney W. Grimes                         ap = argv + 2;
1224b88c807SRodney W. Grimes                         for (;;) {
1239d37e157SJilles Tjoelker                                 STPUTS(p, concat);
1244b88c807SRodney W. Grimes                                 if ((p = *ap++) == NULL)
1254b88c807SRodney W. Grimes                                         break;
1264b88c807SRodney W. Grimes                                 STPUTC(' ', concat);
1274b88c807SRodney W. Grimes                         }
1284b88c807SRodney W. Grimes                         STPUTC('\0', concat);
1294b88c807SRodney W. Grimes                         p = grabstackstr(concat);
1304b88c807SRodney W. Grimes                 }
131c5aef537SJilles Tjoelker                 evalstring(p, builtin_flags);
132b84d7af7SJilles Tjoelker         } else
133b84d7af7SJilles Tjoelker                 exitstatus = 0;
1344b88c807SRodney W. Grimes         return exitstatus;
1354b88c807SRodney W. Grimes }
1364b88c807SRodney W. Grimes 
1374b88c807SRodney W. Grimes 
1384b88c807SRodney W. Grimes /*
1394b88c807SRodney W. Grimes  * Execute a command or commands contained in a string.
1404b88c807SRodney W. Grimes  */
1414b88c807SRodney W. Grimes 
1424b88c807SRodney W. Grimes void
evalstring(const char * s,int flags)14322afca9bSJilles Tjoelker evalstring(const char *s, int flags)
1444b88c807SRodney W. Grimes {
1454b88c807SRodney W. Grimes 	union node *n;
1464b88c807SRodney W. Grimes 	struct stackmark smark;
147960da934SJilles Tjoelker 	int flags_exit;
148b84d7af7SJilles Tjoelker 	int any;
1494b88c807SRodney W. Grimes 
150960da934SJilles Tjoelker 	flags_exit = flags & EV_EXIT;
151960da934SJilles Tjoelker 	flags &= ~EV_EXIT;
152b84d7af7SJilles Tjoelker 	any = 0;
1534b88c807SRodney W. Grimes 	setstackmark(&smark);
1544b88c807SRodney W. Grimes 	setinputstring(s, 1);
1554b88c807SRodney W. Grimes 	while ((n = parsecmd(0)) != NEOF) {
1569338c85cSJilles Tjoelker 		if (n != NULL && !nflag) {
157960da934SJilles Tjoelker 			if (flags_exit && preadateof())
158960da934SJilles Tjoelker 				evaltree(n, flags | EV_EXIT);
159960da934SJilles Tjoelker 			else
160cb806389SStefan Farfeleder 				evaltree(n, flags);
161b84d7af7SJilles Tjoelker 			any = 1;
16275e17168SJilles Tjoelker 			if (evalskip)
16375e17168SJilles Tjoelker 				break;
164960da934SJilles Tjoelker 		}
1654b88c807SRodney W. Grimes 		popstackmark(&smark);
166e9e92235SJilles Tjoelker 		setstackmark(&smark);
1674b88c807SRodney W. Grimes 	}
1684b88c807SRodney W. Grimes 	popfile();
1694b88c807SRodney W. Grimes 	popstackmark(&smark);
170b84d7af7SJilles Tjoelker 	if (!any)
171b84d7af7SJilles Tjoelker 		exitstatus = 0;
172960da934SJilles Tjoelker 	if (flags_exit)
17345496405SJilles Tjoelker 		exraise(EXEXIT);
1744b88c807SRodney W. Grimes }
1754b88c807SRodney W. Grimes 
1764b88c807SRodney W. Grimes 
1774b88c807SRodney W. Grimes /*
1784b88c807SRodney W. Grimes  * Evaluate a parse tree.  The value is left in the global variable
1794b88c807SRodney W. Grimes  * exitstatus.
1804b88c807SRodney W. Grimes  */
1814b88c807SRodney W. Grimes 
1824b88c807SRodney W. Grimes void
evaltree(union node * n,int flags)1835134c3f7SWarner Losh evaltree(union node *n, int flags)
1844b88c807SRodney W. Grimes {
185457c463dSStefan Farfeleder 	int do_etest;
186dca867f1SJilles Tjoelker 	union node *next;
18784edde8bSJilles Tjoelker 	struct stackmark smark;
188457c463dSStefan Farfeleder 
18984edde8bSJilles Tjoelker 	setstackmark(&smark);
190457c463dSStefan Farfeleder 	do_etest = 0;
1914b88c807SRodney W. Grimes 	if (n == NULL) {
1924b88c807SRodney W. Grimes 		TRACE(("evaltree(NULL) called\n"));
1934b88c807SRodney W. Grimes 		exitstatus = 0;
1944b88c807SRodney W. Grimes 		goto out;
1954b88c807SRodney W. Grimes 	}
196dca867f1SJilles Tjoelker 	do {
197dca867f1SJilles Tjoelker 		next = NULL;
198aa9caaf6SPeter Wemm #ifndef NO_HISTORY
1994b88c807SRodney W. Grimes 		displayhist = 1;	/* show history substitutions done with fc */
200aa9caaf6SPeter Wemm #endif
2019957cb23SStefan Farfeleder 		TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
2024b88c807SRodney W. Grimes 		switch (n->type) {
2034b88c807SRodney W. Grimes 		case NSEMI:
2042340828bSStefan Farfeleder 			evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
2054b88c807SRodney W. Grimes 			if (evalskip)
2064b88c807SRodney W. Grimes 				goto out;
207dca867f1SJilles Tjoelker 			next = n->nbinary.ch2;
2084b88c807SRodney W. Grimes 			break;
2094b88c807SRodney W. Grimes 		case NAND:
2104b88c807SRodney W. Grimes 			evaltree(n->nbinary.ch1, EV_TESTED);
211d1e99272SSteve Price 			if (evalskip || exitstatus != 0) {
2124b88c807SRodney W. Grimes 				goto out;
213d1e99272SSteve Price 			}
214dca867f1SJilles Tjoelker 			next = n->nbinary.ch2;
2154b88c807SRodney W. Grimes 			break;
2164b88c807SRodney W. Grimes 		case NOR:
2174b88c807SRodney W. Grimes 			evaltree(n->nbinary.ch1, EV_TESTED);
2184b88c807SRodney W. Grimes 			if (evalskip || exitstatus == 0)
2194b88c807SRodney W. Grimes 				goto out;
220dca867f1SJilles Tjoelker 			next = n->nbinary.ch2;
2214b88c807SRodney W. Grimes 			break;
2224b88c807SRodney W. Grimes 		case NREDIR:
223c3bb8589SJilles Tjoelker 			evalredir(n, flags);
2244b88c807SRodney W. Grimes 			break;
2254b88c807SRodney W. Grimes 		case NSUBSHELL:
2264b88c807SRodney W. Grimes 			evalsubshell(n, flags);
227457c463dSStefan Farfeleder 			do_etest = !(flags & EV_TESTED);
2284b88c807SRodney W. Grimes 			break;
2294b88c807SRodney W. Grimes 		case NBACKGND:
2304b88c807SRodney W. Grimes 			evalsubshell(n, flags);
2314b88c807SRodney W. Grimes 			break;
2324b88c807SRodney W. Grimes 		case NIF: {
2334b88c807SRodney W. Grimes 			evaltree(n->nif.test, EV_TESTED);
2344b88c807SRodney W. Grimes 			if (evalskip)
2354b88c807SRodney W. Grimes 				goto out;
236ab0a2172SSteve Price 			if (exitstatus == 0)
237dca867f1SJilles Tjoelker 				next = n->nif.ifpart;
238aa9caaf6SPeter Wemm 			else if (n->nif.elsepart)
239dca867f1SJilles Tjoelker 				next = n->nif.elsepart;
240ab0a2172SSteve Price 			else
241ab0a2172SSteve Price 				exitstatus = 0;
2424b88c807SRodney W. Grimes 			break;
2434b88c807SRodney W. Grimes 		}
2444b88c807SRodney W. Grimes 		case NWHILE:
2454b88c807SRodney W. Grimes 		case NUNTIL:
2464ee9cb0eSStefan Farfeleder 			evalloop(n, flags & ~EV_EXIT);
2474b88c807SRodney W. Grimes 			break;
2484b88c807SRodney W. Grimes 		case NFOR:
2494ee9cb0eSStefan Farfeleder 			evalfor(n, flags & ~EV_EXIT);
2504b88c807SRodney W. Grimes 			break;
2514b88c807SRodney W. Grimes 		case NCASE:
25293fcb251SJilles Tjoelker 			next = evalcase(n);
25393fcb251SJilles Tjoelker 			break;
25493fcb251SJilles Tjoelker 		case NCLIST:
25593fcb251SJilles Tjoelker 			next = n->nclist.body;
25693fcb251SJilles Tjoelker 			break;
25793fcb251SJilles Tjoelker 		case NCLISTFALLTHRU:
25893fcb251SJilles Tjoelker 			if (n->nclist.body) {
25993fcb251SJilles Tjoelker 				evaltree(n->nclist.body, flags & ~EV_EXIT);
26093fcb251SJilles Tjoelker 				if (evalskip)
26193fcb251SJilles Tjoelker 					goto out;
26293fcb251SJilles Tjoelker 			}
26393fcb251SJilles Tjoelker 			next = n->nclist.next;
2644b88c807SRodney W. Grimes 			break;
2654b88c807SRodney W. Grimes 		case NDEFUN:
2664b88c807SRodney W. Grimes 			defun(n->narg.text, n->narg.next);
2674b88c807SRodney W. Grimes 			exitstatus = 0;
2684b88c807SRodney W. Grimes 			break;
2694b88c807SRodney W. Grimes 		case NNOT:
2704b88c807SRodney W. Grimes 			evaltree(n->nnot.com, EV_TESTED);
2716e0f89a4SJilles Tjoelker 			if (evalskip)
2726e0f89a4SJilles Tjoelker 				goto out;
2734b88c807SRodney W. Grimes 			exitstatus = !exitstatus;
2744b88c807SRodney W. Grimes 			break;
2754b88c807SRodney W. Grimes 
2764b88c807SRodney W. Grimes 		case NPIPE:
2774b88c807SRodney W. Grimes 			evalpipe(n);
278457c463dSStefan Farfeleder 			do_etest = !(flags & EV_TESTED);
2794b88c807SRodney W. Grimes 			break;
2804b88c807SRodney W. Grimes 		case NCMD:
2814b88c807SRodney W. Grimes 			evalcommand(n, flags, (struct backcmd *)NULL);
282457c463dSStefan Farfeleder 			do_etest = !(flags & EV_TESTED);
2834b88c807SRodney W. Grimes 			break;
2844b88c807SRodney W. Grimes 		default:
2854b88c807SRodney W. Grimes 			out1fmt("Node type = %d\n", n->type);
2864b88c807SRodney W. Grimes 			flushout(&output);
2874b88c807SRodney W. Grimes 			break;
2884b88c807SRodney W. Grimes 		}
289dca867f1SJilles Tjoelker 		n = next;
29084edde8bSJilles Tjoelker 		popstackmark(&smark);
291e9e92235SJilles Tjoelker 		setstackmark(&smark);
292dca867f1SJilles Tjoelker 	} while (n != NULL);
2934b88c807SRodney W. Grimes out:
29484edde8bSJilles Tjoelker 	popstackmark(&smark);
29525e0f0f5SJilles Tjoelker 	if (pendingsig)
2964b88c807SRodney W. Grimes 		dotrap();
29745496405SJilles Tjoelker 	if (eflag && exitstatus != 0 && do_etest)
2984b88c807SRodney W. Grimes 		exitshell(exitstatus);
29945496405SJilles Tjoelker 	if (flags & EV_EXIT)
30045496405SJilles Tjoelker 		exraise(EXEXIT);
3014b88c807SRodney W. Grimes }
3024b88c807SRodney W. Grimes 
3034b88c807SRodney W. Grimes 
30488328642SDavid E. O'Brien static void
evalloop(union node * n,int flags)3054ee9cb0eSStefan Farfeleder evalloop(union node *n, int flags)
3064b88c807SRodney W. Grimes {
3074b88c807SRodney W. Grimes 	int status;
3084b88c807SRodney W. Grimes 
3094b88c807SRodney W. Grimes 	loopnest++;
3104b88c807SRodney W. Grimes 	status = 0;
3114b88c807SRodney W. Grimes 	for (;;) {
31233c5acf0SJilles Tjoelker 		if (!evalskip)
3134b88c807SRodney W. Grimes 			evaltree(n->nbinary.ch1, EV_TESTED);
3144b88c807SRodney W. Grimes 		if (evalskip) {
31533c5acf0SJilles Tjoelker 			if (evalskip == SKIPCONT && --skipcount <= 0) {
3164b88c807SRodney W. Grimes 				evalskip = 0;
3174b88c807SRodney W. Grimes 				continue;
3184b88c807SRodney W. Grimes 			}
3194b88c807SRodney W. Grimes 			if (evalskip == SKIPBREAK && --skipcount <= 0)
3204b88c807SRodney W. Grimes 				evalskip = 0;
3212935c4ccSJilles Tjoelker 			if (evalskip == SKIPRETURN)
3228f2dc7deSJilles Tjoelker 				status = exitstatus;
3234b88c807SRodney W. Grimes 			break;
3244b88c807SRodney W. Grimes 		}
3254b88c807SRodney W. Grimes 		if (n->type == NWHILE) {
3264b88c807SRodney W. Grimes 			if (exitstatus != 0)
3274b88c807SRodney W. Grimes 				break;
3284b88c807SRodney W. Grimes 		} else {
3294b88c807SRodney W. Grimes 			if (exitstatus == 0)
3304b88c807SRodney W. Grimes 				break;
3314b88c807SRodney W. Grimes 		}
3324ee9cb0eSStefan Farfeleder 		evaltree(n->nbinary.ch2, flags);
3334b88c807SRodney W. Grimes 		status = exitstatus;
3344b88c807SRodney W. Grimes 	}
3354b88c807SRodney W. Grimes 	loopnest--;
3364b88c807SRodney W. Grimes 	exitstatus = status;
3374b88c807SRodney W. Grimes }
3384b88c807SRodney W. Grimes 
3394b88c807SRodney W. Grimes 
3404b88c807SRodney W. Grimes 
34188328642SDavid E. O'Brien static void
evalfor(union node * n,int flags)3424ee9cb0eSStefan Farfeleder evalfor(union node *n, int flags)
3434b88c807SRodney W. Grimes {
3444b88c807SRodney W. Grimes 	struct arglist arglist;
3454b88c807SRodney W. Grimes 	union node *argp;
3468ef0ae8aSJilles Tjoelker 	int i;
3476eff4a75SJilles Tjoelker 	int status;
3484b88c807SRodney W. Grimes 
3498ef0ae8aSJilles Tjoelker 	emptyarglist(&arglist);
3504b88c807SRodney W. Grimes 	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
351aa9caaf6SPeter Wemm 		oexitstatus = exitstatus;
3524b88c807SRodney W. Grimes 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3534b88c807SRodney W. Grimes 	}
3544b88c807SRodney W. Grimes 
3554b88c807SRodney W. Grimes 	loopnest++;
3566eff4a75SJilles Tjoelker 	status = 0;
3578ef0ae8aSJilles Tjoelker 	for (i = 0; i < arglist.count; i++) {
3588ef0ae8aSJilles Tjoelker 		setvar(n->nfor.var, arglist.args[i], 0);
3594ee9cb0eSStefan Farfeleder 		evaltree(n->nfor.body, flags);
3606eff4a75SJilles Tjoelker 		status = exitstatus;
3614b88c807SRodney W. Grimes 		if (evalskip) {
3624b88c807SRodney W. Grimes 			if (evalskip == SKIPCONT && --skipcount <= 0) {
3634b88c807SRodney W. Grimes 				evalskip = 0;
3644b88c807SRodney W. Grimes 				continue;
3654b88c807SRodney W. Grimes 			}
3664b88c807SRodney W. Grimes 			if (evalskip == SKIPBREAK && --skipcount <= 0)
3674b88c807SRodney W. Grimes 				evalskip = 0;
3684b88c807SRodney W. Grimes 			break;
3694b88c807SRodney W. Grimes 		}
3704b88c807SRodney W. Grimes 	}
3714b88c807SRodney W. Grimes 	loopnest--;
3726eff4a75SJilles Tjoelker 	exitstatus = status;
3734b88c807SRodney W. Grimes }
3744b88c807SRodney W. Grimes 
3754b88c807SRodney W. Grimes 
37693fcb251SJilles Tjoelker /*
37793fcb251SJilles Tjoelker  * Evaluate a case statement, returning the selected tree.
37893fcb251SJilles Tjoelker  *
37993fcb251SJilles Tjoelker  * The exit status needs care to get right.
38093fcb251SJilles Tjoelker  */
3814b88c807SRodney W. Grimes 
382a157dc4dSJilles Tjoelker static union node *
evalcase(union node * n)38393fcb251SJilles Tjoelker evalcase(union node *n)
3844b88c807SRodney W. Grimes {
3854b88c807SRodney W. Grimes 	union node *cp;
3864b88c807SRodney W. Grimes 	union node *patp;
3874b88c807SRodney W. Grimes 	struct arglist arglist;
3884b88c807SRodney W. Grimes 
3898ef0ae8aSJilles Tjoelker 	emptyarglist(&arglist);
390aa9caaf6SPeter Wemm 	oexitstatus = exitstatus;
3914b88c807SRodney W. Grimes 	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
392a157dc4dSJilles Tjoelker 	for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
3934b88c807SRodney W. Grimes 		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
3948ef0ae8aSJilles Tjoelker 			if (casematch(patp, arglist.args[0])) {
395c9afaa63SJilles Tjoelker 				while (cp->nclist.next &&
39693fcb251SJilles Tjoelker 				    cp->type == NCLISTFALLTHRU &&
39793fcb251SJilles Tjoelker 				    cp->nclist.body == NULL)
398c9afaa63SJilles Tjoelker 					cp = cp->nclist.next;
39993fcb251SJilles Tjoelker 				if (cp->nclist.next &&
40093fcb251SJilles Tjoelker 				    cp->type == NCLISTFALLTHRU)
40193fcb251SJilles Tjoelker 					return (cp);
40292371efcSJilles Tjoelker 				if (cp->nclist.body == NULL)
40392371efcSJilles Tjoelker 					exitstatus = 0;
404a157dc4dSJilles Tjoelker 				return (cp->nclist.body);
4054b88c807SRodney W. Grimes 			}
4064b88c807SRodney W. Grimes 		}
4074b88c807SRodney W. Grimes 	}
40892371efcSJilles Tjoelker 	exitstatus = 0;
409a157dc4dSJilles Tjoelker 	return (NULL);
4104b88c807SRodney W. Grimes }
4114b88c807SRodney W. Grimes 
4124b88c807SRodney W. Grimes 
4134b88c807SRodney W. Grimes 
4144b88c807SRodney W. Grimes /*
4154b88c807SRodney W. Grimes  * Kick off a subshell to evaluate a tree.
4164b88c807SRodney W. Grimes  */
4174b88c807SRodney W. Grimes 
41888328642SDavid E. O'Brien static void
evalsubshell(union node * n,int flags)4195134c3f7SWarner Losh evalsubshell(union node *n, int flags)
4204b88c807SRodney W. Grimes {
4214b88c807SRodney W. Grimes 	struct job *jp;
4224b88c807SRodney W. Grimes 	int backgnd = (n->type == NBACKGND);
4234b88c807SRodney W. Grimes 
424b3f892d9SJilles Tjoelker 	oexitstatus = exitstatus;
4254b88c807SRodney W. Grimes 	expredir(n->nredir.redirect);
4264f6e4215SJilles Tjoelker 	if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
4274f6e4215SJilles Tjoelker 			forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
4284b88c807SRodney W. Grimes 		if (backgnd)
4294b88c807SRodney W. Grimes 			flags &=~ EV_TESTED;
4304b88c807SRodney W. Grimes 		redirect(n->nredir.redirect, 0);
4314b88c807SRodney W. Grimes 		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
432384aedabSJilles Tjoelker 	} else if (! backgnd) {
4334b88c807SRodney W. Grimes 		INTOFF;
43457b2932aSMartin Cracauer 		exitstatus = waitforjob(jp, (int *)NULL);
4354b88c807SRodney W. Grimes 		INTON;
43603b3a844SJilles Tjoelker 	} else
43703b3a844SJilles Tjoelker 		exitstatus = 0;
4384b88c807SRodney W. Grimes }
4394b88c807SRodney W. Grimes 
4404b88c807SRodney W. Grimes 
441c3bb8589SJilles Tjoelker /*
442c3bb8589SJilles Tjoelker  * Evaluate a redirected compound command.
443c3bb8589SJilles Tjoelker  */
444c3bb8589SJilles Tjoelker 
44588328642SDavid E. O'Brien static void
evalredir(union node * n,int flags)446c3bb8589SJilles Tjoelker evalredir(union node *n, int flags)
447c3bb8589SJilles Tjoelker {
448c3bb8589SJilles Tjoelker 	struct jmploc jmploc;
449c3bb8589SJilles Tjoelker 	struct jmploc *savehandler;
450c3bb8589SJilles Tjoelker 	volatile int in_redirect = 1;
451c3bb8589SJilles Tjoelker 
452b3f892d9SJilles Tjoelker 	oexitstatus = exitstatus;
453c3bb8589SJilles Tjoelker 	expredir(n->nredir.redirect);
454c3bb8589SJilles Tjoelker 	savehandler = handler;
455c3bb8589SJilles Tjoelker 	if (setjmp(jmploc.loc)) {
456c3bb8589SJilles Tjoelker 		int e;
457c3bb8589SJilles Tjoelker 
458c3bb8589SJilles Tjoelker 		handler = savehandler;
459c3bb8589SJilles Tjoelker 		e = exception;
460c3bb8589SJilles Tjoelker 		popredir();
461bb324af6SJilles Tjoelker 		if (e == EXERROR && in_redirect) {
462eab49982SJilles Tjoelker 			FORCEINTON;
463c3bb8589SJilles Tjoelker 			return;
464c3bb8589SJilles Tjoelker 		}
465c3bb8589SJilles Tjoelker 		longjmp(handler->loc, 1);
466c3bb8589SJilles Tjoelker 	} else {
467c3bb8589SJilles Tjoelker 		INTOFF;
468c3bb8589SJilles Tjoelker 		handler = &jmploc;
469c3bb8589SJilles Tjoelker 		redirect(n->nredir.redirect, REDIR_PUSH);
470c3bb8589SJilles Tjoelker 		in_redirect = 0;
471c3bb8589SJilles Tjoelker 		INTON;
472c3bb8589SJilles Tjoelker 		evaltree(n->nredir.n, flags);
473c3bb8589SJilles Tjoelker 	}
474c3bb8589SJilles Tjoelker 	INTOFF;
475c3bb8589SJilles Tjoelker 	handler = savehandler;
476c3bb8589SJilles Tjoelker 	popredir();
477c3bb8589SJilles Tjoelker 	INTON;
478c3bb8589SJilles Tjoelker }
479c3bb8589SJilles Tjoelker 
4804b88c807SRodney W. Grimes 
4814dc6bdd3SJilles Tjoelker static void
exphere(union node * redir,struct arglist * fn)4824dc6bdd3SJilles Tjoelker exphere(union node *redir, struct arglist *fn)
4834dc6bdd3SJilles Tjoelker {
4844dc6bdd3SJilles Tjoelker 	struct jmploc jmploc;
4854dc6bdd3SJilles Tjoelker 	struct jmploc *savehandler;
4864dc6bdd3SJilles Tjoelker 	struct localvar *savelocalvars;
4874dc6bdd3SJilles Tjoelker 	int need_longjmp = 0;
488adba77a6SJilles Tjoelker 	unsigned char saveoptreset;
4894dc6bdd3SJilles Tjoelker 
490781bfb5aSJilles Tjoelker 	redir->nhere.expdoc = "";
4914dc6bdd3SJilles Tjoelker 	savelocalvars = localvars;
4924dc6bdd3SJilles Tjoelker 	localvars = NULL;
493adba77a6SJilles Tjoelker 	saveoptreset = shellparam.reset;
4944dc6bdd3SJilles Tjoelker 	forcelocal++;
4954dc6bdd3SJilles Tjoelker 	savehandler = handler;
4964dc6bdd3SJilles Tjoelker 	if (setjmp(jmploc.loc))
497bb324af6SJilles Tjoelker 		need_longjmp = exception != EXERROR;
4984dc6bdd3SJilles Tjoelker 	else {
4994dc6bdd3SJilles Tjoelker 		handler = &jmploc;
5004dc6bdd3SJilles Tjoelker 		expandarg(redir->nhere.doc, fn, 0);
5018ef0ae8aSJilles Tjoelker 		redir->nhere.expdoc = fn->args[0];
5024dc6bdd3SJilles Tjoelker 		INTOFF;
5034dc6bdd3SJilles Tjoelker 	}
5044dc6bdd3SJilles Tjoelker 	handler = savehandler;
5054dc6bdd3SJilles Tjoelker 	forcelocal--;
5064dc6bdd3SJilles Tjoelker 	poplocalvars();
5074dc6bdd3SJilles Tjoelker 	localvars = savelocalvars;
508adba77a6SJilles Tjoelker 	shellparam.reset = saveoptreset;
5094dc6bdd3SJilles Tjoelker 	if (need_longjmp)
5104dc6bdd3SJilles Tjoelker 		longjmp(handler->loc, 1);
5114dc6bdd3SJilles Tjoelker 	INTON;
5124dc6bdd3SJilles Tjoelker }
5134dc6bdd3SJilles Tjoelker 
5144dc6bdd3SJilles Tjoelker 
5154b88c807SRodney W. Grimes /*
5164b88c807SRodney W. Grimes  * Compute the names of the files in a redirection list.
5174b88c807SRodney W. Grimes  */
5184b88c807SRodney W. Grimes 
51988328642SDavid E. O'Brien static void
expredir(union node * n)5205134c3f7SWarner Losh expredir(union node *n)
5214b88c807SRodney W. Grimes {
522afb033d5SSteve Price 	union node *redir;
5234b88c807SRodney W. Grimes 
5244b88c807SRodney W. Grimes 	for (redir = n ; redir ; redir = redir->nfile.next) {
5254b88c807SRodney W. Grimes 		struct arglist fn;
5268ef0ae8aSJilles Tjoelker 		emptyarglist(&fn);
527aa9caaf6SPeter Wemm 		switch (redir->type) {
528aa9caaf6SPeter Wemm 		case NFROM:
529aa9caaf6SPeter Wemm 		case NTO:
5304682f420SBrian Somers 		case NFROMTO:
531aa9caaf6SPeter Wemm 		case NAPPEND:
5321a958c66STim J. Robbins 		case NCLOBBER:
533f649ab8bSJilles Tjoelker 			expandarg(redir->nfile.fname, &fn, EXP_TILDE);
5348ef0ae8aSJilles Tjoelker 			redir->nfile.expfname = fn.args[0];
535aa9caaf6SPeter Wemm 			break;
536aa9caaf6SPeter Wemm 		case NFROMFD:
537aa9caaf6SPeter Wemm 		case NTOFD:
538aa9caaf6SPeter Wemm 			if (redir->ndup.vname) {
539f649ab8bSJilles Tjoelker 				expandarg(redir->ndup.vname, &fn, EXP_TILDE);
5408ef0ae8aSJilles Tjoelker 				fixredir(redir, fn.args[0], 1);
541aa9caaf6SPeter Wemm 			}
542aa9caaf6SPeter Wemm 			break;
5434dc6bdd3SJilles Tjoelker 		case NXHERE:
5444dc6bdd3SJilles Tjoelker 			exphere(redir, &fn);
5454dc6bdd3SJilles Tjoelker 			break;
5464b88c807SRodney W. Grimes 		}
5474b88c807SRodney W. Grimes 	}
5484b88c807SRodney W. Grimes }
5494b88c807SRodney W. Grimes 
5504b88c807SRodney W. Grimes 
5514b88c807SRodney W. Grimes 
5524b88c807SRodney W. Grimes /*
5534b88c807SRodney W. Grimes  * Evaluate a pipeline.  All the processes in the pipeline are children
5544b88c807SRodney W. Grimes  * of the process creating the pipeline.  (This differs from some versions
5554b88c807SRodney W. Grimes  * of the shell, which make the last process in a pipeline the parent
5564b88c807SRodney W. Grimes  * of all the rest.)
5574b88c807SRodney W. Grimes  */
5584b88c807SRodney W. Grimes 
55988328642SDavid E. O'Brien static void
evalpipe(union node * n)5605134c3f7SWarner Losh evalpipe(union node *n)
5614b88c807SRodney W. Grimes {
5624b88c807SRodney W. Grimes 	struct job *jp;
5634b88c807SRodney W. Grimes 	struct nodelist *lp;
5644b88c807SRodney W. Grimes 	int pipelen;
5654b88c807SRodney W. Grimes 	int prevfd;
5664b88c807SRodney W. Grimes 	int pip[2];
5674b88c807SRodney W. Grimes 
5689957cb23SStefan Farfeleder 	TRACE(("evalpipe(%p) called\n", (void *)n));
5694b88c807SRodney W. Grimes 	pipelen = 0;
5704b88c807SRodney W. Grimes 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
5714b88c807SRodney W. Grimes 		pipelen++;
5724b88c807SRodney W. Grimes 	INTOFF;
5734b88c807SRodney W. Grimes 	jp = makejob(n, pipelen);
5744b88c807SRodney W. Grimes 	prevfd = -1;
5754b88c807SRodney W. Grimes 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
5764b88c807SRodney W. Grimes 		prehash(lp->n);
5774b88c807SRodney W. Grimes 		pip[1] = -1;
5784b88c807SRodney W. Grimes 		if (lp->next) {
5794b88c807SRodney W. Grimes 			if (pipe(pip) < 0) {
58042580a3eSJilles Tjoelker 				if (prevfd >= 0)
5814b88c807SRodney W. Grimes 					close(prevfd);
5826c48b6cfSMartin Cracauer 				error("Pipe call failed: %s", strerror(errno));
5834b88c807SRodney W. Grimes 			}
5844b88c807SRodney W. Grimes 		}
5854b88c807SRodney W. Grimes 		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
5864b88c807SRodney W. Grimes 			INTON;
5874b88c807SRodney W. Grimes 			if (prevfd > 0) {
5887e1c7266SDag-Erling Smørgrav 				dup2(prevfd, 0);
5894b88c807SRodney W. Grimes 				close(prevfd);
5904b88c807SRodney W. Grimes 			}
5914b88c807SRodney W. Grimes 			if (pip[1] >= 0) {
5926d9efc24SMartin Cracauer 				if (!(prevfd >= 0 && pip[0] == 0))
5934b88c807SRodney W. Grimes 					close(pip[0]);
5944b88c807SRodney W. Grimes 				if (pip[1] != 1) {
5957e1c7266SDag-Erling Smørgrav 					dup2(pip[1], 1);
5964b88c807SRodney W. Grimes 					close(pip[1]);
5974b88c807SRodney W. Grimes 				}
5984b88c807SRodney W. Grimes 			}
5994b88c807SRodney W. Grimes 			evaltree(lp->n, EV_EXIT);
6004b88c807SRodney W. Grimes 		}
6014b88c807SRodney W. Grimes 		if (prevfd >= 0)
6024b88c807SRodney W. Grimes 			close(prevfd);
6034b88c807SRodney W. Grimes 		prevfd = pip[0];
604e64a11e9SJilles Tjoelker 		if (pip[1] != -1)
6054b88c807SRodney W. Grimes 			close(pip[1]);
6064b88c807SRodney W. Grimes 	}
6074b88c807SRodney W. Grimes 	INTON;
6084b88c807SRodney W. Grimes 	if (n->npipe.backgnd == 0) {
6094b88c807SRodney W. Grimes 		INTOFF;
61057b2932aSMartin Cracauer 		exitstatus = waitforjob(jp, (int *)NULL);
6114b88c807SRodney W. Grimes 		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
6124b88c807SRodney W. Grimes 		INTON;
61303b3a844SJilles Tjoelker 	} else
61403b3a844SJilles Tjoelker 		exitstatus = 0;
6154b88c807SRodney W. Grimes }
6164b88c807SRodney W. Grimes 
6174b88c807SRodney W. Grimes 
6184b88c807SRodney W. Grimes 
619acd7984fSJilles Tjoelker static int
is_valid_fast_cmdsubst(union node * n)620acd7984fSJilles Tjoelker is_valid_fast_cmdsubst(union node *n)
621acd7984fSJilles Tjoelker {
622acd7984fSJilles Tjoelker 
623c543e1aeSJilles Tjoelker 	return (n->type == NCMD);
624acd7984fSJilles Tjoelker }
625acd7984fSJilles Tjoelker 
6264b88c807SRodney W. Grimes /*
6274b88c807SRodney W. Grimes  * Execute a command inside back quotes.  If it's a builtin command, we
6284b88c807SRodney W. Grimes  * want to save its output in a block obtained from malloc.  Otherwise
6294b88c807SRodney W. Grimes  * we fork off a subprocess and get the output of the command via a pipe.
6304b88c807SRodney W. Grimes  * Should be called with interrupts off.
6314b88c807SRodney W. Grimes  */
6324b88c807SRodney W. Grimes 
6334b88c807SRodney W. Grimes void
evalbackcmd(union node * n,struct backcmd * result)6345134c3f7SWarner Losh evalbackcmd(union node *n, struct backcmd *result)
6354b88c807SRodney W. Grimes {
6364b88c807SRodney W. Grimes 	int pip[2];
6374b88c807SRodney W. Grimes 	struct job *jp;
63884edde8bSJilles Tjoelker 	struct stackmark smark;
63945b71cd1SJilles Tjoelker 	struct jmploc jmploc;
64045b71cd1SJilles Tjoelker 	struct jmploc *savehandler;
641c543e1aeSJilles Tjoelker 	struct localvar *savelocalvars;
642adba77a6SJilles Tjoelker 	unsigned char saveoptreset;
6434b88c807SRodney W. Grimes 
6444b88c807SRodney W. Grimes 	result->fd = -1;
6454b88c807SRodney W. Grimes 	result->buf = NULL;
6464b88c807SRodney W. Grimes 	result->nleft = 0;
6474b88c807SRodney W. Grimes 	result->jp = NULL;
648aa9caaf6SPeter Wemm 	if (n == NULL) {
6494b88c807SRodney W. Grimes 		exitstatus = 0;
65033c5acf0SJilles Tjoelker 		return;
651aa9caaf6SPeter Wemm 	}
65233c5acf0SJilles Tjoelker 	setstackmark(&smark);
653aa9caaf6SPeter Wemm 	exitstatus = oexitstatus;
654925420d0SJilles Tjoelker 	if (is_valid_fast_cmdsubst(n)) {
655c543e1aeSJilles Tjoelker 		savelocalvars = localvars;
656c543e1aeSJilles Tjoelker 		localvars = NULL;
657adba77a6SJilles Tjoelker 		saveoptreset = shellparam.reset;
658c543e1aeSJilles Tjoelker 		forcelocal++;
65945b71cd1SJilles Tjoelker 		savehandler = handler;
66045b71cd1SJilles Tjoelker 		if (setjmp(jmploc.loc)) {
661b5532964SJilles Tjoelker 			if (exception == EXERROR)
662b5532964SJilles Tjoelker 				/* nothing */;
66345b71cd1SJilles Tjoelker 			else if (exception != 0) {
66445b71cd1SJilles Tjoelker 				handler = savehandler;
665c543e1aeSJilles Tjoelker 				forcelocal--;
666c543e1aeSJilles Tjoelker 				poplocalvars();
667c543e1aeSJilles Tjoelker 				localvars = savelocalvars;
668adba77a6SJilles Tjoelker 				shellparam.reset = saveoptreset;
66945b71cd1SJilles Tjoelker 				longjmp(handler->loc, 1);
67045b71cd1SJilles Tjoelker 			}
67145b71cd1SJilles Tjoelker 		} else {
67245b71cd1SJilles Tjoelker 			handler = &jmploc;
6734b88c807SRodney W. Grimes 			evalcommand(n, EV_BACKCMD, result);
67445b71cd1SJilles Tjoelker 		}
67545b71cd1SJilles Tjoelker 		handler = savehandler;
676c543e1aeSJilles Tjoelker 		forcelocal--;
677c543e1aeSJilles Tjoelker 		poplocalvars();
678c543e1aeSJilles Tjoelker 		localvars = savelocalvars;
679adba77a6SJilles Tjoelker 		shellparam.reset = saveoptreset;
6804b88c807SRodney W. Grimes 	} else {
6814b88c807SRodney W. Grimes 		if (pipe(pip) < 0)
6826c48b6cfSMartin Cracauer 			error("Pipe call failed: %s", strerror(errno));
6834b88c807SRodney W. Grimes 		jp = makejob(n, 1);
6844b88c807SRodney W. Grimes 		if (forkshell(jp, n, FORK_NOJOB) == 0) {
6854b88c807SRodney W. Grimes 			FORCEINTON;
6864b88c807SRodney W. Grimes 			close(pip[0]);
6874b88c807SRodney W. Grimes 			if (pip[1] != 1) {
6887e1c7266SDag-Erling Smørgrav 				dup2(pip[1], 1);
6894b88c807SRodney W. Grimes 				close(pip[1]);
6904b88c807SRodney W. Grimes 			}
6914b88c807SRodney W. Grimes 			evaltree(n, EV_EXIT);
6924b88c807SRodney W. Grimes 		}
6934b88c807SRodney W. Grimes 		close(pip[1]);
6944b88c807SRodney W. Grimes 		result->fd = pip[0];
6954b88c807SRodney W. Grimes 		result->jp = jp;
6964b88c807SRodney W. Grimes 	}
6974b88c807SRodney W. Grimes 	popstackmark(&smark);
6986da31df8STim J. Robbins 	TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
6994b88c807SRodney W. Grimes 		result->fd, result->buf, result->nleft, result->jp));
7004b88c807SRodney W. Grimes }
7014b88c807SRodney W. Grimes 
70284fbdd8cSJilles Tjoelker static int
mustexpandto(const char * argtext,const char * mask)70384fbdd8cSJilles Tjoelker mustexpandto(const char *argtext, const char *mask)
70484fbdd8cSJilles Tjoelker {
70584fbdd8cSJilles Tjoelker 	for (;;) {
70684fbdd8cSJilles Tjoelker 		if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
70784fbdd8cSJilles Tjoelker 			argtext++;
70884fbdd8cSJilles Tjoelker 			continue;
70984fbdd8cSJilles Tjoelker 		}
71084fbdd8cSJilles Tjoelker 		if (*argtext == CTLESC)
71184fbdd8cSJilles Tjoelker 			argtext++;
71284fbdd8cSJilles Tjoelker 		else if (BASESYNTAX[(int)*argtext] == CCTL)
71384fbdd8cSJilles Tjoelker 			return (0);
71484fbdd8cSJilles Tjoelker 		if (*argtext != *mask)
71584fbdd8cSJilles Tjoelker 			return (0);
71684fbdd8cSJilles Tjoelker 		if (*argtext == '\0')
71784fbdd8cSJilles Tjoelker 			return (1);
71884fbdd8cSJilles Tjoelker 		argtext++;
71984fbdd8cSJilles Tjoelker 		mask++;
72084fbdd8cSJilles Tjoelker 	}
72184fbdd8cSJilles Tjoelker }
72284fbdd8cSJilles Tjoelker 
72384fbdd8cSJilles Tjoelker static int
isdeclarationcmd(struct narg * arg)72484fbdd8cSJilles Tjoelker isdeclarationcmd(struct narg *arg)
72584fbdd8cSJilles Tjoelker {
72684fbdd8cSJilles Tjoelker 	int have_command = 0;
72784fbdd8cSJilles Tjoelker 
72884fbdd8cSJilles Tjoelker 	if (arg == NULL)
72984fbdd8cSJilles Tjoelker 		return (0);
73084fbdd8cSJilles Tjoelker 	while (mustexpandto(arg->text, "command")) {
73184fbdd8cSJilles Tjoelker 		have_command = 1;
73284fbdd8cSJilles Tjoelker 		arg = &arg->next->narg;
73384fbdd8cSJilles Tjoelker 		if (arg == NULL)
73484fbdd8cSJilles Tjoelker 			return (0);
73584fbdd8cSJilles Tjoelker 		/*
73684fbdd8cSJilles Tjoelker 		 * To also allow "command -p" and "command --" as part of
73784fbdd8cSJilles Tjoelker 		 * a declaration command, add code here.
73884fbdd8cSJilles Tjoelker 		 * We do not do this, as ksh does not do it either and it
73984fbdd8cSJilles Tjoelker 		 * is not required by POSIX.
74084fbdd8cSJilles Tjoelker 		 */
74184fbdd8cSJilles Tjoelker 	}
74284fbdd8cSJilles Tjoelker 	return (mustexpandto(arg->text, "export") ||
74384fbdd8cSJilles Tjoelker 	    mustexpandto(arg->text, "readonly") ||
74484fbdd8cSJilles Tjoelker 	    (mustexpandto(arg->text, "local") &&
74584fbdd8cSJilles Tjoelker 		(have_command || !isfunc("local"))));
74684fbdd8cSJilles Tjoelker }
74784fbdd8cSJilles Tjoelker 
748d1670d42SJilles Tjoelker static void
xtracecommand(struct arglist * varlist,int argc,char ** argv)749046bfe52SJilles Tjoelker xtracecommand(struct arglist *varlist, int argc, char **argv)
750d1670d42SJilles Tjoelker {
751d1670d42SJilles Tjoelker 	char sep = 0;
7528ef0ae8aSJilles Tjoelker 	const char *text, *p, *ps4;
7538ef0ae8aSJilles Tjoelker 	int i;
754d1670d42SJilles Tjoelker 
755d1670d42SJilles Tjoelker 	ps4 = expandstr(ps4val());
756d1670d42SJilles Tjoelker 	out2str(ps4 != NULL ? ps4 : ps4val());
7578ef0ae8aSJilles Tjoelker 	for (i = 0; i < varlist->count; i++) {
7588ef0ae8aSJilles Tjoelker 		text = varlist->args[i];
759d1670d42SJilles Tjoelker 		if (sep != 0)
760d1670d42SJilles Tjoelker 			out2c(' ');
7618ef0ae8aSJilles Tjoelker 		p = strchr(text, '=');
762d1670d42SJilles Tjoelker 		if (p != NULL) {
763d1670d42SJilles Tjoelker 			p++;
7648ef0ae8aSJilles Tjoelker 			outbin(text, p - text, out2);
765d1670d42SJilles Tjoelker 			out2qstr(p);
766d1670d42SJilles Tjoelker 		} else
7678ef0ae8aSJilles Tjoelker 			out2qstr(text);
768d1670d42SJilles Tjoelker 		sep = ' ';
769d1670d42SJilles Tjoelker 	}
770046bfe52SJilles Tjoelker 	for (i = 0; i < argc; i++) {
771046bfe52SJilles Tjoelker 		text = argv[i];
772d1670d42SJilles Tjoelker 		if (sep != 0)
773d1670d42SJilles Tjoelker 			out2c(' ');
7748ef0ae8aSJilles Tjoelker 		out2qstr(text);
775d1670d42SJilles Tjoelker 		sep = ' ';
776d1670d42SJilles Tjoelker 	}
777d1670d42SJilles Tjoelker 	out2c('\n');
778d1670d42SJilles Tjoelker 	flushout(&errout);
779d1670d42SJilles Tjoelker }
780d1670d42SJilles Tjoelker 
78111535bdfSJilles Tjoelker /*
78211535bdfSJilles Tjoelker  * Check if a builtin can safely be executed in the same process,
78311535bdfSJilles Tjoelker  * even though it should be in a subshell (command substitution).
78411535bdfSJilles Tjoelker  * Note that jobid, jobs, times and trap can show information not
78511535bdfSJilles Tjoelker  * available in a child process; this is deliberate.
78611535bdfSJilles Tjoelker  * The arguments should already have been expanded.
78711535bdfSJilles Tjoelker  */
78811535bdfSJilles Tjoelker static int
safe_builtin(int idx,int argc,char ** argv)78911535bdfSJilles Tjoelker safe_builtin(int idx, int argc, char **argv)
79011535bdfSJilles Tjoelker {
7913ecb77f0SBryan Drewery 	/* Generated from builtins.def. */
7923ecb77f0SBryan Drewery 	if (safe_builtin_always(idx))
79311535bdfSJilles Tjoelker 		return (1);
79411535bdfSJilles Tjoelker 	if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
79511535bdfSJilles Tjoelker 	    idx == UMASKCMD)
79611535bdfSJilles Tjoelker 		return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
79711535bdfSJilles Tjoelker 	if (idx == SETCMD)
79811535bdfSJilles Tjoelker 		return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
79911535bdfSJilles Tjoelker 		    argv[1][0] == '+') && argv[1][1] == 'o' &&
80011535bdfSJilles Tjoelker 		    argv[1][2] == '\0'));
80111535bdfSJilles Tjoelker 	return (0);
80211535bdfSJilles Tjoelker }
8034b88c807SRodney W. Grimes 
8044b88c807SRodney W. Grimes /*
8054b88c807SRodney W. Grimes  * Execute a simple command.
806e23a66acSJilles Tjoelker  * Note: This may or may not return if (flags & EV_EXIT).
8074b88c807SRodney W. Grimes  */
8084b88c807SRodney W. Grimes 
80988328642SDavid E. O'Brien static void
evalcommand(union node * cmd,int flags,struct backcmd * backcmd)8105134c3f7SWarner Losh evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
8114b88c807SRodney W. Grimes {
8124b88c807SRodney W. Grimes 	union node *argp;
8134b88c807SRodney W. Grimes 	struct arglist arglist;
8144b88c807SRodney W. Grimes 	struct arglist varlist;
8154b88c807SRodney W. Grimes 	char **argv;
8164b88c807SRodney W. Grimes 	int argc;
8174b88c807SRodney W. Grimes 	char **envp;
8184b88c807SRodney W. Grimes 	int varflag;
8194b88c807SRodney W. Grimes 	int mode;
8204b88c807SRodney W. Grimes 	int pip[2];
8214b88c807SRodney W. Grimes 	struct cmdentry cmdentry;
8224b88c807SRodney W. Grimes 	struct job *jp;
8234b88c807SRodney W. Grimes 	struct jmploc jmploc;
824224fbf9fSJilles Tjoelker 	struct jmploc *savehandler;
825224fbf9fSJilles Tjoelker 	char *savecmdname;
826224fbf9fSJilles Tjoelker 	struct shparam saveparam;
827224fbf9fSJilles Tjoelker 	struct localvar *savelocalvars;
828eaa34893SJilles Tjoelker 	struct parsefile *savetopfile;
8294b88c807SRodney W. Grimes 	volatile int e;
8304b88c807SRodney W. Grimes 	char *lastarg;
831c8a5f665SJilles Tjoelker 	int signaled;
832a436dc79SMartin Cracauer 	int do_clearcmdentry;
833c5e4fa99SJilles Tjoelker 	const char *path = pathval();
8348ef0ae8aSJilles Tjoelker 	int i;
8354b88c807SRodney W. Grimes 
8364b88c807SRodney W. Grimes 	/* First expand the arguments. */
8379957cb23SStefan Farfeleder 	TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
8388ef0ae8aSJilles Tjoelker 	emptyarglist(&arglist);
8398ef0ae8aSJilles Tjoelker 	emptyarglist(&varlist);
8404b88c807SRodney W. Grimes 	varflag = 1;
841e23a66acSJilles Tjoelker 	jp = NULL;
842a436dc79SMartin Cracauer 	do_clearcmdentry = 0;
843aa9caaf6SPeter Wemm 	oexitstatus = exitstatus;
844aa9caaf6SPeter Wemm 	exitstatus = 0;
845046bfe52SJilles Tjoelker 	/* Add one slot at the beginning for tryexec(). */
846046bfe52SJilles Tjoelker 	appendarglist(&arglist, nullstr);
8474b88c807SRodney W. Grimes 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
84805a447d0SJilles Tjoelker 		if (varflag && isassignment(argp->narg.text)) {
84984fbdd8cSJilles Tjoelker 			expandarg(argp, varflag == 1 ? &varlist : &arglist,
85084fbdd8cSJilles Tjoelker 			    EXP_VARTILDE);
8514b88c807SRodney W. Grimes 			continue;
85284fbdd8cSJilles Tjoelker 		} else if (varflag == 1)
85384fbdd8cSJilles Tjoelker 			varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
8544b88c807SRodney W. Grimes 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8554b88c807SRodney W. Grimes 	}
856046bfe52SJilles Tjoelker 	appendarglist(&arglist, nullstr);
8574b88c807SRodney W. Grimes 	expredir(cmd->ncmd.redirect);
858046bfe52SJilles Tjoelker 	argc = arglist.count - 2;
859046bfe52SJilles Tjoelker 	argv = &arglist.args[1];
8604b88c807SRodney W. Grimes 
8618ef0ae8aSJilles Tjoelker 	argv[argc] = NULL;
8624b88c807SRodney W. Grimes 	lastarg = NULL;
8634b88c807SRodney W. Grimes 	if (iflag && funcnest == 0 && argc > 0)
8648ef0ae8aSJilles Tjoelker 		lastarg = argv[argc - 1];
8654b88c807SRodney W. Grimes 
8664b88c807SRodney W. Grimes 	/* Print the command if xflag is set. */
867d1670d42SJilles Tjoelker 	if (xflag)
868046bfe52SJilles Tjoelker 		xtracecommand(&varlist, argc, argv);
8694b88c807SRodney W. Grimes 
8704b88c807SRodney W. Grimes 	/* Now locate the command. */
8714b88c807SRodney W. Grimes 	if (argc == 0) {
87285170a4aSStefan Farfeleder 		/* Variable assignment(s) without command */
8734b88c807SRodney W. Grimes 		cmdentry.cmdtype = CMDBUILTIN;
8744b88c807SRodney W. Grimes 		cmdentry.u.index = BLTINCMD;
8753a64dbc2SJilles Tjoelker 		cmdentry.special = 0;
8764b88c807SRodney W. Grimes 	} else {
877aa9caaf6SPeter Wemm 		static const char PATH[] = "PATH=";
878c848bc18SJilles Tjoelker 		int cmd_flags = 0, bltinonly = 0;
879aa9caaf6SPeter Wemm 
880aa9caaf6SPeter Wemm 		/*
881aa9caaf6SPeter Wemm 		 * Modify the command lookup path, if a PATH= assignment
882aa9caaf6SPeter Wemm 		 * is present
883aa9caaf6SPeter Wemm 		 */
8848ef0ae8aSJilles Tjoelker 		for (i = 0; i < varlist.count; i++)
8858ef0ae8aSJilles Tjoelker 			if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) {
8868ef0ae8aSJilles Tjoelker 				path = varlist.args[i] + sizeof(PATH) - 1;
887a436dc79SMartin Cracauer 				/*
888a436dc79SMartin Cracauer 				 * On `PATH=... command`, we need to make
889a436dc79SMartin Cracauer 				 * sure that the command isn't using the
890a436dc79SMartin Cracauer 				 * non-updated hash table of the outer PATH
891a436dc79SMartin Cracauer 				 * setting and we need to make sure that
892a436dc79SMartin Cracauer 				 * the hash table isn't filled with items
893a436dc79SMartin Cracauer 				 * from the temporary setting.
894a436dc79SMartin Cracauer 				 *
89548556dffSElyes HAOUAS 				 * It would be better to forbid using and
896a436dc79SMartin Cracauer 				 * updating the table while this command
897a436dc79SMartin Cracauer 				 * runs, by the command finding mechanism
898a436dc79SMartin Cracauer 				 * is heavily integrated with hash handling,
899a436dc79SMartin Cracauer 				 * so we just delete the hash before and after
900a436dc79SMartin Cracauer 				 * the command runs. Partly deleting like
901a436dc79SMartin Cracauer 				 * changepatch() does doesn't seem worth the
902*c38fe1d2SPei-Ju Chien 				 * booking effort, since most such runs add
9038aa55d81SMaxim Konovalov 				 * directories in front of the new PATH.
904a436dc79SMartin Cracauer 				 */
905c059d822SJilles Tjoelker 				clearcmdentry();
906a436dc79SMartin Cracauer 				do_clearcmdentry = 1;
907a436dc79SMartin Cracauer 			}
908aa9caaf6SPeter Wemm 
9094b88c807SRodney W. Grimes 		for (;;) {
910c848bc18SJilles Tjoelker 			if (bltinonly) {
911c848bc18SJilles Tjoelker 				cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
912c848bc18SJilles Tjoelker 				if (cmdentry.u.index < 0) {
913dc82a6f6SJilles Tjoelker 					cmdentry.u.index = BLTINCMD;
914dc82a6f6SJilles Tjoelker 					argv--;
915dc82a6f6SJilles Tjoelker 					argc++;
916dc82a6f6SJilles Tjoelker 					break;
9174b88c807SRodney W. Grimes 				}
918c848bc18SJilles Tjoelker 			} else
919c848bc18SJilles Tjoelker 				find_command(argv[0], &cmdentry, cmd_flags, path);
920c848bc18SJilles Tjoelker 			/* implement the bltin and command builtins here */
921c848bc18SJilles Tjoelker 			if (cmdentry.cmdtype != CMDBUILTIN)
922c848bc18SJilles Tjoelker 				break;
923c848bc18SJilles Tjoelker 			if (cmdentry.u.index == BLTINCMD) {
924c848bc18SJilles Tjoelker 				if (argc == 1)
925c848bc18SJilles Tjoelker 					break;
926c848bc18SJilles Tjoelker 				argv++;
927c848bc18SJilles Tjoelker 				argc--;
928c848bc18SJilles Tjoelker 				bltinonly = 1;
929c848bc18SJilles Tjoelker 			} else if (cmdentry.u.index == COMMANDCMD) {
930c848bc18SJilles Tjoelker 				if (argc == 1)
931c848bc18SJilles Tjoelker 					break;
932c848bc18SJilles Tjoelker 				if (!strcmp(argv[1], "-p")) {
933c848bc18SJilles Tjoelker 					if (argc == 2)
934c848bc18SJilles Tjoelker 						break;
935c848bc18SJilles Tjoelker 					if (argv[2][0] == '-') {
936c848bc18SJilles Tjoelker 						if (strcmp(argv[2], "--"))
937c848bc18SJilles Tjoelker 							break;
938c848bc18SJilles Tjoelker 						if (argc == 3)
939c848bc18SJilles Tjoelker 							break;
940c848bc18SJilles Tjoelker 						argv += 3;
941c848bc18SJilles Tjoelker 						argc -= 3;
942c848bc18SJilles Tjoelker 					} else {
943c848bc18SJilles Tjoelker 						argv += 2;
944c848bc18SJilles Tjoelker 						argc -= 2;
945c848bc18SJilles Tjoelker 					}
946c848bc18SJilles Tjoelker 					path = _PATH_STDPATH;
947c059d822SJilles Tjoelker 					clearcmdentry();
948c848bc18SJilles Tjoelker 					do_clearcmdentry = 1;
949c848bc18SJilles Tjoelker 				} else if (!strcmp(argv[1], "--")) {
950c848bc18SJilles Tjoelker 					if (argc == 2)
951c848bc18SJilles Tjoelker 						break;
952c848bc18SJilles Tjoelker 					argv += 2;
953c848bc18SJilles Tjoelker 					argc -= 2;
954c848bc18SJilles Tjoelker 				} else if (argv[1][0] == '-')
955c848bc18SJilles Tjoelker 					break;
956c848bc18SJilles Tjoelker 				else {
957c848bc18SJilles Tjoelker 					argv++;
958c848bc18SJilles Tjoelker 					argc--;
959c848bc18SJilles Tjoelker 				}
960c848bc18SJilles Tjoelker 				cmd_flags |= DO_NOFUNC;
961c848bc18SJilles Tjoelker 				bltinonly = 0;
962c848bc18SJilles Tjoelker 			} else
9634b88c807SRodney W. Grimes 				break;
9644b88c807SRodney W. Grimes 		}
965c848bc18SJilles Tjoelker 		/*
966c848bc18SJilles Tjoelker 		 * Special builtins lose their special properties when
967c848bc18SJilles Tjoelker 		 * called via 'command'.
968c848bc18SJilles Tjoelker 		 */
969c848bc18SJilles Tjoelker 		if (cmd_flags & DO_NOFUNC)
970c848bc18SJilles Tjoelker 			cmdentry.special = 0;
9714b88c807SRodney W. Grimes 	}
9724b88c807SRodney W. Grimes 
9734b88c807SRodney W. Grimes 	/* Fork off a child process if necessary. */
97447e5204eSJilles Tjoelker 	if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
9756e28dacfSJilles Tjoelker 	    && ((flags & EV_EXIT) == 0 || have_traps()))
976aa9caaf6SPeter Wemm 	 || ((flags & EV_BACKCMD) != 0
97711535bdfSJilles Tjoelker 	    && (cmdentry.cmdtype != CMDBUILTIN ||
97811535bdfSJilles Tjoelker 		 !safe_builtin(cmdentry.u.index, argc, argv)))) {
9794b88c807SRodney W. Grimes 		jp = makejob(cmd, 1);
98047e5204eSJilles Tjoelker 		mode = FORK_FG;
9814b88c807SRodney W. Grimes 		if (flags & EV_BACKCMD) {
9824b88c807SRodney W. Grimes 			mode = FORK_NOJOB;
9834b88c807SRodney W. Grimes 			if (pipe(pip) < 0)
9846c48b6cfSMartin Cracauer 				error("Pipe call failed: %s", strerror(errno));
9854b88c807SRodney W. Grimes 		}
986caf29fabSJilles Tjoelker 		if (cmdentry.cmdtype == CMDNORMAL &&
987caf29fabSJilles Tjoelker 		    cmd->ncmd.redirect == NULL &&
9888ef0ae8aSJilles Tjoelker 		    varlist.count == 0 &&
989caf29fabSJilles Tjoelker 		    (mode == FORK_FG || mode == FORK_NOJOB) &&
990caf29fabSJilles Tjoelker 		    !disvforkset() && !iflag && !mflag) {
991caf29fabSJilles Tjoelker 			vforkexecshell(jp, argv, environment(), path,
992caf29fabSJilles Tjoelker 			    cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL);
993caf29fabSJilles Tjoelker 			goto parent;
994caf29fabSJilles Tjoelker 		}
9954b88c807SRodney W. Grimes 		if (forkshell(jp, cmd, mode) != 0)
9964b88c807SRodney W. Grimes 			goto parent;	/* at end of routine */
9974b88c807SRodney W. Grimes 		if (flags & EV_BACKCMD) {
9984b88c807SRodney W. Grimes 			FORCEINTON;
9994b88c807SRodney W. Grimes 			close(pip[0]);
10004b88c807SRodney W. Grimes 			if (pip[1] != 1) {
10017e1c7266SDag-Erling Smørgrav 				dup2(pip[1], 1);
10024b88c807SRodney W. Grimes 				close(pip[1]);
10034b88c807SRodney W. Grimes 			}
1004c5aef537SJilles Tjoelker 			flags &= ~EV_BACKCMD;
10054b88c807SRodney W. Grimes 		}
10064b88c807SRodney W. Grimes 		flags |= EV_EXIT;
10074b88c807SRodney W. Grimes 	}
10084b88c807SRodney W. Grimes 
10094b88c807SRodney W. Grimes 	/* This is the child process if a fork occurred. */
10104b88c807SRodney W. Grimes 	/* Execute the command. */
10114b88c807SRodney W. Grimes 	if (cmdentry.cmdtype == CMDFUNCTION) {
1012ab0a2172SSteve Price #ifdef DEBUG
10134b88c807SRodney W. Grimes 		trputs("Shell function:  ");  trargs(argv);
1014ab0a2172SSteve Price #endif
10154b88c807SRodney W. Grimes 		saveparam = shellparam;
10164b88c807SRodney W. Grimes 		shellparam.malloc = 0;
1017ab0a2172SSteve Price 		shellparam.reset = 1;
10184b88c807SRodney W. Grimes 		shellparam.nparam = argc - 1;
10194b88c807SRodney W. Grimes 		shellparam.p = argv + 1;
10201bc2fdfaSJilles Tjoelker 		shellparam.optp = NULL;
10214b88c807SRodney W. Grimes 		shellparam.optnext = NULL;
10224b88c807SRodney W. Grimes 		INTOFF;
10234b88c807SRodney W. Grimes 		savelocalvars = localvars;
10244b88c807SRodney W. Grimes 		localvars = NULL;
1025eb33e843SJilles Tjoelker 		reffunc(cmdentry.u.func);
1026224fbf9fSJilles Tjoelker 		savehandler = handler;
10274b88c807SRodney W. Grimes 		if (setjmp(jmploc.loc)) {
102817490974SJilles Tjoelker 			popredir();
1029eb33e843SJilles Tjoelker 			unreffunc(cmdentry.u.func);
10304b88c807SRodney W. Grimes 			poplocalvars();
10314b88c807SRodney W. Grimes 			localvars = savelocalvars;
1032cf45f124SJilles Tjoelker 			freeparam(&shellparam);
1033cf45f124SJilles Tjoelker 			shellparam = saveparam;
103492004afeSJilles Tjoelker 			funcnest--;
10354b88c807SRodney W. Grimes 			handler = savehandler;
10364b88c807SRodney W. Grimes 			longjmp(handler->loc, 1);
10374b88c807SRodney W. Grimes 		}
10384b88c807SRodney W. Grimes 		handler = &jmploc;
103992004afeSJilles Tjoelker 		funcnest++;
104017490974SJilles Tjoelker 		redirect(cmd->ncmd.redirect, REDIR_PUSH);
10419922c6d2SJilles Tjoelker 		INTON;
10428ef0ae8aSJilles Tjoelker 		for (i = 0; i < varlist.count; i++)
10438ef0ae8aSJilles Tjoelker 			mklocal(varlist.args[i]);
104494c53a08SStefan Farfeleder 		exitstatus = oexitstatus;
104545496405SJilles Tjoelker 		evaltree(getfuncnode(cmdentry.u.func),
104645496405SJilles Tjoelker 		    flags & (EV_TESTED | EV_EXIT));
10474b88c807SRodney W. Grimes 		INTOFF;
1048eb33e843SJilles Tjoelker 		unreffunc(cmdentry.u.func);
10494b88c807SRodney W. Grimes 		poplocalvars();
10504b88c807SRodney W. Grimes 		localvars = savelocalvars;
10514b88c807SRodney W. Grimes 		freeparam(&shellparam);
10524b88c807SRodney W. Grimes 		shellparam = saveparam;
10534b88c807SRodney W. Grimes 		handler = savehandler;
105492004afeSJilles Tjoelker 		funcnest--;
10554b88c807SRodney W. Grimes 		popredir();
10564b88c807SRodney W. Grimes 		INTON;
10572935c4ccSJilles Tjoelker 		if (evalskip == SKIPRETURN) {
10584b88c807SRodney W. Grimes 			evalskip = 0;
10594b88c807SRodney W. Grimes 			skipcount = 0;
10604b88c807SRodney W. Grimes 		}
1061e23a66acSJilles Tjoelker 		if (jp)
10624b88c807SRodney W. Grimes 			exitshell(exitstatus);
10634b88c807SRodney W. Grimes 	} else if (cmdentry.cmdtype == CMDBUILTIN) {
1064ab0a2172SSteve Price #ifdef DEBUG
10654b88c807SRodney W. Grimes 		trputs("builtin command:  ");  trargs(argv);
1066ab0a2172SSteve Price #endif
10674b88c807SRodney W. Grimes 		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
10684b88c807SRodney W. Grimes 		if (flags == EV_BACKCMD) {
10694b88c807SRodney W. Grimes 			memout.nextc = memout.buf;
10704b88c807SRodney W. Grimes 			mode |= REDIR_BACKQ;
10714b88c807SRodney W. Grimes 		}
10724b88c807SRodney W. Grimes 		savecmdname = commandname;
1073eaa34893SJilles Tjoelker 		savetopfile = getcurrentfile();
10748ef0ae8aSJilles Tjoelker 		cmdenviron = &varlist;
10754b88c807SRodney W. Grimes 		e = -1;
1076224fbf9fSJilles Tjoelker 		savehandler = handler;
10774b88c807SRodney W. Grimes 		if (setjmp(jmploc.loc)) {
10784b88c807SRodney W. Grimes 			e = exception;
107945496405SJilles Tjoelker 			if (e == EXINT)
108045496405SJilles Tjoelker 				exitstatus = SIGINT+128;
10814b88c807SRodney W. Grimes 			goto cmddone;
10824b88c807SRodney W. Grimes 		}
10834b88c807SRodney W. Grimes 		handler = &jmploc;
108485170a4aSStefan Farfeleder 		redirect(cmd->ncmd.redirect, mode);
1085d6d66cfcSJilles Tjoelker 		outclearerror(out1);
10863a64dbc2SJilles Tjoelker 		/*
10873a64dbc2SJilles Tjoelker 		 * If there is no command word, redirection errors should
10883a64dbc2SJilles Tjoelker 		 * not be fatal but assignment errors should.
10893a64dbc2SJilles Tjoelker 		 */
1090a82f5687SJilles Tjoelker 		if (argc == 0)
10913a64dbc2SJilles Tjoelker 			cmdentry.special = 1;
1092850460c0SJilles Tjoelker 		listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1093e4b50334SJilles Tjoelker 		if (argc > 0)
1094e4b50334SJilles Tjoelker 			bltinsetlocale();
10954b88c807SRodney W. Grimes 		commandname = argv[0];
10964b88c807SRodney W. Grimes 		argptr = argv + 1;
1097384aedabSJilles Tjoelker 		nextopt_optptr = NULL;		/* initialize nextopt */
1098cb806389SStefan Farfeleder 		builtin_flags = flags;
10994b88c807SRodney W. Grimes 		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
11004b88c807SRodney W. Grimes 		flushall();
1101d6d66cfcSJilles Tjoelker 		if (outiserror(out1)) {
1102d6d66cfcSJilles Tjoelker 			warning("write error on stdout");
1103d6d66cfcSJilles Tjoelker 			if (exitstatus == 0 || exitstatus == 1)
1104d6d66cfcSJilles Tjoelker 				exitstatus = 2;
1105d6d66cfcSJilles Tjoelker 		}
11064b88c807SRodney W. Grimes cmddone:
1107e4b50334SJilles Tjoelker 		if (argc > 0)
1108e4b50334SJilles Tjoelker 			bltinunsetlocale();
1109b2acf887SMartin Cracauer 		cmdenviron = NULL;
11104b88c807SRodney W. Grimes 		out1 = &output;
11114b88c807SRodney W. Grimes 		out2 = &errout;
11124b88c807SRodney W. Grimes 		freestdout();
1113e23a66acSJilles Tjoelker 		handler = savehandler;
11144b88c807SRodney W. Grimes 		commandname = savecmdname;
1115e23a66acSJilles Tjoelker 		if (jp)
11164b88c807SRodney W. Grimes 			exitshell(exitstatus);
11174b88c807SRodney W. Grimes 		if (flags == EV_BACKCMD) {
11184b88c807SRodney W. Grimes 			backcmd->buf = memout.buf;
11195183ddf2SJilles Tjoelker 			backcmd->nleft = memout.buf != NULL ?
11205183ddf2SJilles Tjoelker 			    memout.nextc - memout.buf : 0;
11214b88c807SRodney W. Grimes 			memout.buf = NULL;
11225183ddf2SJilles Tjoelker 			memout.nextc = NULL;
11235183ddf2SJilles Tjoelker 			memout.bufend = NULL;
1124de29cd08SJilles Tjoelker 			memout.bufsize = 64;
11254b88c807SRodney W. Grimes 		}
112645496405SJilles Tjoelker 		if (cmdentry.u.index != EXECCMD)
1127544754dfSJilles Tjoelker 			popredir();
1128f7cc73afSJilles Tjoelker 		if (e != -1) {
1129bb324af6SJilles Tjoelker 			if (e != EXERROR || cmdentry.special)
1130f7cc73afSJilles Tjoelker 				exraise(e);
1131f7cc73afSJilles Tjoelker 			popfilesupto(savetopfile);
1132f7cc73afSJilles Tjoelker 			if (flags != EV_BACKCMD)
1133f7cc73afSJilles Tjoelker 				FORCEINTON;
1134f7cc73afSJilles Tjoelker 		}
11354b88c807SRodney W. Grimes 	} else {
1136ab0a2172SSteve Price #ifdef DEBUG
11374b88c807SRodney W. Grimes 		trputs("normal command:  ");  trargs(argv);
1138ab0a2172SSteve Price #endif
11394b88c807SRodney W. Grimes 		redirect(cmd->ncmd.redirect, 0);
11408ef0ae8aSJilles Tjoelker 		for (i = 0; i < varlist.count; i++)
11418ef0ae8aSJilles Tjoelker 			setvareq(varlist.args[i], VEXPORT|VSTACK);
11424b88c807SRodney W. Grimes 		envp = environment();
1143c848bc18SJilles Tjoelker 		shellexec(argv, envp, path, cmdentry.u.index);
11444b88c807SRodney W. Grimes 		/*NOTREACHED*/
11454b88c807SRodney W. Grimes 	}
11464b88c807SRodney W. Grimes 	goto out;
11474b88c807SRodney W. Grimes 
11484b88c807SRodney W. Grimes parent:	/* parent process gets here (if we forked) */
114927542743SJilles Tjoelker 	if (mode == FORK_FG) {	/* argument to fork */
11504b88c807SRodney W. Grimes 		INTOFF;
1151c8a5f665SJilles Tjoelker 		exitstatus = waitforjob(jp, &signaled);
11524b88c807SRodney W. Grimes 		INTON;
1153c8a5f665SJilles Tjoelker 		if (iflag && loopnest > 0 && signaled) {
115444874954SMartin Cracauer 			evalskip = SKIPBREAK;
115544874954SMartin Cracauer 			skipcount = loopnest;
115644874954SMartin Cracauer 		}
115727542743SJilles Tjoelker 	} else if (mode == FORK_NOJOB) {
11584b88c807SRodney W. Grimes 		backcmd->fd = pip[0];
11594b88c807SRodney W. Grimes 		close(pip[1]);
11604b88c807SRodney W. Grimes 		backcmd->jp = jp;
116147e5204eSJilles Tjoelker 	}
11624b88c807SRodney W. Grimes 
11634b88c807SRodney W. Grimes out:
11644b88c807SRodney W. Grimes 	if (lastarg)
11654b88c807SRodney W. Grimes 		setvar("_", lastarg, 0);
1166a436dc79SMartin Cracauer 	if (do_clearcmdentry)
1167c059d822SJilles Tjoelker 		clearcmdentry();
11684b88c807SRodney W. Grimes }
11694b88c807SRodney W. Grimes 
11704b88c807SRodney W. Grimes 
11714b88c807SRodney W. Grimes 
11724b88c807SRodney W. Grimes /*
11734b88c807SRodney W. Grimes  * Search for a command.  This is called before we fork so that the
11744b88c807SRodney W. Grimes  * location of the command will be available in the parent as well as
11754b88c807SRodney W. Grimes  * the child.  The check for "goodname" is an overly conservative
11764b88c807SRodney W. Grimes  * check that the name will not be subject to expansion.
11774b88c807SRodney W. Grimes  */
11784b88c807SRodney W. Grimes 
117988328642SDavid E. O'Brien static void
prehash(union node * n)11805134c3f7SWarner Losh prehash(union node *n)
11814b88c807SRodney W. Grimes {
11824b88c807SRodney W. Grimes 	struct cmdentry entry;
11834b88c807SRodney W. Grimes 
1184cecd2b6cSStefan Farfeleder 	if (n && n->type == NCMD && n->ncmd.args)
1185aa9caaf6SPeter Wemm 		if (goodname(n->ncmd.args->narg.text))
1186aa9caaf6SPeter Wemm 			find_command(n->ncmd.args->narg.text, &entry, 0,
1187aa9caaf6SPeter Wemm 				     pathval());
11884b88c807SRodney W. Grimes }
11894b88c807SRodney W. Grimes 
11904b88c807SRodney W. Grimes 
11914b88c807SRodney W. Grimes 
11924b88c807SRodney W. Grimes /*
11934b88c807SRodney W. Grimes  * Builtin commands.  Builtin commands whose functions are closely
11944b88c807SRodney W. Grimes  * tied to evaluation are implemented here.
11954b88c807SRodney W. Grimes  */
11964b88c807SRodney W. Grimes 
11974b88c807SRodney W. Grimes /*
1198dc82a6f6SJilles Tjoelker  * No command given, a bltin command with no arguments, or a bltin command
1199dc82a6f6SJilles Tjoelker  * with an invalid name.
12004b88c807SRodney W. Grimes  */
12014b88c807SRodney W. Grimes 
1202aa9caaf6SPeter Wemm int
bltincmd(int argc,char ** argv)1203dc82a6f6SJilles Tjoelker bltincmd(int argc, char **argv)
1204aa9caaf6SPeter Wemm {
1205dc82a6f6SJilles Tjoelker 	if (argc > 1) {
1206dc82a6f6SJilles Tjoelker 		out2fmt_flush("%s: not found\n", argv[1]);
1207dc82a6f6SJilles Tjoelker 		return 127;
1208dc82a6f6SJilles Tjoelker 	}
1209aa9caaf6SPeter Wemm 	/*
1210b9807277SJilles Tjoelker 	 * Preserve exitstatus of a previous possible command substitution
1211aa9caaf6SPeter Wemm 	 * as POSIX mandates
1212aa9caaf6SPeter Wemm 	 */
12134b88c807SRodney W. Grimes 	return exitstatus;
12144b88c807SRodney W. Grimes }
12154b88c807SRodney W. Grimes 
12164b88c807SRodney W. Grimes 
12174b88c807SRodney W. Grimes /*
12184b88c807SRodney W. Grimes  * Handle break and continue commands.  Break, continue, and return are
12194b88c807SRodney W. Grimes  * all handled by setting the evalskip flag.  The evaluation routines
12204b88c807SRodney W. Grimes  * above all check this flag, and if it is set they start skipping
12214b88c807SRodney W. Grimes  * commands rather than executing them.  The variable skipcount is
12224b88c807SRodney W. Grimes  * the number of loops to break/continue, or the number of function
12234b88c807SRodney W. Grimes  * levels to return.  (The latter is always 1.)  It should probably
12244b88c807SRodney W. Grimes  * be an error to break out of more loops than exist, but it isn't
12254b88c807SRodney W. Grimes  * in the standard shell so we don't make it one here.
12264b88c807SRodney W. Grimes  */
12274b88c807SRodney W. Grimes 
1228aa9caaf6SPeter Wemm int
breakcmd(int argc,char ** argv)12295134c3f7SWarner Losh breakcmd(int argc, char **argv)
1230aa9caaf6SPeter Wemm {
12314d34663bSJilles Tjoelker 	long n;
12324d34663bSJilles Tjoelker 	char *end;
12334b88c807SRodney W. Grimes 
12344d34663bSJilles Tjoelker 	if (argc > 1) {
12354d34663bSJilles Tjoelker 		/* Allow arbitrarily large numbers. */
12364d34663bSJilles Tjoelker 		n = strtol(argv[1], &end, 10);
12374d34663bSJilles Tjoelker 		if (!is_digit(argv[1][0]) || *end != '\0')
12384d34663bSJilles Tjoelker 			error("Illegal number: %s", argv[1]);
12394d34663bSJilles Tjoelker 	} else
12404d34663bSJilles Tjoelker 		n = 1;
12414b88c807SRodney W. Grimes 	if (n > loopnest)
12424b88c807SRodney W. Grimes 		n = loopnest;
12434b88c807SRodney W. Grimes 	if (n > 0) {
12444b88c807SRodney W. Grimes 		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
12454b88c807SRodney W. Grimes 		skipcount = n;
12464b88c807SRodney W. Grimes 	}
12474b88c807SRodney W. Grimes 	return 0;
12484b88c807SRodney W. Grimes }
12494b88c807SRodney W. Grimes 
12502babaf74STim J. Robbins /*
12512babaf74STim J. Robbins  * The `command' command.
12522babaf74STim J. Robbins  */
12532babaf74STim J. Robbins int
commandcmd(int argc __unused,char ** argv __unused)12547cbda738SJilles Tjoelker commandcmd(int argc __unused, char **argv __unused)
12552babaf74STim J. Robbins {
125635ad337dSJilles Tjoelker 	const char *path;
12572babaf74STim J. Robbins 	int ch;
1258b2f153feSStefan Farfeleder 	int cmd = -1;
12592babaf74STim J. Robbins 
1260c848bc18SJilles Tjoelker 	path = bltinlookup("PATH", 1);
12612babaf74STim J. Robbins 
12627cbda738SJilles Tjoelker 	while ((ch = nextopt("pvV")) != '\0') {
12632babaf74STim J. Robbins 		switch (ch) {
12642babaf74STim J. Robbins 		case 'p':
126535ad337dSJilles Tjoelker 			path = _PATH_STDPATH;
12662babaf74STim J. Robbins 			break;
1267b2f153feSStefan Farfeleder 		case 'v':
1268b2f153feSStefan Farfeleder 			cmd = TYPECMD_SMALLV;
1269b2f153feSStefan Farfeleder 			break;
1270b2f153feSStefan Farfeleder 		case 'V':
1271b2f153feSStefan Farfeleder 			cmd = TYPECMD_BIGV;
1272b2f153feSStefan Farfeleder 			break;
12732babaf74STim J. Robbins 		}
12742babaf74STim J. Robbins 	}
12752babaf74STim J. Robbins 
1276b2f153feSStefan Farfeleder 	if (cmd != -1) {
12777cbda738SJilles Tjoelker 		if (*argptr == NULL || argptr[1] != NULL)
1278b2f153feSStefan Farfeleder 			error("wrong number of arguments");
12797cbda738SJilles Tjoelker 		return typecmd_impl(2, argptr - 1, cmd, path);
1280b2f153feSStefan Farfeleder 	}
12817cbda738SJilles Tjoelker 	if (*argptr != NULL)
1282274110dfSJilles Tjoelker 		error("commandcmd bad call");
12832babaf74STim J. Robbins 
12842babaf74STim J. Robbins 	/*
12852babaf74STim J. Robbins 	 * Do nothing successfully if no command was specified;
12862babaf74STim J. Robbins 	 * ksh also does this.
12872babaf74STim J. Robbins 	 */
1288c848bc18SJilles Tjoelker 	return 0;
12892babaf74STim J. Robbins }
12902babaf74STim J. Robbins 
12914b88c807SRodney W. Grimes 
12924b88c807SRodney W. Grimes /*
12934b88c807SRodney W. Grimes  * The return command.
12944b88c807SRodney W. Grimes  */
12954b88c807SRodney W. Grimes 
1296aa9caaf6SPeter Wemm int
returncmd(int argc,char ** argv)12975134c3f7SWarner Losh returncmd(int argc, char **argv)
1298aa9caaf6SPeter Wemm {
1299ab0a2172SSteve Price 	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
13004b88c807SRodney W. Grimes 
13012935c4ccSJilles Tjoelker 	evalskip = SKIPRETURN;
13024b88c807SRodney W. Grimes 	skipcount = 1;
13034b88c807SRodney W. Grimes 	return ret;
13044b88c807SRodney W. Grimes }
13054b88c807SRodney W. Grimes 
13064b88c807SRodney W. Grimes 
1307aa9caaf6SPeter Wemm int
falsecmd(int argc __unused,char ** argv __unused)13085134c3f7SWarner Losh falsecmd(int argc __unused, char **argv __unused)
1309aa9caaf6SPeter Wemm {
1310aa9caaf6SPeter Wemm 	return 1;
1311aa9caaf6SPeter Wemm }
1312aa9caaf6SPeter Wemm 
1313aa9caaf6SPeter Wemm 
1314aa9caaf6SPeter Wemm int
truecmd(int argc __unused,char ** argv __unused)13155134c3f7SWarner Losh truecmd(int argc __unused, char **argv __unused)
1316aa9caaf6SPeter Wemm {
13174b88c807SRodney W. Grimes 	return 0;
13184b88c807SRodney W. Grimes }
13194b88c807SRodney W. Grimes 
13204b88c807SRodney W. Grimes 
1321aa9caaf6SPeter Wemm int
execcmd(int argc,char ** argv)13225134c3f7SWarner Losh execcmd(int argc, char **argv)
1323aa9caaf6SPeter Wemm {
13248ef0ae8aSJilles Tjoelker 	int i;
13258ef0ae8aSJilles Tjoelker 
1326c1564db0SJilles Tjoelker 	/*
1327c1564db0SJilles Tjoelker 	 * Because we have historically not supported any options,
1328c1564db0SJilles Tjoelker 	 * only treat "--" specially.
1329c1564db0SJilles Tjoelker 	 */
1330c1564db0SJilles Tjoelker 	if (argc > 1 && strcmp(argv[1], "--") == 0)
1331c1564db0SJilles Tjoelker 		argc--, argv++;
13324b88c807SRodney W. Grimes 	if (argc > 1) {
13334b88c807SRodney W. Grimes 		iflag = 0;		/* exit on error */
13344b88c807SRodney W. Grimes 		mflag = 0;
13354b88c807SRodney W. Grimes 		optschanged();
13368ef0ae8aSJilles Tjoelker 		for (i = 0; i < cmdenviron->count; i++)
13378ef0ae8aSJilles Tjoelker 			setvareq(cmdenviron->args[i], VEXPORT|VSTACK);
13384b88c807SRodney W. Grimes 		shellexec(argv + 1, environment(), pathval(), 0);
13394b88c807SRodney W. Grimes 
13404b88c807SRodney W. Grimes 	}
13414b88c807SRodney W. Grimes 	return 0;
13424b88c807SRodney W. Grimes }
13431974986aSStefan Farfeleder 
13441974986aSStefan Farfeleder 
13451974986aSStefan Farfeleder int
timescmd(int argc __unused,char ** argv __unused)13461974986aSStefan Farfeleder timescmd(int argc __unused, char **argv __unused)
13471974986aSStefan Farfeleder {
13481974986aSStefan Farfeleder 	struct rusage ru;
13491974986aSStefan Farfeleder 	long shumins, shsmins, chumins, chsmins;
13501974986aSStefan Farfeleder 	double shusecs, shssecs, chusecs, chssecs;
13511974986aSStefan Farfeleder 
13521974986aSStefan Farfeleder 	if (getrusage(RUSAGE_SELF, &ru) < 0)
13531974986aSStefan Farfeleder 		return 1;
13541974986aSStefan Farfeleder 	shumins = ru.ru_utime.tv_sec / 60;
13551974986aSStefan Farfeleder 	shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
13561974986aSStefan Farfeleder 	shsmins = ru.ru_stime.tv_sec / 60;
13571974986aSStefan Farfeleder 	shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
13581974986aSStefan Farfeleder 	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
13591974986aSStefan Farfeleder 		return 1;
13601974986aSStefan Farfeleder 	chumins = ru.ru_utime.tv_sec / 60;
13611974986aSStefan Farfeleder 	chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
13621974986aSStefan Farfeleder 	chsmins = ru.ru_stime.tv_sec / 60;
13631974986aSStefan Farfeleder 	chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
13641974986aSStefan Farfeleder 	out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
13651974986aSStefan Farfeleder 	    shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
13661974986aSStefan Farfeleder 	return 0;
13671974986aSStefan Farfeleder }
1368