xref: /netbsd-src/bin/sh/eval.c (revision da5f4674a3fc214be3572d358b66af40ab9401e7)
1 /*	$NetBSD: eval.c,v 1.74 2003/08/07 09:05:30 agc Exp $	*/
2 
3 /*-
4  * Copyright (c) 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
39 #else
40 __RCSID("$NetBSD: eval.c,v 1.74 2003/08/07 09:05:30 agc Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <stdlib.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <sys/times.h>
49 #include <sys/param.h>
50 #include <sys/types.h>
51 #include <sys/wait.h>
52 #include <sys/sysctl.h>
53 
54 /*
55  * Evaluate a command.
56  */
57 
58 #include "shell.h"
59 #include "nodes.h"
60 #include "syntax.h"
61 #include "expand.h"
62 #include "parser.h"
63 #include "jobs.h"
64 #include "eval.h"
65 #include "builtins.h"
66 #include "options.h"
67 #include "exec.h"
68 #include "redir.h"
69 #include "input.h"
70 #include "output.h"
71 #include "trap.h"
72 #include "var.h"
73 #include "memalloc.h"
74 #include "error.h"
75 #include "show.h"
76 #include "mystring.h"
77 #include "main.h"
78 #ifndef SMALL
79 #include "myhistedit.h"
80 #endif
81 
82 
83 /* flags in argument to evaltree */
84 #define EV_EXIT 01		/* exit after evaluating tree */
85 #define EV_TESTED 02		/* exit status is checked; ignore -e flag */
86 #define EV_BACKCMD 04		/* command executing within back quotes */
87 
88 int evalskip;			/* set if we are skipping commands */
89 STATIC int skipcount;		/* number of levels to skip */
90 MKINIT int loopnest;		/* current loop nesting level */
91 int funcnest;			/* depth of function calls */
92 
93 
94 char *commandname;
95 struct strlist *cmdenviron;
96 int exitstatus;			/* exit status of last command */
97 int back_exitstatus;		/* exit status of backquoted command */
98 
99 
100 STATIC void evalloop(union node *, int);
101 STATIC void evalfor(union node *, int);
102 STATIC void evalcase(union node *, int);
103 STATIC void evalsubshell(union node *, int);
104 STATIC void expredir(union node *);
105 STATIC void evalpipe(union node *);
106 STATIC void evalcommand(union node *, int, struct backcmd *);
107 STATIC void prehash(union node *);
108 
109 
110 /*
111  * Called to reset things after an exception.
112  */
113 
114 #ifdef mkinit
115 INCLUDE "eval.h"
116 
117 RESET {
118 	evalskip = 0;
119 	loopnest = 0;
120 	funcnest = 0;
121 }
122 
123 SHELLPROC {
124 	exitstatus = 0;
125 }
126 #endif
127 
128 
129 
130 /*
131  * The eval commmand.
132  */
133 
134 int
135 evalcmd(int argc, char **argv)
136 {
137         char *p;
138         char *concat;
139         char **ap;
140 
141         if (argc > 1) {
142                 p = argv[1];
143                 if (argc > 2) {
144                         STARTSTACKSTR(concat);
145                         ap = argv + 2;
146                         for (;;) {
147                                 while (*p)
148                                         STPUTC(*p++, concat);
149                                 if ((p = *ap++) == NULL)
150                                         break;
151                                 STPUTC(' ', concat);
152                         }
153                         STPUTC('\0', concat);
154                         p = grabstackstr(concat);
155                 }
156                 evalstring(p, EV_TESTED);
157         }
158         return exitstatus;
159 }
160 
161 
162 /*
163  * Execute a command or commands contained in a string.
164  */
165 
166 void
167 evalstring(char *s, int flag)
168 {
169 	union node *n;
170 	struct stackmark smark;
171 
172 	setstackmark(&smark);
173 	setinputstring(s, 1);
174 
175 	while ((n = parsecmd(0)) != NEOF) {
176 		evaltree(n, flag);
177 		popstackmark(&smark);
178 	}
179 	popfile();
180 	popstackmark(&smark);
181 }
182 
183 
184 
185 /*
186  * Evaluate a parse tree.  The value is left in the global variable
187  * exitstatus.
188  */
189 
190 void
191 evaltree(union node *n, int flags)
192 {
193 	if (n == NULL) {
194 		TRACE(("evaltree(NULL) called\n"));
195 		exitstatus = 0;
196 		goto out;
197 	}
198 #ifndef SMALL
199 	displayhist = 1;	/* show history substitutions done with fc */
200 #endif
201 	TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
202 	    getpid(), n, n->type, flags));
203 	switch (n->type) {
204 	case NSEMI:
205 		evaltree(n->nbinary.ch1, flags & EV_TESTED);
206 		if (evalskip)
207 			goto out;
208 		evaltree(n->nbinary.ch2, flags);
209 		break;
210 	case NAND:
211 		evaltree(n->nbinary.ch1, EV_TESTED);
212 		if (evalskip || exitstatus != 0)
213 			goto out;
214 		evaltree(n->nbinary.ch2, flags | EV_TESTED);
215 		break;
216 	case NOR:
217 		evaltree(n->nbinary.ch1, EV_TESTED);
218 		if (evalskip || exitstatus == 0)
219 			goto out;
220 		evaltree(n->nbinary.ch2, flags | EV_TESTED);
221 		break;
222 	case NREDIR:
223 		expredir(n->nredir.redirect);
224 		redirect(n->nredir.redirect, REDIR_PUSH);
225 		evaltree(n->nredir.n, flags);
226 		popredir();
227 		break;
228 	case NSUBSHELL:
229 		evalsubshell(n, flags);
230 		break;
231 	case NBACKGND:
232 		evalsubshell(n, flags);
233 		break;
234 	case NIF: {
235 		evaltree(n->nif.test, EV_TESTED);
236 		if (evalskip)
237 			goto out;
238 		if (exitstatus == 0)
239 			evaltree(n->nif.ifpart, flags);
240 		else if (n->nif.elsepart)
241 			evaltree(n->nif.elsepart, flags);
242 		else
243 			exitstatus = 0;
244 		break;
245 	}
246 	case NWHILE:
247 	case NUNTIL:
248 		evalloop(n, flags);
249 		break;
250 	case NFOR:
251 		evalfor(n, flags);
252 		break;
253 	case NCASE:
254 		evalcase(n, flags);
255 		break;
256 	case NDEFUN:
257 		defun(n->narg.text, n->narg.next);
258 		exitstatus = 0;
259 		break;
260 	case NNOT:
261 		evaltree(n->nnot.com, EV_TESTED);
262 		exitstatus = !exitstatus;
263 		break;
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 	}
275 out:
276 	if (pendingsigs)
277 		dotrap();
278 	if ((flags & EV_EXIT) != 0)
279 		exitshell(exitstatus);
280 }
281 
282 
283 STATIC void
284 evalloop(union node *n, int flags)
285 {
286 	int status;
287 
288 	loopnest++;
289 	status = 0;
290 	for (;;) {
291 		evaltree(n->nbinary.ch1, EV_TESTED);
292 		if (evalskip) {
293 skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
294 				evalskip = 0;
295 				continue;
296 			}
297 			if (evalskip == SKIPBREAK && --skipcount <= 0)
298 				evalskip = 0;
299 			break;
300 		}
301 		if (n->type == NWHILE) {
302 			if (exitstatus != 0)
303 				break;
304 		} else {
305 			if (exitstatus == 0)
306 				break;
307 		}
308 		evaltree(n->nbinary.ch2, flags & EV_TESTED);
309 		status = exitstatus;
310 		if (evalskip)
311 			goto skipping;
312 	}
313 	loopnest--;
314 	exitstatus = status;
315 }
316 
317 
318 
319 STATIC void
320 evalfor(union node *n, int flags)
321 {
322 	struct arglist arglist;
323 	union node *argp;
324 	struct strlist *sp;
325 	struct stackmark smark;
326 	int status = 0;
327 
328 	setstackmark(&smark);
329 	arglist.lastp = &arglist.list;
330 	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
331 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
332 		if (evalskip)
333 			goto out;
334 	}
335 	*arglist.lastp = NULL;
336 
337 	loopnest++;
338 	for (sp = arglist.list ; sp ; sp = sp->next) {
339 		setvar(n->nfor.var, sp->text, 0);
340 		evaltree(n->nfor.body, flags & EV_TESTED);
341 		status = exitstatus;
342 		if (evalskip) {
343 			if (evalskip == SKIPCONT && --skipcount <= 0) {
344 				evalskip = 0;
345 				continue;
346 			}
347 			if (evalskip == SKIPBREAK && --skipcount <= 0)
348 				evalskip = 0;
349 			break;
350 		}
351 	}
352 	loopnest--;
353 	exitstatus = status;
354 out:
355 	popstackmark(&smark);
356 }
357 
358 
359 
360 STATIC void
361 evalcase(union node *n, int flags)
362 {
363 	union node *cp;
364 	union node *patp;
365 	struct arglist arglist;
366 	struct stackmark smark;
367 	int status = 0;
368 
369 	setstackmark(&smark);
370 	arglist.lastp = &arglist.list;
371 	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
372 	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
373 		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
374 			if (casematch(patp, arglist.list->text)) {
375 				if (evalskip == 0) {
376 					evaltree(cp->nclist.body, flags);
377 					status = exitstatus;
378 				}
379 				goto out;
380 			}
381 		}
382 	}
383 out:
384 	exitstatus = status;
385 	popstackmark(&smark);
386 }
387 
388 
389 
390 /*
391  * Kick off a subshell to evaluate a tree.
392  */
393 
394 STATIC void
395 evalsubshell(union node *n, int flags)
396 {
397 	struct job *jp;
398 	int backgnd = (n->type == NBACKGND);
399 
400 	expredir(n->nredir.redirect);
401 	INTOFF;
402 	jp = makejob(n, 1);
403 	if (forkshell(jp, n, backgnd) == 0) {
404 		INTON;
405 		if (backgnd)
406 			flags &=~ EV_TESTED;
407 		redirect(n->nredir.redirect, 0);
408 		/* never returns */
409 		evaltree(n->nredir.n, flags | EV_EXIT);
410 	}
411 	if (! backgnd)
412 		exitstatus = waitforjob(jp);
413 	INTON;
414 }
415 
416 
417 
418 /*
419  * Compute the names of the files in a redirection list.
420  */
421 
422 STATIC void
423 expredir(union node *n)
424 {
425 	union node *redir;
426 
427 	for (redir = n ; redir ; redir = redir->nfile.next) {
428 		struct arglist fn;
429 		fn.lastp = &fn.list;
430 		switch (redir->type) {
431 		case NFROMTO:
432 		case NFROM:
433 		case NTO:
434 		case NCLOBBER:
435 		case NAPPEND:
436 			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
437 			redir->nfile.expfname = fn.list->text;
438 			break;
439 		case NFROMFD:
440 		case NTOFD:
441 			if (redir->ndup.vname) {
442 				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
443 				fixredir(redir, fn.list->text, 1);
444 			}
445 			break;
446 		}
447 	}
448 }
449 
450 
451 
452 /*
453  * Evaluate a pipeline.  All the processes in the pipeline are children
454  * of the process creating the pipeline.  (This differs from some versions
455  * of the shell, which make the last process in a pipeline the parent
456  * of all the rest.)
457  */
458 
459 STATIC void
460 evalpipe(union node *n)
461 {
462 	struct job *jp;
463 	struct nodelist *lp;
464 	int pipelen;
465 	int prevfd;
466 	int pip[2];
467 
468 	TRACE(("evalpipe(0x%lx) called\n", (long)n));
469 	pipelen = 0;
470 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
471 		pipelen++;
472 	INTOFF;
473 	jp = makejob(n, pipelen);
474 	prevfd = -1;
475 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
476 		prehash(lp->n);
477 		pip[1] = -1;
478 		if (lp->next) {
479 			if (pipe(pip) < 0) {
480 				close(prevfd);
481 				error("Pipe call failed");
482 			}
483 		}
484 		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
485 			INTON;
486 			if (prevfd > 0) {
487 				close(0);
488 				copyfd(prevfd, 0);
489 				close(prevfd);
490 			}
491 			if (pip[1] >= 0) {
492 				close(pip[0]);
493 				if (pip[1] != 1) {
494 					close(1);
495 					copyfd(pip[1], 1);
496 					close(pip[1]);
497 				}
498 			}
499 			evaltree(lp->n, EV_EXIT);
500 		}
501 		if (prevfd >= 0)
502 			close(prevfd);
503 		prevfd = pip[0];
504 		close(pip[1]);
505 	}
506 	if (n->npipe.backgnd == 0) {
507 		exitstatus = waitforjob(jp);
508 		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
509 	}
510 	INTON;
511 }
512 
513 
514 
515 /*
516  * Execute a command inside back quotes.  If it's a builtin command, we
517  * want to save its output in a block obtained from malloc.  Otherwise
518  * we fork off a subprocess and get the output of the command via a pipe.
519  * Should be called with interrupts off.
520  */
521 
522 void
523 evalbackcmd(union node *n, struct backcmd *result)
524 {
525 	int pip[2];
526 	struct job *jp;
527 	struct stackmark smark;		/* unnecessary */
528 
529 	setstackmark(&smark);
530 	result->fd = -1;
531 	result->buf = NULL;
532 	result->nleft = 0;
533 	result->jp = NULL;
534 	if (n == NULL) {
535 		goto out;
536 	}
537 #ifdef notyet
538 	/*
539 	 * For now we disable executing builtins in the same
540 	 * context as the shell, because we are not keeping
541 	 * enough state to recover from changes that are
542 	 * supposed only to affect subshells. eg. echo "`cd /`"
543 	 */
544 	if (n->type == NCMD) {
545 		exitstatus = oexitstatus;
546 		evalcommand(n, EV_BACKCMD, result);
547 	} else
548 #endif
549 	{
550 		INTOFF;
551 		if (pipe(pip) < 0)
552 			error("Pipe call failed");
553 		jp = makejob(n, 1);
554 		if (forkshell(jp, n, FORK_NOJOB) == 0) {
555 			FORCEINTON;
556 			close(pip[0]);
557 			if (pip[1] != 1) {
558 				close(1);
559 				copyfd(pip[1], 1);
560 				close(pip[1]);
561 			}
562 			eflag = 0;
563 			evaltree(n, EV_EXIT);
564 			/* NOTREACHED */
565 		}
566 		close(pip[1]);
567 		result->fd = pip[0];
568 		result->jp = jp;
569 		INTON;
570 	}
571 out:
572 	popstackmark(&smark);
573 	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
574 		result->fd, result->buf, result->nleft, result->jp));
575 }
576 
577 static const char *
578 syspath(void)
579 {
580 	static char *sys_path = NULL;
581 	static int mib[] = {CTL_USER, USER_CS_PATH};
582 	size_t len;
583 
584 	if (sys_path == NULL) {
585 		if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
586 		    (sys_path = ckmalloc(len + 5)) != NULL &&
587 		    sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
588 			memcpy(sys_path, "PATH=", 5);
589 		} else {
590 			ckfree(sys_path);
591 			/* something to keep things happy */
592 			sys_path = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
593 		}
594 	}
595 	return sys_path;
596 }
597 
598 static int
599 parse_command_args(int argc, char **argv, int *use_syspath)
600 {
601 	int sv_argc = argc;
602 	char *cp, c;
603 
604 	*use_syspath = 0;
605 
606 	for (;;) {
607 		argv++;
608 		if (--argc == 0)
609 			break;
610 		cp = *argv;
611 		if (*cp++ != '-')
612 			break;
613 		if (*cp == '-' && cp[1] == 0) {
614 			argv++;
615 			argc--;
616 			break;
617 		}
618 		while ((c = *cp++)) {
619 			switch (c) {
620 			case 'p':
621 				*use_syspath = 1;
622 				break;
623 			default:
624 				/* run 'typecmd' for other options */
625 				return 0;
626 			}
627 		}
628 	}
629 	return sv_argc - argc;
630 }
631 
632 int vforked = 0;
633 
634 /*
635  * Execute a simple command.
636  */
637 
638 STATIC void
639 evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
640 {
641 	struct stackmark smark;
642 	union node *argp;
643 	struct arglist arglist;
644 	struct arglist varlist;
645 	char **argv;
646 	int argc;
647 	char **envp;
648 	int varflag;
649 	struct strlist *sp;
650 	int mode;
651 	int pip[2];
652 	struct cmdentry cmdentry;
653 	struct job *jp;
654 	struct jmploc jmploc;
655 	struct jmploc *volatile savehandler;
656 	char *volatile savecmdname;
657 	volatile struct shparam saveparam;
658 	struct localvar *volatile savelocalvars;
659 	volatile int e;
660 	char *lastarg;
661 	const char *path = pathval();
662 	int temp_path;
663 #if __GNUC__
664 	/* Avoid longjmp clobbering */
665 	(void) &argv;
666 	(void) &argc;
667 	(void) &lastarg;
668 	(void) &flags;
669 #endif
670 
671 	vforked = 0;
672 	/* First expand the arguments. */
673 	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
674 	setstackmark(&smark);
675 	back_exitstatus = 0;
676 
677 	arglist.lastp = &arglist.list;
678 	varflag = 1;
679 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
680 		char *p = argp->narg.text;
681 		if (varflag && is_name(*p)) {
682 			do {
683 				p++;
684 			} while (is_in_name(*p));
685 			if (*p == '=')
686 				continue;
687 		}
688 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
689 		varflag = 0;
690 	}
691 	*arglist.lastp = NULL;
692 
693 	expredir(cmd->ncmd.redirect);
694 
695 	varlist.lastp = &varlist.list;
696 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
697 		char *p = argp->narg.text;
698 		if (!is_name(*p))
699 			break;
700 		do
701 			p++;
702 		while (is_in_name(*p));
703 		if (*p != '=')
704 			break;
705 		expandarg(argp, &varlist, EXP_VARTILDE);
706 	}
707 	*varlist.lastp = NULL;
708 
709 	argc = 0;
710 	for (sp = arglist.list ; sp ; sp = sp->next)
711 		argc++;
712 	argv = stalloc(sizeof (char *) * (argc + 1));
713 
714 	for (sp = arglist.list ; sp ; sp = sp->next) {
715 		TRACE(("evalcommand arg: %s\n", sp->text));
716 		*argv++ = sp->text;
717 	}
718 	*argv = NULL;
719 	lastarg = NULL;
720 	if (iflag && funcnest == 0 && argc > 0)
721 		lastarg = argv[-1];
722 	argv -= argc;
723 
724 	/* Print the command if xflag is set. */
725 	if (xflag) {
726 		char sep = 0;
727 		out2str(ps4val());
728 		for (sp = varlist.list ; sp ; sp = sp->next) {
729 			if (sep != 0)
730 				outc(sep, &errout);
731 			out2str(sp->text);
732 			sep = ' ';
733 		}
734 		for (sp = arglist.list ; sp ; sp = sp->next) {
735 			if (sep != 0)
736 				outc(sep, &errout);
737 			out2str(sp->text);
738 			sep = ' ';
739 		}
740 		outc('\n', &errout);
741 		flushout(&errout);
742 	}
743 
744 	/* Now locate the command. */
745 	if (argc == 0) {
746 		cmdentry.cmdtype = CMDSPLBLTIN;
747 		cmdentry.u.bltin = bltincmd;
748 	} else {
749 		static const char PATH[] = "PATH=";
750 		int cmd_flags = DO_ERR;
751 
752 		/*
753 		 * Modify the command lookup path, if a PATH= assignment
754 		 * is present
755 		 */
756 		for (sp = varlist.list; sp; sp = sp->next)
757 			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
758 				path = sp->text + sizeof(PATH) - 1;
759 
760 		do {
761 			int argsused, use_syspath;
762 			find_command(argv[0], &cmdentry, cmd_flags, path);
763 			if (cmdentry.cmdtype == CMDUNKNOWN) {
764 				exitstatus = 127;
765 				flushout(&errout);
766 				goto out;
767 			}
768 
769 			/* implement the 'command' builtin here */
770 			if (cmdentry.cmdtype != CMDBUILTIN ||
771 			    cmdentry.u.bltin != bltincmd)
772 				break;
773 			cmd_flags |= DO_NOFUNC;
774 			argsused = parse_command_args(argc, argv, &use_syspath);
775 			if (argsused == 0) {
776 				/* use 'type' builting to display info */
777 				cmdentry.u.bltin = typecmd;
778 				break;
779 			}
780 			argc -= argsused;
781 			argv += argsused;
782 			if (use_syspath)
783 				path = syspath() + 5;
784 		} while (argc != 0);
785 		if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
786 			/* posix mandates that 'command <splbltin>' act as if
787 			   <splbltin> was a normal builtin */
788 			cmdentry.cmdtype = CMDBUILTIN;
789 	}
790 
791 	/* Fork off a child process if necessary. */
792 	if (cmd->ncmd.backgnd
793 	 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
794 	 || ((flags & EV_BACKCMD) != 0
795 	    && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
796 		 || cmdentry.u.bltin == dotcmd
797 		 || cmdentry.u.bltin == evalcmd))) {
798 		INTOFF;
799 		jp = makejob(cmd, 1);
800 		mode = cmd->ncmd.backgnd;
801 		if (flags & EV_BACKCMD) {
802 			mode = FORK_NOJOB;
803 			if (pipe(pip) < 0)
804 				error("Pipe call failed");
805 		}
806 #ifdef DO_SHAREDVFORK
807 		/* It is essential that if DO_SHAREDVFORK is defined that the
808 		 * child's address space is actually shared with the parent as
809 		 * we rely on this.
810 		 */
811 		if (cmdentry.cmdtype == CMDNORMAL) {
812 			pid_t	pid;
813 
814 			savelocalvars = localvars;
815 			localvars = NULL;
816 			vforked = 1;
817 			switch (pid = vfork()) {
818 			case -1:
819 				TRACE(("Vfork failed, errno=%d", errno));
820 				INTON;
821 				error("Cannot vfork");
822 				break;
823 			case 0:
824 				/* Make sure that exceptions only unwind to
825 				 * after the vfork(2)
826 				 */
827 				if (setjmp(jmploc.loc)) {
828 					if (exception == EXSHELLPROC) {
829 						/* We can't progress with the vfork,
830 						 * so, set vforked = 2 so the parent
831 						 * knows, and _exit();
832 						 */
833 						vforked = 2;
834 						_exit(0);
835 					} else {
836 						_exit(exerrno);
837 					}
838 				}
839 				savehandler = handler;
840 				handler = &jmploc;
841 				listmklocal(varlist.list, VEXPORT | VNOFUNC);
842 				forkchild(jp, cmd, mode, vforked);
843 				break;
844 			default:
845 				handler = savehandler;	/* restore from vfork(2) */
846 				poplocalvars();
847 				localvars = savelocalvars;
848 				if (vforked == 2) {
849 					vforked = 0;
850 
851 					(void)waitpid(pid, NULL, 0);
852 					/* We need to progress in a normal fork fashion */
853 					goto normal_fork;
854 				}
855 				vforked = 0;
856 				forkparent(jp, cmd, mode, pid);
857 				goto parent;
858 			}
859 		} else {
860 normal_fork:
861 #endif
862 			if (forkshell(jp, cmd, mode) != 0)
863 				goto parent;	/* at end of routine */
864 			FORCEINTON;
865 #ifdef DO_SHAREDVFORK
866 		}
867 #endif
868 		if (flags & EV_BACKCMD) {
869 			if (!vforked) {
870 				FORCEINTON;
871 			}
872 			close(pip[0]);
873 			if (pip[1] != 1) {
874 				close(1);
875 				copyfd(pip[1], 1);
876 				close(pip[1]);
877 			}
878 		}
879 		flags |= EV_EXIT;
880 	}
881 
882 	/* This is the child process if a fork occurred. */
883 	/* Execute the command. */
884 	switch (cmdentry.cmdtype) {
885 	case CMDFUNCTION:
886 #ifdef DEBUG
887 		trputs("Shell function:  ");  trargs(argv);
888 #endif
889 		redirect(cmd->ncmd.redirect, REDIR_PUSH);
890 		saveparam = shellparam;
891 		shellparam.malloc = 0;
892 		shellparam.reset = 1;
893 		shellparam.nparam = argc - 1;
894 		shellparam.p = argv + 1;
895 		shellparam.optnext = NULL;
896 		INTOFF;
897 		savelocalvars = localvars;
898 		localvars = NULL;
899 		INTON;
900 		if (setjmp(jmploc.loc)) {
901 			if (exception == EXSHELLPROC) {
902 				freeparam((volatile struct shparam *)
903 				    &saveparam);
904 			} else {
905 				freeparam(&shellparam);
906 				shellparam = saveparam;
907 			}
908 			poplocalvars();
909 			localvars = savelocalvars;
910 			handler = savehandler;
911 			longjmp(handler->loc, 1);
912 		}
913 		savehandler = handler;
914 		handler = &jmploc;
915 		listmklocal(varlist.list, 0);
916 		/* stop shell blowing its stack */
917 		if (++funcnest > 1000)
918 			error("too many nested function calls");
919 		evaltree(cmdentry.u.func, flags & EV_TESTED);
920 		funcnest--;
921 		INTOFF;
922 		poplocalvars();
923 		localvars = savelocalvars;
924 		freeparam(&shellparam);
925 		shellparam = saveparam;
926 		handler = savehandler;
927 		popredir();
928 		INTON;
929 		if (evalskip == SKIPFUNC) {
930 			evalskip = 0;
931 			skipcount = 0;
932 		}
933 		if (flags & EV_EXIT)
934 			exitshell(exitstatus);
935 		break;
936 
937 	case CMDBUILTIN:
938 	case CMDSPLBLTIN:
939 #ifdef DEBUG
940 		trputs("builtin command:  ");  trargs(argv);
941 #endif
942 		mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
943 		if (flags == EV_BACKCMD) {
944 			memout.nleft = 0;
945 			memout.nextc = memout.buf;
946 			memout.bufsize = 64;
947 			mode |= REDIR_BACKQ;
948 		}
949 		e = -1;
950 		savehandler = handler;
951 		handler = &jmploc;
952 		if (!setjmp(jmploc.loc)) {
953 			/* We need to ensure the command hash table isn't
954 			 * corruped by temporary PATH assignments.
955 			 * However we must ensure the 'local' command works!
956 			 */
957 			if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
958 			    cmdentry.u.bltin == typecmd)) {
959 				savelocalvars = localvars;
960 				localvars = 0;
961 				mklocal(path - 5 /* PATH= */, 0);
962 				temp_path = 1;
963 			} else
964 				temp_path = 0;
965 			redirect(cmd->ncmd.redirect, mode);
966 
967 			savecmdname = commandname;
968 			/* exec is a special builtin, but needs this list... */
969 			cmdenviron = varlist.list;
970 			/* we must check 'readonly' flag for all builtins */
971 			listsetvar(varlist.list,
972 				cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
973 			commandname = argv[0];
974 			/* initialize nextopt */
975 			argptr = argv + 1;
976 			optptr = NULL;
977 			/* and getopt */
978 			optreset = 1;
979 			optind = 1;
980 			exitstatus = cmdentry.u.bltin(argc, argv);
981 		} else {
982 			e = exception;
983 			exitstatus = e == EXINT ? SIGINT + 128 :
984 					e == EXEXEC ? exerrno : 2;
985 		}
986 		handler = savehandler;
987 		flushall();
988 		out1 = &output;
989 		out2 = &errout;
990 		freestdout();
991 		if (temp_path) {
992 			poplocalvars();
993 			localvars = savelocalvars;
994 		}
995 		cmdenviron = NULL;
996 		if (e != EXSHELLPROC) {
997 			commandname = savecmdname;
998 			if (flags & EV_EXIT)
999 				exitshell(exitstatus);
1000 		}
1001 		if (e != -1) {
1002 			if ((e != EXERROR && e != EXEXEC)
1003 			    || cmdentry.cmdtype == CMDSPLBLTIN)
1004 				exraise(e);
1005 			FORCEINTON;
1006 		}
1007 		if (cmdentry.u.bltin != execcmd)
1008 			popredir();
1009 		if (flags == EV_BACKCMD) {
1010 			backcmd->buf = memout.buf;
1011 			backcmd->nleft = memout.nextc - memout.buf;
1012 			memout.buf = NULL;
1013 		}
1014 		break;
1015 
1016 	default:
1017 #ifdef DEBUG
1018 		trputs("normal command:  ");  trargs(argv);
1019 #endif
1020 		clearredir(vforked);
1021 		redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
1022 		if (!vforked)
1023 			for (sp = varlist.list ; sp ; sp = sp->next)
1024 				setvareq(sp->text, VEXPORT|VSTACK);
1025 		envp = environment();
1026 		shellexec(argv, envp, path, cmdentry.u.index, vforked);
1027 		break;
1028 	}
1029 	goto out;
1030 
1031 parent:	/* parent process gets here (if we forked) */
1032 	if (mode == FORK_FG) {	/* argument to fork */
1033 		exitstatus = waitforjob(jp);
1034 	} else if (mode == FORK_NOJOB) {
1035 		backcmd->fd = pip[0];
1036 		close(pip[1]);
1037 		backcmd->jp = jp;
1038 	}
1039 	FORCEINTON;
1040 
1041 out:
1042 	if (lastarg)
1043 		/* dsl: I think this is intended to be used to support
1044 		 * '_' in 'vi' command mode during line editing...
1045 		 * However I implemented that within libedit itself.
1046 		 */
1047 		setvar("_", lastarg, 0);
1048 	popstackmark(&smark);
1049 
1050 	if (eflag && exitstatus && !(flags & EV_TESTED))
1051 		exitshell(exitstatus);
1052 }
1053 
1054 
1055 /*
1056  * Search for a command.  This is called before we fork so that the
1057  * location of the command will be available in the parent as well as
1058  * the child.  The check for "goodname" is an overly conservative
1059  * check that the name will not be subject to expansion.
1060  */
1061 
1062 STATIC void
1063 prehash(union node *n)
1064 {
1065 	struct cmdentry entry;
1066 
1067 	if (n->type == NCMD && n->ncmd.args)
1068 		if (goodname(n->ncmd.args->narg.text))
1069 			find_command(n->ncmd.args->narg.text, &entry, 0,
1070 				     pathval());
1071 }
1072 
1073 
1074 
1075 /*
1076  * Builtin commands.  Builtin commands whose functions are closely
1077  * tied to evaluation are implemented here.
1078  */
1079 
1080 /*
1081  * No command given.
1082  */
1083 
1084 int
1085 bltincmd(int argc, char **argv)
1086 {
1087 	/*
1088 	 * Preserve exitstatus of a previous possible redirection
1089 	 * as POSIX mandates
1090 	 */
1091 	return back_exitstatus;
1092 }
1093 
1094 
1095 /*
1096  * Handle break and continue commands.  Break, continue, and return are
1097  * all handled by setting the evalskip flag.  The evaluation routines
1098  * above all check this flag, and if it is set they start skipping
1099  * commands rather than executing them.  The variable skipcount is
1100  * the number of loops to break/continue, or the number of function
1101  * levels to return.  (The latter is always 1.)  It should probably
1102  * be an error to break out of more loops than exist, but it isn't
1103  * in the standard shell so we don't make it one here.
1104  */
1105 
1106 int
1107 breakcmd(int argc, char **argv)
1108 {
1109 	int n = argc > 1 ? number(argv[1]) : 1;
1110 
1111 	if (n > loopnest)
1112 		n = loopnest;
1113 	if (n > 0) {
1114 		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1115 		skipcount = n;
1116 	}
1117 	return 0;
1118 }
1119 
1120 
1121 /*
1122  * The return command.
1123  */
1124 
1125 int
1126 returncmd(int argc, char **argv)
1127 {
1128 	int ret = argc > 1 ? number(argv[1]) : exitstatus;
1129 
1130 	if (funcnest) {
1131 		evalskip = SKIPFUNC;
1132 		skipcount = 1;
1133 		return ret;
1134 	}
1135 	else {
1136 		/* Do what ksh does; skip the rest of the file */
1137 		evalskip = SKIPFILE;
1138 		skipcount = 1;
1139 		return ret;
1140 	}
1141 }
1142 
1143 
1144 int
1145 falsecmd(int argc, char **argv)
1146 {
1147 	return 1;
1148 }
1149 
1150 
1151 int
1152 truecmd(int argc, char **argv)
1153 {
1154 	return 0;
1155 }
1156 
1157 
1158 int
1159 execcmd(int argc, char **argv)
1160 {
1161 	if (argc > 1) {
1162 		struct strlist *sp;
1163 
1164 		iflag = 0;		/* exit on error */
1165 		mflag = 0;
1166 		optschanged();
1167 		for (sp = cmdenviron; sp; sp = sp->next)
1168 			setvareq(sp->text, VEXPORT|VSTACK);
1169 		shellexec(argv + 1, environment(), pathval(), 0, 0);
1170 	}
1171 	return 0;
1172 }
1173 
1174 static int
1175 conv_time(clock_t ticks, char *seconds, size_t l)
1176 {
1177 	static clock_t tpm = 0;
1178 	clock_t mins;
1179 	int i;
1180 
1181 	if (!tpm)
1182 		tpm = sysconf(_SC_CLK_TCK) * 60;
1183 
1184 	mins = ticks / tpm;
1185 	snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1186 
1187 	if (seconds[0] == '6' && seconds[1] == '0') {
1188 		/* 59.99995 got rounded up... */
1189 		mins++;
1190 		strlcpy(seconds, "0.0", l);
1191 		return mins;
1192 	}
1193 
1194 	/* suppress trailing zeros */
1195 	i = strlen(seconds) - 1;
1196 	for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1197 		seconds[i] = 0;
1198 	return mins;
1199 }
1200 
1201 int
1202 timescmd(int argc, char **argv)
1203 {
1204 	struct tms tms;
1205 	int u, s, cu, cs;
1206 	char us[8], ss[8], cus[8], css[8];
1207 
1208 	nextopt("");
1209 
1210 	times(&tms);
1211 
1212 	u = conv_time(tms.tms_utime, us, sizeof(us));
1213 	s = conv_time(tms.tms_stime, ss, sizeof(ss));
1214 	cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1215 	cs = conv_time(tms.tms_cstime, css, sizeof(css));
1216 
1217 	outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1218 		u, us, s, ss, cu, cus, cs, css);
1219 
1220 	return 0;
1221 }
1222