eval.c revision 52526
1/*-
2 * Copyright (c) 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
40#endif
41static const char rcsid[] =
42  "$FreeBSD: head/bin/sh/eval.c 52526 1999-10-26 13:17:18Z cracauer $";
43#endif /* not lint */
44
45#include <signal.h>
46#include <unistd.h>
47#include <sys/wait.h> /* For WIFSIGNALED(status) */
48
49/*
50 * Evaluate a command.
51 */
52
53#include "shell.h"
54#include "nodes.h"
55#include "syntax.h"
56#include "expand.h"
57#include "parser.h"
58#include "jobs.h"
59#include "eval.h"
60#include "builtins.h"
61#include "options.h"
62#include "exec.h"
63#include "redir.h"
64#include "input.h"
65#include "output.h"
66#include "trap.h"
67#include "var.h"
68#include "memalloc.h"
69#include "error.h"
70#include "show.h"
71#include "mystring.h"
72#ifndef NO_HISTORY
73#include "myhistedit.h"
74#endif
75
76
77/* flags in argument to evaltree */
78#define EV_EXIT 01		/* exit after evaluating tree */
79#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
80#define EV_BACKCMD 04		/* command executing within back quotes */
81
82MKINIT int evalskip;		/* set if we are skipping commands */
83STATIC int skipcount;		/* number of levels to skip */
84MKINIT int loopnest;		/* current loop nesting level */
85int funcnest;			/* depth of function calls */
86
87
88char *commandname;
89struct strlist *cmdenviron;
90int exitstatus;			/* exit status of last command */
91int oexitstatus;		/* saved exit status */
92
93
94STATIC void evalloop __P((union node *));
95STATIC void evalfor __P((union node *));
96STATIC void evalcase __P((union node *, int));
97STATIC void evalsubshell __P((union node *, int));
98STATIC void expredir __P((union node *));
99STATIC void evalpipe __P((union node *));
100STATIC void evalcommand __P((union node *, int, struct backcmd *));
101STATIC void prehash __P((union node *));
102
103
104/*
105 * Called to reset things after an exception.
106 */
107
108#ifdef mkinit
109INCLUDE "eval.h"
110
111RESET {
112	evalskip = 0;
113	loopnest = 0;
114	funcnest = 0;
115}
116
117SHELLPROC {
118	exitstatus = 0;
119}
120#endif
121
122
123
124/*
125 * The eval command.
126 */
127
128int
129evalcmd(argc, argv)
130	int argc;
131	char **argv;
132{
133        char *p;
134        char *concat;
135        char **ap;
136
137        if (argc > 1) {
138                p = argv[1];
139                if (argc > 2) {
140                        STARTSTACKSTR(concat);
141                        ap = argv + 2;
142                        for (;;) {
143                                while (*p)
144                                        STPUTC(*p++, concat);
145                                if ((p = *ap++) == NULL)
146                                        break;
147                                STPUTC(' ', concat);
148                        }
149                        STPUTC('\0', concat);
150                        p = grabstackstr(concat);
151                }
152                evalstring(p);
153        }
154        return exitstatus;
155}
156
157
158/*
159 * Execute a command or commands contained in a string.
160 */
161
162void
163evalstring(s)
164	char *s;
165	{
166	union node *n;
167	struct stackmark smark;
168
169	setstackmark(&smark);
170	setinputstring(s, 1);
171	while ((n = parsecmd(0)) != NEOF) {
172		evaltree(n, 0);
173		popstackmark(&smark);
174	}
175	popfile();
176	popstackmark(&smark);
177}
178
179
180
181/*
182 * Evaluate a parse tree.  The value is left in the global variable
183 * exitstatus.
184 */
185
186void
187evaltree(n, flags)
188	union node *n;
189	int flags;
190{
191	if (n == NULL) {
192		TRACE(("evaltree(NULL) called\n"));
193		exitstatus = 0;
194		goto out;
195	}
196#ifndef NO_HISTORY
197	displayhist = 1;	/* show history substitutions done with fc */
198#endif
199	TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
200	switch (n->type) {
201	case NSEMI:
202		evaltree(n->nbinary.ch1, 0);
203		if (evalskip)
204			goto out;
205		evaltree(n->nbinary.ch2, flags);
206		break;
207	case NAND:
208		evaltree(n->nbinary.ch1, EV_TESTED);
209		if (evalskip || exitstatus != 0) {
210			flags |= EV_TESTED;
211			goto out;
212		}
213		evaltree(n->nbinary.ch2, flags);
214		break;
215	case NOR:
216		evaltree(n->nbinary.ch1, EV_TESTED);
217		if (evalskip || exitstatus == 0)
218			goto out;
219		evaltree(n->nbinary.ch2, flags);
220		break;
221	case NREDIR:
222		expredir(n->nredir.redirect);
223		redirect(n->nredir.redirect, REDIR_PUSH);
224		evaltree(n->nredir.n, flags);
225		popredir();
226		break;
227	case NSUBSHELL:
228		evalsubshell(n, flags);
229		break;
230	case NBACKGND:
231		evalsubshell(n, flags);
232		break;
233	case NIF: {
234		evaltree(n->nif.test, EV_TESTED);
235		if (evalskip)
236			goto out;
237		if (exitstatus == 0)
238			evaltree(n->nif.ifpart, flags);
239		else if (n->nif.elsepart)
240			evaltree(n->nif.elsepart, flags);
241		else
242			exitstatus = 0;
243		break;
244	}
245	case NWHILE:
246	case NUNTIL:
247		evalloop(n);
248		break;
249	case NFOR:
250		evalfor(n);
251		break;
252	case NCASE:
253		evalcase(n, flags);
254		break;
255	case NDEFUN:
256		defun(n->narg.text, n->narg.next);
257		exitstatus = 0;
258		break;
259	case NNOT:
260		evaltree(n->nnot.com, EV_TESTED);
261		exitstatus = !exitstatus;
262		break;
263
264	case NPIPE:
265		evalpipe(n);
266		break;
267	case NCMD:
268		evalcommand(n, flags, (struct backcmd *)NULL);
269		break;
270	default:
271		out1fmt("Node type = %d\n", n->type);
272		flushout(&output);
273		break;
274	}
275out:
276	if (pendingsigs)
277		dotrap();
278	/*
279	 * XXX - Like "!(n->type == NSEMI)", more types will probably
280	 * need to be excluded from this test. It's probably better
281	 * to set or unset EV_TESTED in the loop above than to bloat
282	 * the conditional here.
283	 */
284	if ((flags & EV_EXIT) || (eflag && exitstatus
285	    && !(flags & EV_TESTED) && !(n->type == NSEMI)))
286		exitshell(exitstatus);
287}
288
289
290STATIC void
291evalloop(n)
292	union node *n;
293{
294	int status;
295
296	loopnest++;
297	status = 0;
298	for (;;) {
299		evaltree(n->nbinary.ch1, EV_TESTED);
300		if (evalskip) {
301skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
302				evalskip = 0;
303				continue;
304			}
305			if (evalskip == SKIPBREAK && --skipcount <= 0)
306				evalskip = 0;
307			break;
308		}
309		if (n->type == NWHILE) {
310			if (exitstatus != 0)
311				break;
312		} else {
313			if (exitstatus == 0)
314				break;
315		}
316		evaltree(n->nbinary.ch2, 0);
317		status = exitstatus;
318		if (evalskip)
319			goto skipping;
320	}
321	loopnest--;
322	exitstatus = status;
323}
324
325
326
327STATIC void
328evalfor(n)
329    union node *n;
330{
331	struct arglist arglist;
332	union node *argp;
333	struct strlist *sp;
334	struct stackmark smark;
335
336	setstackmark(&smark);
337	arglist.lastp = &arglist.list;
338	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
339		oexitstatus = exitstatus;
340		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
341		if (evalskip)
342			goto out;
343	}
344	*arglist.lastp = NULL;
345
346	exitstatus = 0;
347	loopnest++;
348	for (sp = arglist.list ; sp ; sp = sp->next) {
349		setvar(n->nfor.var, sp->text, 0);
350		evaltree(n->nfor.body, 0);
351		if (evalskip) {
352			if (evalskip == SKIPCONT && --skipcount <= 0) {
353				evalskip = 0;
354				continue;
355			}
356			if (evalskip == SKIPBREAK && --skipcount <= 0)
357				evalskip = 0;
358			break;
359		}
360	}
361	loopnest--;
362out:
363	popstackmark(&smark);
364}
365
366
367
368STATIC void
369evalcase(n, flags)
370	union node *n;
371	int flags;
372{
373	union node *cp;
374	union node *patp;
375	struct arglist arglist;
376	struct stackmark smark;
377
378	setstackmark(&smark);
379	arglist.lastp = &arglist.list;
380	oexitstatus = exitstatus;
381	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
382	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
383		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
384			if (casematch(patp, arglist.list->text)) {
385				if (evalskip == 0) {
386					evaltree(cp->nclist.body, flags);
387				}
388				goto out;
389			}
390		}
391	}
392out:
393	popstackmark(&smark);
394}
395
396
397
398/*
399 * Kick off a subshell to evaluate a tree.
400 */
401
402STATIC void
403evalsubshell(n, flags)
404	union node *n;
405	int flags;
406{
407	struct job *jp;
408	int backgnd = (n->type == NBACKGND);
409
410	expredir(n->nredir.redirect);
411	jp = makejob(n, 1);
412	if (forkshell(jp, n, backgnd) == 0) {
413		if (backgnd)
414			flags &=~ EV_TESTED;
415		redirect(n->nredir.redirect, 0);
416		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
417	}
418	if (! backgnd) {
419		INTOFF;
420		exitstatus = waitforjob(jp, (int *)NULL);
421		INTON;
422	}
423}
424
425
426
427/*
428 * Compute the names of the files in a redirection list.
429 */
430
431STATIC void
432expredir(n)
433	union node *n;
434{
435	union node *redir;
436
437	for (redir = n ; redir ; redir = redir->nfile.next) {
438		struct arglist fn;
439		fn.lastp = &fn.list;
440		oexitstatus = exitstatus;
441		switch (redir->type) {
442		case NFROM:
443		case NTO:
444		case NAPPEND:
445			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
446			redir->nfile.expfname = fn.list->text;
447			break;
448		case NFROMFD:
449		case NTOFD:
450			if (redir->ndup.vname) {
451				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
452				fixredir(redir, fn.list->text, 1);
453			}
454			break;
455		}
456	}
457}
458
459
460
461/*
462 * Evaluate a pipeline.  All the processes in the pipeline are children
463 * of the process creating the pipeline.  (This differs from some versions
464 * of the shell, which make the last process in a pipeline the parent
465 * of all the rest.)
466 */
467
468STATIC void
469evalpipe(n)
470	union node *n;
471{
472	struct job *jp;
473	struct nodelist *lp;
474	int pipelen;
475	int prevfd;
476	int pip[2];
477
478	TRACE(("evalpipe(0x%lx) called\n", (long)n));
479	pipelen = 0;
480	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
481		pipelen++;
482	INTOFF;
483	jp = makejob(n, pipelen);
484	prevfd = -1;
485	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
486		prehash(lp->n);
487		pip[1] = -1;
488		if (lp->next) {
489			if (pipe(pip) < 0) {
490				close(prevfd);
491				error("Pipe call failed");
492			}
493		}
494		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
495			INTON;
496			if (prevfd > 0) {
497				close(0);
498				copyfd(prevfd, 0);
499				close(prevfd);
500			}
501			if (pip[1] >= 0) {
502				close(pip[0]);
503				if (pip[1] != 1) {
504					close(1);
505					copyfd(pip[1], 1);
506					close(pip[1]);
507				}
508			}
509			evaltree(lp->n, EV_EXIT);
510		}
511		if (prevfd >= 0)
512			close(prevfd);
513		prevfd = pip[0];
514		close(pip[1]);
515	}
516	INTON;
517	if (n->npipe.backgnd == 0) {
518		INTOFF;
519		exitstatus = waitforjob(jp, (int *)NULL);
520		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
521		INTON;
522	}
523}
524
525
526
527/*
528 * Execute a command inside back quotes.  If it's a builtin command, we
529 * want to save its output in a block obtained from malloc.  Otherwise
530 * we fork off a subprocess and get the output of the command via a pipe.
531 * Should be called with interrupts off.
532 */
533
534void
535evalbackcmd(n, result)
536	union node *n;
537	struct backcmd *result;
538{
539	int pip[2];
540	struct job *jp;
541	struct stackmark smark;		/* unnecessary */
542
543	setstackmark(&smark);
544	result->fd = -1;
545	result->buf = NULL;
546	result->nleft = 0;
547	result->jp = NULL;
548	if (n == NULL) {
549		exitstatus = 0;
550		goto out;
551	}
552	if (n->type == NCMD) {
553		exitstatus = oexitstatus;
554		evalcommand(n, EV_BACKCMD, result);
555	} else {
556		exitstatus = 0;
557		if (pipe(pip) < 0)
558			error("Pipe call failed");
559		jp = makejob(n, 1);
560		if (forkshell(jp, n, FORK_NOJOB) == 0) {
561			FORCEINTON;
562			close(pip[0]);
563			if (pip[1] != 1) {
564				close(1);
565				copyfd(pip[1], 1);
566				close(pip[1]);
567			}
568			evaltree(n, EV_EXIT);
569		}
570		close(pip[1]);
571		result->fd = pip[0];
572		result->jp = jp;
573	}
574out:
575	popstackmark(&smark);
576	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
577		result->fd, result->buf, result->nleft, result->jp));
578}
579
580
581
582/*
583 * Execute a simple command.
584 */
585
586STATIC void
587evalcommand(cmd, flags, backcmd)
588	union node *cmd;
589	int flags;
590	struct backcmd *backcmd;
591{
592	struct stackmark smark;
593	union node *argp;
594	struct arglist arglist;
595	struct arglist varlist;
596	char **argv;
597	int argc;
598	char **envp;
599	int varflag;
600	struct strlist *sp;
601	int mode;
602	int pip[2];
603	struct cmdentry cmdentry;
604	struct job *jp;
605	struct jmploc jmploc;
606	struct jmploc *volatile savehandler;
607	char *volatile savecmdname;
608	volatile struct shparam saveparam;
609	struct localvar *volatile savelocalvars;
610	volatile int e;
611	char *lastarg;
612	int realstatus;
613#if __GNUC__
614	/* Avoid longjmp clobbering */
615	(void) &argv;
616	(void) &argc;
617	(void) &lastarg;
618	(void) &flags;
619#endif
620
621	/* First expand the arguments. */
622	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
623	setstackmark(&smark);
624	arglist.lastp = &arglist.list;
625	varlist.lastp = &varlist.list;
626	varflag = 1;
627	oexitstatus = exitstatus;
628	exitstatus = 0;
629	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
630		char *p = argp->narg.text;
631		if (varflag && is_name(*p)) {
632			do {
633				p++;
634			} while (is_in_name(*p));
635			if (*p == '=') {
636				expandarg(argp, &varlist, EXP_VARTILDE);
637				continue;
638			}
639		}
640		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
641		varflag = 0;
642	}
643	*arglist.lastp = NULL;
644	*varlist.lastp = NULL;
645	expredir(cmd->ncmd.redirect);
646	argc = 0;
647	for (sp = arglist.list ; sp ; sp = sp->next)
648		argc++;
649	argv = stalloc(sizeof (char *) * (argc + 1));
650
651	for (sp = arglist.list ; sp ; sp = sp->next) {
652		TRACE(("evalcommand arg: %s\n", sp->text));
653		*argv++ = sp->text;
654	}
655	*argv = NULL;
656	lastarg = NULL;
657	if (iflag && funcnest == 0 && argc > 0)
658		lastarg = argv[-1];
659	argv -= argc;
660
661	/* Print the command if xflag is set. */
662	if (xflag) {
663		outc('+', &errout);
664		for (sp = varlist.list ; sp ; sp = sp->next) {
665			outc(' ', &errout);
666			out2str(sp->text);
667		}
668		for (sp = arglist.list ; sp ; sp = sp->next) {
669			outc(' ', &errout);
670			out2str(sp->text);
671		}
672		outc('\n', &errout);
673		flushout(&errout);
674	}
675
676	/* Now locate the command. */
677	if (argc == 0) {
678		cmdentry.cmdtype = CMDBUILTIN;
679		cmdentry.u.index = BLTINCMD;
680	} else {
681		static const char PATH[] = "PATH=";
682		char *path = pathval();
683
684		/*
685		 * Modify the command lookup path, if a PATH= assignment
686		 * is present
687		 */
688		for (sp = varlist.list ; sp ; sp = sp->next)
689			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
690				path = sp->text + sizeof(PATH) - 1;
691
692		find_command(argv[0], &cmdentry, 1, path);
693		if (cmdentry.cmdtype == CMDUNKNOWN) {	/* command not found */
694			exitstatus = 127;
695			flushout(&errout);
696			return;
697		}
698		/* implement the bltin builtin here */
699		if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
700			for (;;) {
701				argv++;
702				if (--argc == 0)
703					break;
704				if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
705					outfmt(&errout, "%s: not found\n", *argv);
706					exitstatus = 127;
707					flushout(&errout);
708					return;
709				}
710				if (cmdentry.u.index != BLTINCMD)
711					break;
712			}
713		}
714	}
715
716	/* Fork off a child process if necessary. */
717	if (cmd->ncmd.backgnd
718	 || (cmdentry.cmdtype == CMDNORMAL
719	    && ((flags & EV_EXIT) == 0 || Tflag))
720	 || ((flags & EV_BACKCMD) != 0
721	    && (cmdentry.cmdtype != CMDBUILTIN
722		 || cmdentry.u.index == CDCMD
723		 || cmdentry.u.index == DOTCMD
724		 || cmdentry.u.index == EVALCMD))) {
725		jp = makejob(cmd, 1);
726		mode = cmd->ncmd.backgnd;
727		if (flags & EV_BACKCMD) {
728			mode = FORK_NOJOB;
729			if (pipe(pip) < 0)
730				error("Pipe call failed");
731		}
732		if (forkshell(jp, cmd, mode) != 0)
733			goto parent;	/* at end of routine */
734		if (flags & EV_BACKCMD) {
735			FORCEINTON;
736			close(pip[0]);
737			if (pip[1] != 1) {
738				close(1);
739				copyfd(pip[1], 1);
740				close(pip[1]);
741			}
742		}
743		flags |= EV_EXIT;
744	}
745
746	/* This is the child process if a fork occurred. */
747	/* Execute the command. */
748	if (cmdentry.cmdtype == CMDFUNCTION) {
749#ifdef DEBUG
750		trputs("Shell function:  ");  trargs(argv);
751#endif
752		redirect(cmd->ncmd.redirect, REDIR_PUSH);
753		saveparam = shellparam;
754		shellparam.malloc = 0;
755		shellparam.reset = 1;
756		shellparam.nparam = argc - 1;
757		shellparam.p = argv + 1;
758		shellparam.optnext = NULL;
759		INTOFF;
760		savelocalvars = localvars;
761		localvars = NULL;
762		INTON;
763		if (setjmp(jmploc.loc)) {
764			if (exception == EXSHELLPROC)
765				freeparam((struct shparam *)&saveparam);
766			else {
767				freeparam(&shellparam);
768				shellparam = saveparam;
769			}
770			poplocalvars();
771			localvars = savelocalvars;
772			handler = savehandler;
773			longjmp(handler->loc, 1);
774		}
775		savehandler = handler;
776		handler = &jmploc;
777		for (sp = varlist.list ; sp ; sp = sp->next)
778			mklocal(sp->text);
779		funcnest++;
780		if (flags & EV_TESTED)
781			evaltree(cmdentry.u.func, EV_TESTED);
782		else
783			evaltree(cmdentry.u.func, 0);
784		funcnest--;
785		INTOFF;
786		poplocalvars();
787		localvars = savelocalvars;
788		freeparam(&shellparam);
789		shellparam = saveparam;
790		handler = savehandler;
791		popredir();
792		INTON;
793		if (evalskip == SKIPFUNC) {
794			evalskip = 0;
795			skipcount = 0;
796		}
797		if (flags & EV_EXIT)
798			exitshell(exitstatus);
799	} else if (cmdentry.cmdtype == CMDBUILTIN) {
800#ifdef DEBUG
801		trputs("builtin command:  ");  trargs(argv);
802#endif
803		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
804		if (flags == EV_BACKCMD) {
805			memout.nleft = 0;
806			memout.nextc = memout.buf;
807			memout.bufsize = 64;
808			mode |= REDIR_BACKQ;
809		}
810		redirect(cmd->ncmd.redirect, mode);
811		savecmdname = commandname;
812		cmdenviron = varlist.list;
813		e = -1;
814		if (setjmp(jmploc.loc)) {
815			e = exception;
816			exitstatus = (e == EXINT)? SIGINT+128 : 2;
817			goto cmddone;
818		}
819		savehandler = handler;
820		handler = &jmploc;
821		commandname = argv[0];
822		argptr = argv + 1;
823		optptr = NULL;			/* initialize nextopt */
824		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
825		flushall();
826cmddone:
827		out1 = &output;
828		out2 = &errout;
829		freestdout();
830		if (e != EXSHELLPROC) {
831			commandname = savecmdname;
832			if (flags & EV_EXIT) {
833				exitshell(exitstatus);
834			}
835		}
836		handler = savehandler;
837		if (e != -1) {
838			if ((e != EXERROR && e != EXEXEC)
839			   || cmdentry.u.index == BLTINCMD
840			   || cmdentry.u.index == DOTCMD
841			   || cmdentry.u.index == EVALCMD
842#ifndef NO_HISTORY
843			   || cmdentry.u.index == HISTCMD
844#endif
845			   || cmdentry.u.index == EXECCMD)
846				exraise(e);
847			FORCEINTON;
848		}
849		if (cmdentry.u.index != EXECCMD)
850			popredir();
851		if (flags == EV_BACKCMD) {
852			backcmd->buf = memout.buf;
853			backcmd->nleft = memout.nextc - memout.buf;
854			memout.buf = NULL;
855		}
856	} else {
857#ifdef DEBUG
858		trputs("normal command:  ");  trargs(argv);
859#endif
860		clearredir();
861		redirect(cmd->ncmd.redirect, 0);
862		for (sp = varlist.list ; sp ; sp = sp->next)
863			setvareq(sp->text, VEXPORT|VSTACK);
864		envp = environment();
865		shellexec(argv, envp, pathval(), cmdentry.u.index);
866		/*NOTREACHED*/
867	}
868	goto out;
869
870parent:	/* parent process gets here (if we forked) */
871	if (mode == 0) {	/* argument to fork */
872		INTOFF;
873		exitstatus = waitforjob(jp, &realstatus);
874		INTON;
875		if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) {
876			evalskip = SKIPBREAK;
877			skipcount = loopnest;
878		}
879	} else if (mode == 2) {
880		backcmd->fd = pip[0];
881		close(pip[1]);
882		backcmd->jp = jp;
883	}
884
885out:
886	if (lastarg)
887		setvar("_", lastarg, 0);
888	popstackmark(&smark);
889}
890
891
892
893/*
894 * Search for a command.  This is called before we fork so that the
895 * location of the command will be available in the parent as well as
896 * the child.  The check for "goodname" is an overly conservative
897 * check that the name will not be subject to expansion.
898 */
899
900STATIC void
901prehash(n)
902	union node *n;
903{
904	struct cmdentry entry;
905
906	if (n->type == NCMD && n->ncmd.args)
907		if (goodname(n->ncmd.args->narg.text))
908			find_command(n->ncmd.args->narg.text, &entry, 0,
909				     pathval());
910}
911
912
913
914/*
915 * Builtin commands.  Builtin commands whose functions are closely
916 * tied to evaluation are implemented here.
917 */
918
919/*
920 * No command given, or a bltin command with no arguments.  Set the
921 * specified variables.
922 */
923
924int
925bltincmd(argc, argv)
926	int argc __unused;
927	char **argv __unused;
928{
929	listsetvar(cmdenviron);
930	/*
931	 * Preserve exitstatus of a previous possible redirection
932	 * as POSIX mandates
933	 */
934	return exitstatus;
935}
936
937
938/*
939 * Handle break and continue commands.  Break, continue, and return are
940 * all handled by setting the evalskip flag.  The evaluation routines
941 * above all check this flag, and if it is set they start skipping
942 * commands rather than executing them.  The variable skipcount is
943 * the number of loops to break/continue, or the number of function
944 * levels to return.  (The latter is always 1.)  It should probably
945 * be an error to break out of more loops than exist, but it isn't
946 * in the standard shell so we don't make it one here.
947 */
948
949int
950breakcmd(argc, argv)
951	int argc;
952	char **argv;
953{
954	int n = argc > 1 ? number(argv[1]) : 1;
955
956	if (n > loopnest)
957		n = loopnest;
958	if (n > 0) {
959		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
960		skipcount = n;
961	}
962	return 0;
963}
964
965
966/*
967 * The return command.
968 */
969
970int
971returncmd(argc, argv)
972	int argc;
973	char **argv;
974{
975	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
976
977	if (funcnest) {
978		evalskip = SKIPFUNC;
979		skipcount = 1;
980	} else {
981		/* skip the rest of the file */
982		evalskip = SKIPFILE;
983		skipcount = 1;
984	}
985	return ret;
986}
987
988
989int
990falsecmd(argc, argv)
991	int argc __unused;
992	char **argv __unused;
993{
994	return 1;
995}
996
997
998int
999truecmd(argc, argv)
1000	int argc __unused;
1001	char **argv __unused;
1002{
1003	return 0;
1004}
1005
1006
1007int
1008execcmd(argc, argv)
1009	int argc;
1010	char **argv;
1011{
1012	if (argc > 1) {
1013		struct strlist *sp;
1014
1015		iflag = 0;		/* exit on error */
1016		mflag = 0;
1017		optschanged();
1018		for (sp = cmdenviron; sp ; sp = sp->next)
1019			setvareq(sp->text, VEXPORT|VSTACK);
1020		shellexec(argv + 1, environment(), pathval(), 0);
1021
1022	}
1023	return 0;
1024}
1025