eval.c revision 1556
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
38static char sccsid[] = "@(#)eval.c	8.1 (Berkeley) 5/31/93";
39#endif /* not lint */
40
41/*
42 * Evaluate a command.
43 */
44
45#include "shell.h"
46#include "nodes.h"
47#include "syntax.h"
48#include "expand.h"
49#include "parser.h"
50#include "jobs.h"
51#include "eval.h"
52#include "builtins.h"
53#include "options.h"
54#include "exec.h"
55#include "redir.h"
56#include "input.h"
57#include "output.h"
58#include "trap.h"
59#include "var.h"
60#include "memalloc.h"
61#include "error.h"
62#include "mystring.h"
63#include "myhistedit.h"
64#include <signal.h>
65
66
67/* flags in argument to evaltree */
68#define EV_EXIT 01		/* exit after evaluating tree */
69#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
70#define EV_BACKCMD 04		/* command executing within back quotes */
71
72
73/* reasons for skipping commands (see comment on breakcmd routine) */
74#define SKIPBREAK 1
75#define SKIPCONT 2
76#define SKIPFUNC 3
77
78MKINIT int evalskip;		/* set if we are skipping commands */
79STATIC int skipcount;		/* number of levels to skip */
80MKINIT int loopnest;		/* current loop nesting level */
81int funcnest;			/* depth of function calls */
82
83
84char *commandname;
85struct strlist *cmdenviron;
86int exitstatus;			/* exit status of last command */
87
88
89#ifdef __STDC__
90STATIC void evalloop(union node *);
91STATIC void evalfor(union node *);
92STATIC void evalcase(union node *, int);
93STATIC void evalsubshell(union node *, int);
94STATIC void expredir(union node *);
95STATIC void evalpipe(union node *);
96STATIC void evalcommand(union node *, int, struct backcmd *);
97STATIC void prehash(union node *);
98#else
99STATIC void evalloop();
100STATIC void evalfor();
101STATIC void evalcase();
102STATIC void evalsubshell();
103STATIC void expredir();
104STATIC void evalpipe();
105STATIC void evalcommand();
106STATIC void prehash();
107#endif
108
109
110
111/*
112 * Called to reset things after an exception.
113 */
114
115#ifdef mkinit
116INCLUDE "eval.h"
117
118RESET {
119	evalskip = 0;
120	loopnest = 0;
121	funcnest = 0;
122}
123
124SHELLPROC {
125	exitstatus = 0;
126}
127#endif
128
129
130
131/*
132 * The eval commmand.
133 */
134
135evalcmd(argc, argv)
136	char **argv;
137{
138        char *p;
139        char *concat;
140        char **ap;
141
142        if (argc > 1) {
143                p = argv[1];
144                if (argc > 2) {
145                        STARTSTACKSTR(concat);
146                        ap = argv + 2;
147                        for (;;) {
148                                while (*p)
149                                        STPUTC(*p++, concat);
150                                if ((p = *ap++) == NULL)
151                                        break;
152                                STPUTC(' ', concat);
153                        }
154                        STPUTC('\0', concat);
155                        p = grabstackstr(concat);
156                }
157                evalstring(p);
158        }
159        return exitstatus;
160}
161
162
163/*
164 * Execute a command or commands contained in a string.
165 */
166
167void
168evalstring(s)
169	char *s;
170	{
171	union node *n;
172	struct stackmark smark;
173
174	setstackmark(&smark);
175	setinputstring(s, 1);
176	while ((n = parsecmd(0)) != NEOF) {
177		evaltree(n, 0);
178		popstackmark(&smark);
179	}
180	popfile();
181	popstackmark(&smark);
182}
183
184
185
186/*
187 * Evaluate a parse tree.  The value is left in the global variable
188 * exitstatus.
189 */
190
191void
192evaltree(n, flags)
193	union node *n;
194	{
195	if (n == NULL) {
196		TRACE(("evaltree(NULL) called\n"));
197		exitstatus = 0;
198		goto out;
199	}
200	displayhist = 1;	/* show history substitutions done with fc */
201	TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
202	switch (n->type) {
203	case NSEMI:
204		evaltree(n->nbinary.ch1, 0);
205		if (evalskip)
206			goto out;
207		evaltree(n->nbinary.ch2, flags);
208		break;
209	case NAND:
210		evaltree(n->nbinary.ch1, EV_TESTED);
211		if (evalskip || exitstatus != 0)
212			goto out;
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		int status = 0;
235
236		evaltree(n->nif.test, EV_TESTED);
237		if (evalskip)
238			goto out;
239		if (exitstatus == 0) {
240			evaltree(n->nif.ifpart, flags);
241			status = exitstatus;
242		} else if (n->nif.elsepart) {
243			evaltree(n->nif.elsepart, flags);
244			status = exitstatus;
245		}
246		exitstatus = status;
247		break;
248	}
249	case NWHILE:
250	case NUNTIL:
251		evalloop(n);
252		break;
253	case NFOR:
254		evalfor(n);
255		break;
256	case NCASE:
257		evalcase(n, flags);
258		break;
259	case NDEFUN:
260		defun(n->narg.text, n->narg.next);
261		exitstatus = 0;
262		break;
263	case NNOT:
264		evaltree(n->nnot.com, EV_TESTED);
265		exitstatus = !exitstatus;
266		break;
267
268	case NPIPE:
269		evalpipe(n);
270		break;
271	case NCMD:
272		evalcommand(n, flags, (struct backcmd *)NULL);
273		break;
274	default:
275		out1fmt("Node type = %d\n", n->type);
276		flushout(&output);
277		break;
278	}
279out:
280	if (pendingsigs)
281		dotrap();
282	if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
283		exitshell(exitstatus);
284}
285
286
287STATIC void
288evalloop(n)
289	union node *n;
290	{
291	int status;
292
293	loopnest++;
294	status = 0;
295	for (;;) {
296		evaltree(n->nbinary.ch1, EV_TESTED);
297		if (evalskip) {
298skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
299				evalskip = 0;
300				continue;
301			}
302			if (evalskip == SKIPBREAK && --skipcount <= 0)
303				evalskip = 0;
304			break;
305		}
306		if (n->type == NWHILE) {
307			if (exitstatus != 0)
308				break;
309		} else {
310			if (exitstatus == 0)
311				break;
312		}
313		evaltree(n->nbinary.ch2, 0);
314		status = exitstatus;
315		if (evalskip)
316			goto skipping;
317	}
318	loopnest--;
319	exitstatus = status;
320}
321
322
323
324STATIC void
325evalfor(n)
326	union node *n;
327	{
328	struct arglist arglist;
329	union node *argp;
330	struct strlist *sp;
331	struct stackmark smark;
332
333	setstackmark(&smark);
334	arglist.lastp = &arglist.list;
335	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
336		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
337		if (evalskip)
338			goto out;
339	}
340	*arglist.lastp = NULL;
341
342	exitstatus = 0;
343	loopnest++;
344	for (sp = arglist.list ; sp ; sp = sp->next) {
345		setvar(n->nfor.var, sp->text, 0);
346		evaltree(n->nfor.body, 0);
347		if (evalskip) {
348			if (evalskip == SKIPCONT && --skipcount <= 0) {
349				evalskip = 0;
350				continue;
351			}
352			if (evalskip == SKIPBREAK && --skipcount <= 0)
353				evalskip = 0;
354			break;
355		}
356	}
357	loopnest--;
358out:
359	popstackmark(&smark);
360}
361
362
363
364STATIC void
365evalcase(n, flags)
366	union node *n;
367	{
368	union node *cp;
369	union node *patp;
370	struct arglist arglist;
371	struct stackmark smark;
372
373	setstackmark(&smark);
374	arglist.lastp = &arglist.list;
375	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
376	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
377		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
378			if (casematch(patp, arglist.list->text)) {
379				if (evalskip == 0) {
380					evaltree(cp->nclist.body, flags);
381				}
382				goto out;
383			}
384		}
385	}
386out:
387	popstackmark(&smark);
388}
389
390
391
392/*
393 * Kick off a subshell to evaluate a tree.
394 */
395
396STATIC void
397evalsubshell(n, flags)
398	union node *n;
399	{
400	struct job *jp;
401	int backgnd = (n->type == NBACKGND);
402
403	expredir(n->nredir.redirect);
404	jp = makejob(n, 1);
405	if (forkshell(jp, n, backgnd) == 0) {
406		if (backgnd)
407			flags &=~ EV_TESTED;
408		redirect(n->nredir.redirect, 0);
409		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
410	}
411	if (! backgnd) {
412		INTOFF;
413		exitstatus = waitforjob(jp);
414		INTON;
415	}
416}
417
418
419
420/*
421 * Compute the names of the files in a redirection list.
422 */
423
424STATIC void
425expredir(n)
426	union node *n;
427	{
428	register union node *redir;
429
430	for (redir = n ; redir ; redir = redir->nfile.next) {
431		if (redir->type == NFROM
432		 || redir->type == NTO
433		 || redir->type == NAPPEND) {
434			struct arglist fn;
435			fn.lastp = &fn.list;
436			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
437			redir->nfile.expfname = fn.list->text;
438		}
439	}
440}
441
442
443
444/*
445 * Evaluate a pipeline.  All the processes in the pipeline are children
446 * of the process creating the pipeline.  (This differs from some versions
447 * of the shell, which make the last process in a pipeline the parent
448 * of all the rest.)
449 */
450
451STATIC void
452evalpipe(n)
453	union node *n;
454	{
455	struct job *jp;
456	struct nodelist *lp;
457	int pipelen;
458	int prevfd;
459	int pip[2];
460
461	TRACE(("evalpipe(0x%x) called\n", (int)n));
462	pipelen = 0;
463	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
464		pipelen++;
465	INTOFF;
466	jp = makejob(n, pipelen);
467	prevfd = -1;
468	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
469		prehash(lp->n);
470		pip[1] = -1;
471		if (lp->next) {
472			if (pipe(pip) < 0) {
473				close(prevfd);
474				error("Pipe call failed");
475			}
476		}
477		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
478			INTON;
479			if (prevfd > 0) {
480				close(0);
481				copyfd(prevfd, 0);
482				close(prevfd);
483			}
484			if (pip[1] >= 0) {
485				close(pip[0]);
486				if (pip[1] != 1) {
487					close(1);
488					copyfd(pip[1], 1);
489					close(pip[1]);
490				}
491			}
492			evaltree(lp->n, EV_EXIT);
493		}
494		if (prevfd >= 0)
495			close(prevfd);
496		prevfd = pip[0];
497		close(pip[1]);
498	}
499	INTON;
500	if (n->npipe.backgnd == 0) {
501		INTOFF;
502		exitstatus = waitforjob(jp);
503		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
504		INTON;
505	}
506}
507
508
509
510/*
511 * Execute a command inside back quotes.  If it's a builtin command, we
512 * want to save its output in a block obtained from malloc.  Otherwise
513 * we fork off a subprocess and get the output of the command via a pipe.
514 * Should be called with interrupts off.
515 */
516
517void
518evalbackcmd(n, result)
519	union node *n;
520	struct backcmd *result;
521	{
522	int pip[2];
523	struct job *jp;
524	struct stackmark smark;		/* unnecessary */
525
526	setstackmark(&smark);
527	result->fd = -1;
528	result->buf = NULL;
529	result->nleft = 0;
530	result->jp = NULL;
531	exitstatus = 0;
532	if (n == NULL)
533		goto out;
534	if (n->type == NCMD) {
535		evalcommand(n, EV_BACKCMD, result);
536	} else {
537		if (pipe(pip) < 0)
538			error("Pipe call failed");
539		jp = makejob(n, 1);
540		if (forkshell(jp, n, FORK_NOJOB) == 0) {
541			FORCEINTON;
542			close(pip[0]);
543			if (pip[1] != 1) {
544				close(1);
545				copyfd(pip[1], 1);
546				close(pip[1]);
547			}
548			evaltree(n, EV_EXIT);
549		}
550		close(pip[1]);
551		result->fd = pip[0];
552		result->jp = jp;
553	}
554out:
555	popstackmark(&smark);
556	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
557		result->fd, result->buf, result->nleft, result->jp));
558}
559
560
561
562/*
563 * Execute a simple command.
564 */
565
566STATIC void
567evalcommand(cmd, flags, backcmd)
568	union node *cmd;
569	struct backcmd *backcmd;
570	{
571	struct stackmark smark;
572	union node *argp;
573	struct arglist arglist;
574	struct arglist varlist;
575	char **argv;
576	int argc;
577	char **envp;
578	int varflag;
579	struct strlist *sp;
580	register char *p;
581	int mode;
582	int pip[2];
583	struct cmdentry cmdentry;
584	struct job *jp;
585	struct jmploc jmploc;
586	struct jmploc *volatile savehandler;
587	char *volatile savecmdname;
588	volatile struct shparam saveparam;
589	struct localvar *volatile savelocalvars;
590	volatile int e;
591	char *lastarg;
592
593	/* First expand the arguments. */
594	TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
595	setstackmark(&smark);
596	arglist.lastp = &arglist.list;
597	varlist.lastp = &varlist.list;
598	varflag = 1;
599	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
600		p = argp->narg.text;
601		if (varflag && is_name(*p)) {
602			do {
603				p++;
604			} while (is_in_name(*p));
605			if (*p == '=') {
606				expandarg(argp, &varlist, EXP_VARTILDE);
607				continue;
608			}
609		}
610		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
611		varflag = 0;
612	}
613	*arglist.lastp = NULL;
614	*varlist.lastp = NULL;
615	expredir(cmd->ncmd.redirect);
616	argc = 0;
617	for (sp = arglist.list ; sp ; sp = sp->next)
618		argc++;
619	argv = stalloc(sizeof (char *) * (argc + 1));
620
621	for (sp = arglist.list ; sp ; sp = sp->next) {
622		TRACE(("evalcommand arg: %s\n", sp->text));
623		*argv++ = sp->text;
624	}
625	*argv = NULL;
626	lastarg = NULL;
627	if (iflag && funcnest == 0 && argc > 0)
628		lastarg = argv[-1];
629	argv -= argc;
630
631	/* Print the command if xflag is set. */
632	if (xflag) {
633		outc('+', &errout);
634		for (sp = varlist.list ; sp ; sp = sp->next) {
635			outc(' ', &errout);
636			out2str(sp->text);
637		}
638		for (sp = arglist.list ; sp ; sp = sp->next) {
639			outc(' ', &errout);
640			out2str(sp->text);
641		}
642		outc('\n', &errout);
643		flushout(&errout);
644	}
645
646	/* Now locate the command. */
647	if (argc == 0) {
648		cmdentry.cmdtype = CMDBUILTIN;
649		cmdentry.u.index = BLTINCMD;
650	} else {
651		find_command(argv[0], &cmdentry, 1);
652		if (cmdentry.cmdtype == CMDUNKNOWN) {	/* command not found */
653			exitstatus = 2;
654			flushout(&errout);
655			return;
656		}
657		/* implement the bltin builtin here */
658		if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
659			for (;;) {
660				argv++;
661				if (--argc == 0)
662					break;
663				if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
664					outfmt(&errout, "%s: not found\n", *argv);
665					exitstatus = 2;
666					flushout(&errout);
667					return;
668				}
669				if (cmdentry.u.index != BLTINCMD)
670					break;
671			}
672		}
673	}
674
675	/* Fork off a child process if necessary. */
676	if (cmd->ncmd.backgnd
677	 || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
678	 || (flags & EV_BACKCMD) != 0
679	    && (cmdentry.cmdtype != CMDBUILTIN
680		 || cmdentry.u.index == DOTCMD
681		 || cmdentry.u.index == EVALCMD)) {
682		jp = makejob(cmd, 1);
683		mode = cmd->ncmd.backgnd;
684		if (flags & EV_BACKCMD) {
685			mode = FORK_NOJOB;
686			if (pipe(pip) < 0)
687				error("Pipe call failed");
688		}
689		if (forkshell(jp, cmd, mode) != 0)
690			goto parent;	/* at end of routine */
691		if (flags & EV_BACKCMD) {
692			FORCEINTON;
693			close(pip[0]);
694			if (pip[1] != 1) {
695				close(1);
696				copyfd(pip[1], 1);
697				close(pip[1]);
698			}
699		}
700		flags |= EV_EXIT;
701	}
702
703	/* This is the child process if a fork occurred. */
704	/* Execute the command. */
705	if (cmdentry.cmdtype == CMDFUNCTION) {
706		trputs("Shell function:  ");  trargs(argv);
707		redirect(cmd->ncmd.redirect, REDIR_PUSH);
708		saveparam = shellparam;
709		shellparam.malloc = 0;
710		shellparam.nparam = argc - 1;
711		shellparam.p = argv + 1;
712		shellparam.optnext = NULL;
713		INTOFF;
714		savelocalvars = localvars;
715		localvars = NULL;
716		INTON;
717		if (setjmp(jmploc.loc)) {
718			if (exception == EXSHELLPROC)
719				freeparam((struct shparam *)&saveparam);
720			else {
721				freeparam(&shellparam);
722				shellparam = saveparam;
723			}
724			poplocalvars();
725			localvars = savelocalvars;
726			handler = savehandler;
727			longjmp(handler->loc, 1);
728		}
729		savehandler = handler;
730		handler = &jmploc;
731		for (sp = varlist.list ; sp ; sp = sp->next)
732			mklocal(sp->text);
733		funcnest++;
734		evaltree(cmdentry.u.func, 0);
735		funcnest--;
736		INTOFF;
737		poplocalvars();
738		localvars = savelocalvars;
739		freeparam(&shellparam);
740		shellparam = saveparam;
741		handler = savehandler;
742		popredir();
743		INTON;
744		if (evalskip == SKIPFUNC) {
745			evalskip = 0;
746			skipcount = 0;
747		}
748		if (flags & EV_EXIT)
749			exitshell(exitstatus);
750	} else if (cmdentry.cmdtype == CMDBUILTIN) {
751		trputs("builtin command:  ");  trargs(argv);
752		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
753		if (flags == EV_BACKCMD) {
754			memout.nleft = 0;
755			memout.nextc = memout.buf;
756			memout.bufsize = 64;
757			mode |= REDIR_BACKQ;
758		}
759		redirect(cmd->ncmd.redirect, mode);
760		savecmdname = commandname;
761		cmdenviron = varlist.list;
762		e = -1;
763		if (setjmp(jmploc.loc)) {
764			e = exception;
765			exitstatus = (e == EXINT)? SIGINT+128 : 2;
766			goto cmddone;
767		}
768		savehandler = handler;
769		handler = &jmploc;
770		commandname = argv[0];
771		argptr = argv + 1;
772		optptr = NULL;			/* initialize nextopt */
773		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
774		flushall();
775cmddone:
776		out1 = &output;
777		out2 = &errout;
778		freestdout();
779		if (e != EXSHELLPROC) {
780			commandname = savecmdname;
781			if (flags & EV_EXIT) {
782				exitshell(exitstatus);
783			}
784		}
785		handler = savehandler;
786		if (e != -1) {
787			if (e != EXERROR || cmdentry.u.index == BLTINCMD
788					       || cmdentry.u.index == DOTCMD
789					       || cmdentry.u.index == EVALCMD
790					       || cmdentry.u.index == HISTCMD
791					       || cmdentry.u.index == EXECCMD)
792				exraise(e);
793			FORCEINTON;
794		}
795		if (cmdentry.u.index != EXECCMD)
796			popredir();
797		if (flags == EV_BACKCMD) {
798			backcmd->buf = memout.buf;
799			backcmd->nleft = memout.nextc - memout.buf;
800			memout.buf = NULL;
801		}
802	} else {
803		trputs("normal command:  ");  trargs(argv);
804		clearredir();
805		redirect(cmd->ncmd.redirect, 0);
806		if (varlist.list) {
807			p = stalloc(strlen(pathval()) + 1);
808			scopy(pathval(), p);
809		} else {
810			p = pathval();
811		}
812		for (sp = varlist.list ; sp ; sp = sp->next)
813			setvareq(sp->text, VEXPORT|VSTACK);
814		envp = environment();
815		shellexec(argv, envp, p, cmdentry.u.index);
816		/*NOTREACHED*/
817	}
818	goto out;
819
820parent:	/* parent process gets here (if we forked) */
821	if (mode == 0) {	/* argument to fork */
822		INTOFF;
823		exitstatus = waitforjob(jp);
824		INTON;
825	} else if (mode == 2) {
826		backcmd->fd = pip[0];
827		close(pip[1]);
828		backcmd->jp = jp;
829	}
830
831out:
832	if (lastarg)
833		setvar("_", lastarg, 0);
834	popstackmark(&smark);
835}
836
837
838
839/*
840 * Search for a command.  This is called before we fork so that the
841 * location of the command will be available in the parent as well as
842 * the child.  The check for "goodname" is an overly conservative
843 * check that the name will not be subject to expansion.
844 */
845
846STATIC void
847prehash(n)
848	union node *n;
849	{
850	struct cmdentry entry;
851
852	if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
853		find_command(n->ncmd.args->narg.text, &entry, 0);
854}
855
856
857
858/*
859 * Builtin commands.  Builtin commands whose functions are closely
860 * tied to evaluation are implemented here.
861 */
862
863/*
864 * No command given, or a bltin command with no arguments.  Set the
865 * specified variables.
866 */
867
868bltincmd(argc, argv)  char **argv; {
869	listsetvar(cmdenviron);
870	return exitstatus;
871}
872
873
874/*
875 * Handle break and continue commands.  Break, continue, and return are
876 * all handled by setting the evalskip flag.  The evaluation routines
877 * above all check this flag, and if it is set they start skipping
878 * commands rather than executing them.  The variable skipcount is
879 * the number of loops to break/continue, or the number of function
880 * levels to return.  (The latter is always 1.)  It should probably
881 * be an error to break out of more loops than exist, but it isn't
882 * in the standard shell so we don't make it one here.
883 */
884
885breakcmd(argc, argv)  char **argv; {
886	int n;
887
888	n = 1;
889	if (argc > 1)
890		n = number(argv[1]);
891	if (n > loopnest)
892		n = loopnest;
893	if (n > 0) {
894		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
895		skipcount = n;
896	}
897	return 0;
898}
899
900
901/*
902 * The return command.
903 */
904
905returncmd(argc, argv)  char **argv; {
906	int ret;
907
908	ret = exitstatus;
909	if (argc > 1)
910		ret = number(argv[1]);
911	if (funcnest) {
912		evalskip = SKIPFUNC;
913		skipcount = 1;
914	}
915	return ret;
916}
917
918
919truecmd(argc, argv)  char **argv; {
920	return 0;
921}
922
923
924execcmd(argc, argv)  char **argv; {
925	if (argc > 1) {
926		iflag = 0;		/* exit on error */
927		mflag = 0;
928		optschanged();
929		shellexec(argv + 1, environment(), pathval(), 0);
930
931	}
932	return 0;
933}
934