eval.c revision 201343
14Srgrimes/*-
24Srgrimes * Copyright (c) 1993
34Srgrimes *	The Regents of the University of California.  All rights reserved.
44Srgrimes *
54Srgrimes * This code is derived from software contributed to Berkeley by
64Srgrimes * Kenneth Almquist.
74Srgrimes *
84Srgrimes * Redistribution and use in source and binary forms, with or without
94Srgrimes * modification, are permitted provided that the following conditions
104Srgrimes * are met:
114Srgrimes * 1. Redistributions of source code must retain the above copyright
124Srgrimes *    notice, this list of conditions and the following disclaimer.
134Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
144Srgrimes *    notice, this list of conditions and the following disclaimer in the
154Srgrimes *    documentation and/or other materials provided with the distribution.
164Srgrimes * 4. Neither the name of the University nor the names of its contributors
174Srgrimes *    may be used to endorse or promote products derived from this software
184Srgrimes *    without specific prior written permission.
194Srgrimes *
204Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
214Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
244Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304Srgrimes * SUCH DAMAGE.
314Srgrimes */
324Srgrimes
334Srgrimes#ifndef lint
344Srgrimes#if 0
354Srgrimesstatic char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
36619Srgrimes#endif
3748888Sbde#endif /* not lint */
384Srgrimes#include <sys/cdefs.h>
394Srgrimes__FBSDID("$FreeBSD: head/bin/sh/eval.c 201343 2009-12-31 16:13:33Z jilles $");
403185Ssos
4119173Sbde#include <paths.h>
4219173Sbde#include <signal.h>
4319173Sbde#include <stdlib.h>
4419173Sbde#include <unistd.h>
453185Ssos#include <sys/resource.h>
463185Ssos#include <sys/wait.h> /* For WIFSIGNALED(status) */
473185Ssos#include <errno.h>
483185Ssos
492913Sache/*
502913Sache * Evaluate a command.
5116299Spst */
5233929Sphk
5313228Swollman#include "shell.h"
542056Swollman#include "nodes.h"
552056Swollman#include "syntax.h"
562056Swollman#include "expand.h"
572056Swollman#include "parser.h"
5831253Sbde#include "jobs.h"
5931253Sbde#include "eval.h"
6031253Sbde#include "builtins.h"
6115508Sbde#include "options.h"
6215508Sbde#include "exec.h"
634180Sbde#include "redir.h"
6415508Sbde#include "input.h"
6515508Sbde#include "output.h"
6615508Sbde#include "trap.h"
6730805Sbde#include "var.h"
682056Swollman#include "memalloc.h"
6926309Speter#include "error.h"
7028551Sbde#include "show.h"
7132054Sphk#include "mystring.h"
7247588Sbde#ifndef NO_HISTORY
7334617Sphk#include "myhistedit.h"
7434617Sphk#endif
7534617Sphk
7634617Sphk
7730805Sbdeint evalskip;			/* set if we are skipping commands */
7830805SbdeSTATIC int skipcount;		/* number of levels to skip */
7930805SbdeMKINIT int loopnest;		/* current loop nesting level */
8028921Sfsmpint funcnest;			/* depth of function calls */
8126949SfsmpSTATIC int builtin_flags;	/* evalcommand flags for builtins */
8228921Sfsmp
8332054Sphk
8415508Sbdechar *commandname;
852056Swollmanstruct strlist *cmdenviron;
862056Swollmanint exitstatus;			/* exit status of last command */
8747642Sdfrint oexitstatus;		/* saved exit status */
882056Swollman
894Srgrimes
9045897SpeterSTATIC void evalloop(union node *, int);
9128487SfsmpSTATIC void evalfor(union node *, int);
9228921SfsmpSTATIC void evalcase(union node *, int);
9329000SfsmpSTATIC void evalsubshell(union node *, int);
9429000SfsmpSTATIC void expredir(union node *);
9534058SteggeSTATIC void evalpipe(union node *);
9634571SteggeSTATIC void evalcommand(union node *, int, struct backcmd *);
9734571SteggeSTATIC void prehash(union node *);
9834058Stegge
9934058Stegge
10034571Stegge/*
10134571Stegge * Called to reset things after an exception.
10234571Stegge */
10328921Sfsmp
10428921Sfsmp#ifdef mkinit
1052873SbdeINCLUDE "eval.h"
1062873Sbde
1072873SbdeRESET {
1082873Sbde	evalskip = 0;
1092873Sbde	loopnest = 0;
1102913Sache	funcnest = 0;
1112873Sbde}
11215508Sbde
1134SrgrimesSHELLPROC {
1144180Sbde	exitstatus = 0;
1154180Sbde}
1164180Sbde#endif
1174180Sbde
1184180Sbde
1194180Sbde
1204180Sbde/*
1214180Sbde * The eval command.
1224180Sbde */
1234180Sbde
12417236Sjoergint
12517231Sjoergevalcmd(int argc, char **argv)
12633690Sphk{
1274180Sbde        char *p;
12817231Sjoerg        char *concat;
1294180Sbde        char **ap;
13041787Smckay
13147588Sbde        if (argc > 1) {
13215045Sache                p = argv[1];
13346847Speter                if (argc > 2) {
13432052Sphk                        STARTSTACKSTR(concat);
13532052Sphk                        ap = argv + 2;
13633690Sphk                        for (;;) {
13733690Sphk                                while (*p)
13833690Sphk                                        STPUTC(*p++, concat);
13932052Sphk                                if ((p = *ap++) == NULL)
14032052Sphk                                        break;
14132005Sphk                                STPUTC(' ', concat);
14247592Sphk                        }
14341787Smckay                        STPUTC('\0', concat);
1441390Ssos                        p = grabstackstr(concat);
1454180Sbde                }
1465291Sbde                evalstring(p, builtin_flags & EV_TESTED);
1474180Sbde        }
14819173Sbde        return exitstatus;
14933690Sphk}
15033690Sphk
15133690Sphk
1524180Sbde/*
1534180Sbde * Execute a command or commands contained in a string.
1544180Sbde */
1554180Sbde
1564180Sbdevoid
1574180Sbdeevalstring(char *s, int flags)
15819173Sbde{
15919173Sbde	union node *n;
1604180Sbde	struct stackmark smark;
16115345Snate	int flags_exit;
16233690Sphk
16317231Sjoerg	flags_exit = flags & EV_EXIT;
16417231Sjoerg	flags &= ~EV_EXIT;
16517236Sjoerg	setstackmark(&smark);
16617236Sjoerg	setinputstring(s, 1);
16717236Sjoerg	while ((n = parsecmd(0)) != NEOF) {
16817236Sjoerg		if (n != NULL) {
16917231Sjoerg			if (flags_exit && preadateof())
17017231Sjoerg				evaltree(n, flags | EV_EXIT);
17117231Sjoerg			else
17219173Sbde				evaltree(n, flags);
17333690Sphk		}
1744180Sbde		popstackmark(&smark);
17536719Sphk	}
17636719Sphk	popfile();
17721783Sbde	popstackmark(&smark);
17817353Sbde	if (flags_exit)
17940610Sphk		exitshell(exitstatus);
18033690Sphk}
18136741Sphk
18236198Sphk
18333690Sphk/*
18433690Sphk * Evaluate a parse tree.  The value is left in the global variable
18533690Sphk * exitstatus.
18633690Sphk */
18733690Sphk
18840610Sphkvoid
18933690Sphkevaltree(union node *n, int flags)
19040610Sphk{
19133690Sphk	int do_etest;
19236741Sphk
19336198Sphk	do_etest = 0;
19433690Sphk	if (n == NULL) {
19533690Sphk		TRACE(("evaltree(NULL) called\n"));
19633690Sphk		exitstatus = 0;
19733690Sphk		goto out;
19833690Sphk	}
19940610Sphk#ifndef NO_HISTORY
20033690Sphk	displayhist = 1;	/* show history substitutions done with fc */
20112724Sphk#endif
2023185Ssos	TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
2032074Swollman	switch (n->type) {
20439503Sbde	case NSEMI:
20547588Sbde		evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
20647588Sbde		if (evalskip)
20739503Sbde			goto out;
20847588Sbde		evaltree(n->nbinary.ch2, flags);
20939503Sbde		break;
21039503Sbde	case NAND:
21139503Sbde		evaltree(n->nbinary.ch1, EV_TESTED);
21247588Sbde		if (evalskip || exitstatus != 0) {
21347588Sbde			goto out;
21439503Sbde		}
2151549Srgrimes		evaltree(n->nbinary.ch2, flags);
2161442Ssos		break;
21717236Sjoerg	case NOR:
21817231Sjoerg		evaltree(n->nbinary.ch1, EV_TESTED);
2198448Sbde		if (evalskip || exitstatus == 0)
2201442Ssos			goto out;
22117236Sjoerg		evaltree(n->nbinary.ch2, flags);
22217231Sjoerg		break;
2234180Sbde	case NREDIR:
2244180Sbde		expredir(n->nredir.redirect);
22533309Sbde		redirect(n->nredir.redirect, REDIR_PUSH);
2261549Srgrimes		evaltree(n->nredir.n, flags);
2278448Sbde		popredir();
2281390Ssos		break;
2291442Ssos	case NSUBSHELL:
23017236Sjoerg		evalsubshell(n, flags);
23117231Sjoerg		do_etest = !(flags & EV_TESTED);
2328448Sbde		break;
2334180Sbde	case NBACKGND:
23429000Sfsmp		evalsubshell(n, flags);
2354180Sbde		break;
2364180Sbde	case NIF: {
2374180Sbde		evaltree(n->nif.test, EV_TESTED);
23829000Sfsmp		if (evalskip)
2394180Sbde			goto out;
2401442Ssos		if (exitstatus == 0)
24117231Sjoerg			evaltree(n->nif.ifpart, flags);
2421442Ssos		else if (n->nif.elsepart)
24317236Sjoerg			evaltree(n->nif.elsepart, flags);
24417231Sjoerg		else
2454180Sbde			exitstatus = 0;
2464180Sbde		break;
24733309Sbde	}
24834961Sphk	case NWHILE:
24933309Sbde	case NUNTIL:
25034961Sphk		evalloop(n, flags & ~EV_EXIT);
25134961Sphk		break;
25234961Sphk	case NFOR:
25334961Sphk		evalfor(n, flags & ~EV_EXIT);
25434961Sphk		break;
25533309Sbde	case NCASE:
25633309Sbde		evalcase(n, flags);
25733309Sbde		break;
25833309Sbde	case NDEFUN:
25933309Sbde		defun(n->narg.text, n->narg.next);
26034961Sphk		exitstatus = 0;
2611549Srgrimes		break;
2628448Sbde	case NNOT:
2635291Sbde		evaltree(n->nnot.com, EV_TESTED);
26429000Sfsmp		exitstatus = !exitstatus;
2654180Sbde		break;
2664180Sbde
2674180Sbde	case NPIPE:
2684180Sbde		evalpipe(n);
26929000Sfsmp		do_etest = !(flags & EV_TESTED);
2704180Sbde		break;
27117231Sjoerg	case NCMD:
27217231Sjoerg		evalcommand(n, flags, (struct backcmd *)NULL);
2731442Ssos		do_etest = !(flags & EV_TESTED);
2741442Ssos		break;
2751442Ssos	default:
2761390Ssos		out1fmt("Node type = %d\n", n->type);
2771390Ssos		flushout(&output);
27817231Sjoerg		break;
27917236Sjoerg	}
28017231Sjoergout:
2811390Ssos	if (pendingsigs)
2824180Sbde		dotrap();
2831390Ssos	if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest))
28417231Sjoerg		exitshell(exitstatus);
28517231Sjoerg}
28617231Sjoerg
28717231Sjoerg
28836810SphkSTATIC void
28933690Sphkevalloop(union node *n, int flags)
29017231Sjoerg{
29117231Sjoerg	int status;
29217236Sjoerg
29317236Sjoerg	loopnest++;
29417236Sjoerg	status = 0;
29517231Sjoerg	for (;;) {
29617236Sjoerg		evaltree(n->nbinary.ch1, EV_TESTED);
29717236Sjoerg		if (evalskip) {
29817236Sjoergskipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
29917236Sjoerg				evalskip = 0;
30017236Sjoerg				continue;
30117236Sjoerg			}
30217236Sjoerg			if (evalskip == SKIPBREAK && --skipcount <= 0)
30317236Sjoerg				evalskip = 0;
30417236Sjoerg			break;
30517236Sjoerg		}
30617236Sjoerg		if (n->type == NWHILE) {
30717236Sjoerg			if (exitstatus != 0)
30817236Sjoerg				break;
30917236Sjoerg		} else {
31017231Sjoerg			if (exitstatus == 0)
3111442Ssos				break;
31217231Sjoerg		}
31317231Sjoerg		evaltree(n->nbinary.ch2, flags);
3141390Ssos		status = exitstatus;
3151390Ssos		if (evalskip)
3161390Ssos			goto skipping;
3171390Ssos	}
3181390Ssos	loopnest--;
31917231Sjoerg	exitstatus = status;
32017231Sjoerg}
32117231Sjoerg
32217231Sjoerg
32317236Sjoerg
32417236SjoergSTATIC void
32517236Sjoergevalfor(union node *n, int flags)
32617236Sjoerg{
32717236Sjoerg	struct arglist arglist;
32817236Sjoerg	union node *argp;
32917236Sjoerg	struct strlist *sp;
33017236Sjoerg	struct stackmark smark;
33117236Sjoerg
33217236Sjoerg	setstackmark(&smark);
33317231Sjoerg	arglist.lastp = &arglist.list;
3341390Ssos	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
3351390Ssos		oexitstatus = exitstatus;
3361390Ssos		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3371390Ssos		if (evalskip)
3381390Ssos			goto out;
33917231Sjoerg	}
34017231Sjoerg	*arglist.lastp = NULL;
34117236Sjoerg
34217236Sjoerg	exitstatus = 0;
34317236Sjoerg	loopnest++;
34417231Sjoerg	for (sp = arglist.list ; sp ; sp = sp->next) {
34517236Sjoerg		setvar(n->nfor.var, sp->text, 0);
34617236Sjoerg		evaltree(n->nfor.body, flags);
34717236Sjoerg		if (evalskip) {
34817236Sjoerg			if (evalskip == SKIPCONT && --skipcount <= 0) {
34917236Sjoerg				evalskip = 0;
35017236Sjoerg				continue;
35117236Sjoerg			}
35217231Sjoerg			if (evalskip == SKIPBREAK && --skipcount <= 0)
35317231Sjoerg				evalskip = 0;
3541390Ssos			break;
3551390Ssos		}
3561390Ssos	}
3571390Ssos	loopnest--;
3581390Ssosout:
35917231Sjoerg	popstackmark(&smark);
36017231Sjoerg}
36117231Sjoerg
36217231Sjoerg
36317236Sjoerg
36417231SjoergSTATIC void
3651390Ssosevalcase(union node *n, int flags)
3661390Ssos{
3673185Ssos	union node *cp;
3683185Ssos	union node *patp;
3693185Ssos	struct arglist arglist;
3703185Ssos	struct stackmark smark;
3713185Ssos
3723185Ssos	setstackmark(&smark);
3733185Ssos	arglist.lastp = &arglist.list;
3743185Ssos	oexitstatus = exitstatus;
3753185Ssos	exitstatus = 0;
3763185Ssos	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
3773185Ssos	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
3783185Ssos		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
37924676Smckay			if (casematch(patp, arglist.list->text)) {
38024676Smckay				if (evalskip == 0) {
38124676Smckay					evaltree(cp->nclist.body, flags);
38224676Smckay				}
38324676Smckay				goto out;
38424676Smckay			}
38524676Smckay		}
38624676Smckay	}
3873185Ssosout:
38812724Sphk	popstackmark(&smark);
3893185Ssos}
3903185Ssos
39124676Smckay
3923185Ssos
3933185Ssos/*
3941390Ssos * Kick off a subshell to evaluate a tree.
39518297Sbde */
3965291Sbde
39718297SbdeSTATIC void
39818297Sbdeevalsubshell(union node *n, int flags)
39918297Sbde{
4003185Ssos	struct job *jp;
4015291Sbde	int backgnd = (n->type == NBACKGND);
4025291Sbde
4035291Sbde	expredir(n->nredir.redirect);
4045291Sbde	if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
4053185Ssos			forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
40618297Sbde		if (backgnd)
4073185Ssos			flags &=~ EV_TESTED;
4081390Ssos		redirect(n->nredir.redirect, 0);
40910268Sbde		evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
4101390Ssos	} else if (! backgnd) {
41116428Sbde		INTOFF;
4121390Ssos		exitstatus = waitforjob(jp, (int *)NULL);
4131390Ssos		INTON;
41416428Sbde	}
41529000Sfsmp}
41616428Sbde
41716428Sbde
41819173Sbde
41916428Sbde/*
4201390Ssos * Compute the names of the files in a redirection list.
4211390Ssos */
42216428Sbde
42328921SfsmpSTATIC void
42416428Sbdeexpredir(union node *n)
4251390Ssos{
4261390Ssos	union node *redir;
4271390Ssos
4282017Swollman	for (redir = n ; redir ; redir = redir->nfile.next) {
4291390Ssos		struct arglist fn;
43015508Sbde		fn.lastp = &fn.list;
4311390Ssos		oexitstatus = exitstatus;
4321390Ssos		switch (redir->type) {
4331390Ssos		case NFROM:
4341390Ssos		case NTO:
4351390Ssos		case NFROMTO:
43622106Sbde		case NAPPEND:
4371390Ssos		case NCLOBBER:
4381390Ssos			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
4391390Ssos			redir->nfile.expfname = fn.list->text;
4401390Ssos			break;
4411390Ssos		case NFROMFD:
4421390Ssos		case NTOFD:
4431390Ssos			if (redir->ndup.vname) {
4441390Ssos				expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
4451390Ssos				fixredir(redir, fn.list->text, 1);
4461390Ssos			}
4471390Ssos			break;
4481390Ssos		}
4491390Ssos	}
4501390Ssos}
4511390Ssos
4521390Ssos
45321783Sbde
45421783Sbde/*
45521783Sbde * Evaluate a pipeline.  All the processes in the pipeline are children
45621783Sbde * of the process creating the pipeline.  (This differs from some versions
45721783Sbde * of the shell, which make the last process in a pipeline the parent
45821783Sbde * of all the rest.)
45921783Sbde */
4601390Ssos
4611390SsosSTATIC void
4621390Ssosevalpipe(union node *n)
4631390Ssos{
4641390Ssos	struct job *jp;
4651390Ssos	struct nodelist *lp;
46610268Sbde	int pipelen;
46722106Sbde	int prevfd;
4681390Ssos	int pip[2];
46915508Sbde
4701390Ssos	TRACE(("evalpipe(%p) called\n", (void *)n));
4711390Ssos	pipelen = 0;
47222106Sbde	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
47322106Sbde		pipelen++;
47422106Sbde	INTOFF;
47522106Sbde	jp = makejob(n, pipelen);
47622106Sbde	prevfd = -1;
47722106Sbde	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
47822106Sbde		prehash(lp->n);
47922106Sbde		pip[1] = -1;
48022106Sbde		if (lp->next) {
48122106Sbde			if (pipe(pip) < 0) {
48222106Sbde				close(prevfd);
48322106Sbde				error("Pipe call failed: %s", strerror(errno));
48422106Sbde			}
48522106Sbde		}
48622106Sbde		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
48722106Sbde			INTON;
48822106Sbde			if (prevfd > 0) {
48922106Sbde				dup2(prevfd, 0);
49022106Sbde				close(prevfd);
4911390Ssos			}
4921390Ssos			if (pip[1] >= 0) {
49310268Sbde				if (!(prevfd >= 0 && pip[0] == 0))
4941390Ssos					close(pip[0]);
4951390Ssos				if (pip[1] != 1) {
4961390Ssos					dup2(pip[1], 1);
49721783Sbde					close(pip[1]);
4981390Ssos				}
49921783Sbde			}
50021783Sbde			evaltree(lp->n, EV_EXIT);
50121783Sbde		}
50221783Sbde		if (prevfd >= 0)
50321783Sbde			close(prevfd);
50421783Sbde		prevfd = pip[0];
50521783Sbde		close(pip[1]);
50621783Sbde	}
50721783Sbde	INTON;
50821783Sbde	if (n->npipe.backgnd == 0) {
50921783Sbde		INTOFF;
51021783Sbde		exitstatus = waitforjob(jp, (int *)NULL);
5111390Ssos		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
5121390Ssos		INTON;
5131390Ssos	}
5141390Ssos}
5151390Ssos
5161390Ssos
5171390Ssos
5181390Ssos/*
5191390Ssos * Execute a command inside back quotes.  If it's a builtin command, we
5203185Ssos * want to save its output in a block obtained from malloc.  Otherwise
5211390Ssos * we fork off a subprocess and get the output of the command via a pipe.
5221390Ssos * Should be called with interrupts off.
5231390Ssos */
5241390Ssos
5251390Ssosvoid
5261390Ssosevalbackcmd(union node *n, struct backcmd *result)
5278876Srgrimes{
5281390Ssos	int pip[2];
5291390Ssos	struct job *jp;
53017231Sjoerg	struct stackmark smark;		/* unnecessary */
5311390Ssos
5328876Srgrimes	setstackmark(&smark);
53317231Sjoerg	result->fd = -1;
53417231Sjoerg	result->buf = NULL;
53517231Sjoerg	result->nleft = 0;
53617231Sjoerg	result->jp = NULL;
53717231Sjoerg	if (n == NULL) {
53829000Sfsmp		exitstatus = 0;
5391390Ssos		goto out;
5401390Ssos	}
54129000Sfsmp	if (n->type == NCMD) {
5421390Ssos		exitstatus = oexitstatus;
54317231Sjoerg		evalcommand(n, EV_BACKCMD, result);
54417231Sjoerg	} else {
5451390Ssos		exitstatus = 0;
5462873Sbde		if (pipe(pip) < 0)
5471390Ssos			error("Pipe call failed: %s", strerror(errno));
54817231Sjoerg		jp = makejob(n, 1);
54917231Sjoerg		if (forkshell(jp, n, FORK_NOJOB) == 0) {
5501390Ssos			FORCEINTON;
5511390Ssos			close(pip[0]);
5522913Sache			if (pip[1] != 1) {
5532913Sache				dup2(pip[1], 1);
5542913Sache				close(pip[1]);
5552913Sache			}
55614943Sbde			evaltree(n, EV_EXIT);
55714943Sbde		}
55814943Sbde		close(pip[1]);
55914943Sbde		result->fd = pip[0];
56014943Sbde		result->jp = jp;
56114943Sbde	}
56214943Sbdeout:
56314943Sbde	popstackmark(&smark);
56414943Sbde	TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
56514943Sbde		result->fd, result->buf, result->nleft, result->jp));
56614943Sbde}
56714943Sbde
56814943Sbde
56913445Sphk
5705291Sbde/*
5712913Sache * Execute a simple command.
57233309Sbde */
5735291Sbde
57433309SbdeSTATIC void
5755291Sbdeevalcommand(union node *cmd, int flags, struct backcmd *backcmd)
57633309Sbde{
5772913Sache	struct stackmark smark;
5782913Sache	union node *argp;
57913445Sphk	struct arglist arglist;
5802913Sache	struct arglist varlist;
5812913Sache	char **argv;
58213445Sphk	int argc;
5832913Sache	char **envp;
5842913Sache	int varflag;
58515508Sbde	struct strlist *sp;
58615508Sbde	int mode;
58715508Sbde	int pip[2];
58848160Sgreen	struct cmdentry cmdentry;
58915508Sbde	struct job *jp;
59015508Sbde	struct jmploc jmploc;
59115508Sbde	struct jmploc *savehandler;
59223393Sbde	char *savecmdname;
59323393Sbde	struct shparam saveparam;
59415508Sbde	struct localvar *savelocalvars;
59515508Sbde	struct parsefile *savetopfile;
59615508Sbde	volatile int e;
59715508Sbde	char *lastarg;
59815508Sbde	int realstatus;
59915508Sbde	int do_clearcmdentry;
60015508Sbde
60115508Sbde	/* First expand the arguments. */
60215508Sbde	TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
60315508Sbde	setstackmark(&smark);
60415508Sbde	arglist.lastp = &arglist.list;
60515508Sbde	varlist.lastp = &varlist.list;
60615508Sbde	varflag = 1;
60715508Sbde	do_clearcmdentry = 0;
60815508Sbde	oexitstatus = exitstatus;
60915508Sbde	exitstatus = 0;
61015508Sbde	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
61115508Sbde		char *p = argp->narg.text;
61215508Sbde		if (varflag && is_name(*p)) {
61315508Sbde			do {
61415508Sbde				p++;
61515508Sbde			} while (is_in_name(*p));
61615508Sbde			if (*p == '=') {
61715508Sbde				expandarg(argp, &varlist, EXP_VARTILDE);
61815508Sbde				continue;
61915508Sbde			}
62015508Sbde		}
62115508Sbde		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
62215508Sbde		varflag = 0;
62315508Sbde	}
62415508Sbde	*arglist.lastp = NULL;
62515508Sbde	*varlist.lastp = NULL;
62632054Sphk	expredir(cmd->ncmd.redirect);
62748160Sgreen	argc = 0;
62848266Speter	for (sp = arglist.list ; sp ; sp = sp->next)
62948266Speter		argc++;
63015508Sbde	argv = stalloc(sizeof (char *) * (argc + 1));
63115508Sbde
63215508Sbde	for (sp = arglist.list ; sp ; sp = sp->next) {
63315508Sbde		TRACE(("evalcommand arg: %s\n", sp->text));
63415508Sbde		*argv++ = sp->text;
63515508Sbde	}
63615508Sbde	*argv = NULL;
63715508Sbde	lastarg = NULL;
63815508Sbde	if (iflag && funcnest == 0 && argc > 0)
63915508Sbde		lastarg = argv[-1];
64015508Sbde	argv -= argc;
64115508Sbde
64215508Sbde	/* Print the command if xflag is set. */
64315508Sbde	if (xflag) {
64415508Sbde		char sep = 0;
64515508Sbde		const char *p;
64615508Sbde		out2str(ps4val());
64715508Sbde		for (sp = varlist.list ; sp ; sp = sp->next) {
64815508Sbde			if (sep != 0)
64915508Sbde				outc(' ', &errout);
65015508Sbde			p = sp->text;
65115508Sbde			while (*p != '=' && *p != '\0')
65215508Sbde				out2c(*p++);
65315508Sbde			if (*p != '\0') {
65415508Sbde				out2c(*p++);
65515508Sbde				out2qstr(p);
65615508Sbde			}
65715508Sbde			sep = ' ';
65815508Sbde		}
65915508Sbde		for (sp = arglist.list ; sp ; sp = sp->next) {
66015508Sbde			if (sep != 0)
66115508Sbde				outc(' ', &errout);
66215508Sbde			/* Disambiguate command looking like assignment. */
66333690Sphk			if (sp == arglist.list &&
66448160Sgreen					strchr(sp->text, '=') != NULL &&
66533690Sphk					strchr(sp->text, '\'') == NULL) {
66633690Sphk				out2c('\'');
66733690Sphk				out2str(sp->text);
66832005Sphk				out2c('\'');
66933929Sphk			} else
67015508Sbde				out2qstr(sp->text);
67115508Sbde			sep = ' ';
67215508Sbde		}
67315508Sbde		outc('\n', &errout);
67423393Sbde		flushout(&errout);
67523393Sbde	}
67623393Sbde
67715508Sbde	/* Now locate the command. */
67815508Sbde	if (argc == 0) {
67915508Sbde		/* Variable assignment(s) without command */
68015508Sbde		cmdentry.cmdtype = CMDBUILTIN;
68115508Sbde		cmdentry.u.index = BLTINCMD;
68215508Sbde		cmdentry.special = 1;
68316874Sbde	} else {
68433309Sbde		static const char PATH[] = "PATH=";
68515508Sbde		char *path = pathval();
68615508Sbde
68729000Sfsmp		/*
68815508Sbde		 * Modify the command lookup path, if a PATH= assignment
68933309Sbde		 * is present
69033309Sbde		 */
69133309Sbde		for (sp = varlist.list ; sp ; sp = sp->next)
69233309Sbde			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) {
69333309Sbde				path = sp->text + sizeof(PATH) - 1;
69433309Sbde				/*
69533309Sbde				 * On `PATH=... command`, we need to make
69628921Sfsmp				 * sure that the command isn't using the
69715508Sbde				 * non-updated hash table of the outer PATH
69815508Sbde				 * setting and we need to make sure that
69915508Sbde				 * the hash table isn't filled with items
7005291Sbde				 * from the temporary setting.
70133690Sphk				 *
7025291Sbde				 * It would be better to forbit using and
7035291Sbde				 * updating the table while this command
7041390Ssos				 * runs, by the command finding mechanism
7058876Srgrimes				 * is heavily integrated with hash handling,
706798Swollman				 * so we just delete the hash before and after
70715508Sbde				 * the command runs. Partly deleting like
70815508Sbde				 * changepatch() does doesn't seem worth the
70932054Sphk				 * bookinging effort, since most such runs add
71032054Sphk				 * directories in front of the new PATH.
71132054Sphk				 */
71232054Sphk				clearcmdentry(0);
71332054Sphk				do_clearcmdentry = 1;
71415508Sbde			}
71515508Sbde
71615508Sbde		find_command(argv[0], &cmdentry, 0, path);
71716874Sbde		/* implement the bltin builtin here */
71815508Sbde		if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
71915508Sbde			for (;;) {
72015508Sbde				argv++;
72115508Sbde				if (--argc == 0)
72215508Sbde					break;
72318288Sbde				if ((cmdentry.u.index = find_builtin(*argv,
72415508Sbde				    &cmdentry.special)) < 0) {
72515508Sbde					outfmt(&errout, "%s: not found\n", *argv);
72615508Sbde					exitstatus = 127;
72715508Sbde					flushout(&errout);
72815508Sbde					return;
72915508Sbde				}
73015508Sbde				if (cmdentry.u.index != BLTINCMD)
73115508Sbde					break;
73215508Sbde			}
73315508Sbde		}
73415508Sbde	}
73515508Sbde
73616300Spst	/* Fork off a child process if necessary. */
73719173Sbde	if (cmd->ncmd.backgnd
73815508Sbde	 || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
73915508Sbde	    && ((flags & EV_EXIT) == 0 || have_traps()))
74015508Sbde	 || ((flags & EV_BACKCMD) != 0
74115508Sbde	    && (cmdentry.cmdtype != CMDBUILTIN
74215508Sbde		 || cmdentry.u.index == CDCMD
74323393Sbde		 || cmdentry.u.index == DOTCMD
74423393Sbde		 || cmdentry.u.index == EVALCMD))
74523393Sbde	 || (cmdentry.cmdtype == CMDBUILTIN &&
74623393Sbde	    cmdentry.u.index == COMMANDCMD)) {
74732005Sphk		jp = makejob(cmd, 1);
74815508Sbde		mode = cmd->ncmd.backgnd;
74915508Sbde		if (flags & EV_BACKCMD) {
75015508Sbde			mode = FORK_NOJOB;
75140610Sphk			if (pipe(pip) < 0)
75240610Sphk				error("Pipe call failed: %s", strerror(errno));
75315508Sbde		}
75432005Sphk		if (forkshell(jp, cmd, mode) != 0)
75532005Sphk			goto parent;	/* at end of routine */
75616300Spst		if (flags & EV_BACKCMD) {
75719173Sbde			FORCEINTON;
75832005Sphk			close(pip[0]);
75932005Sphk			if (pip[1] != 1) {
76015508Sbde				dup2(pip[1], 1);
76115508Sbde				close(pip[1]);
76232054Sphk			}
76315508Sbde		}
76415508Sbde		flags |= EV_EXIT;
76515508Sbde	}
76615508Sbde
76715508Sbde	/* This is the child process if a fork occurred. */
76848160Sgreen	/* Execute the command. */
76948160Sgreen	if (cmdentry.cmdtype == CMDFUNCTION) {
77015508Sbde#ifdef DEBUG
77148160Sgreen		trputs("Shell function:  ");  trargs(argv);
77232054Sphk#endif
77323393Sbde		redirect(cmd->ncmd.redirect, REDIR_PUSH);
77433690Sphk		saveparam = shellparam;
77516300Spst		shellparam.malloc = 0;
77615508Sbde		shellparam.reset = 1;
77734617Sphk		shellparam.nparam = argc - 1;
77834617Sphk		shellparam.p = argv + 1;
77934617Sphk		shellparam.optnext = NULL;
78034617Sphk		INTOFF;
78134617Sphk		savelocalvars = localvars;
78234617Sphk		localvars = NULL;
78334617Sphk		reffunc(cmdentry.u.func);
78434617Sphk		savehandler = handler;
78534617Sphk		if (setjmp(jmploc.loc)) {
78634617Sphk			if (exception == EXSHELLPROC)
78734617Sphk				freeparam(&saveparam);
78834617Sphk			else {
78934617Sphk				freeparam(&shellparam);
79034617Sphk				shellparam = saveparam;
79134617Sphk			}
79234617Sphk			unreffunc(cmdentry.u.func);
79334617Sphk			poplocalvars();
79434617Sphk			localvars = savelocalvars;
79534617Sphk			funcnest--;
79634617Sphk			handler = savehandler;
79734617Sphk			longjmp(handler->loc, 1);
79834617Sphk		}
79934617Sphk		handler = &jmploc;
80034617Sphk		funcnest++;
80147592Sphk		INTON;
80240610Sphk		for (sp = varlist.list ; sp ; sp = sp->next)
80340610Sphk			mklocal(sp->text);
80433690Sphk		exitstatus = oexitstatus;
80534617Sphk		if (flags & EV_TESTED)
80634617Sphk			evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
8074Srgrimes		else
8084Srgrimes			evaltree(getfuncnode(cmdentry.u.func), 0);
8092913Sache		INTOFF;
81041787Smckay		unreffunc(cmdentry.u.func);
81141787Smckay		poplocalvars();
8122913Sache		localvars = savelocalvars;
8132913Sache		freeparam(&shellparam);
8143185Ssos		shellparam = saveparam;
8154Srgrimes		handler = savehandler;
8162913Sache		funcnest--;
8172913Sache		popredir();
8182913Sache		INTON;
8192913Sache		if (evalskip == SKIPFUNC) {
82033690Sphk			evalskip = 0;
8214Srgrimes			skipcount = 0;
82232850Sphk		}
82332850Sphk		if (flags & EV_EXIT)
82433690Sphk			exitshell(exitstatus);
82533690Sphk	} else if (cmdentry.cmdtype == CMDBUILTIN) {
82633690Sphk#ifdef DEBUG
82732850Sphk		trputs("builtin command:  ");  trargs(argv);
82832850Sphk#endif
8291390Ssos		mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
83041787Smckay		if (flags == EV_BACKCMD) {
8319202Srgrimes			memout.nleft = 0;
8322913Sache			memout.nextc = memout.buf;
8334Srgrimes			memout.bufsize = 64;
83441787Smckay			mode |= REDIR_BACKQ;
83541787Smckay		}
8362913Sache		savecmdname = commandname;
8374Srgrimes		savetopfile = getcurrentfile();
8382913Sache		cmdenviron = varlist.list;
8393355Sache		e = -1;
84041787Smckay		savehandler = handler;
8413355Sache		if (setjmp(jmploc.loc)) {
8423355Sache			e = exception;
8432913Sache			exitstatus = (e == EXINT)? SIGINT+128 : 2;
8443355Sache			goto cmddone;
8453355Sache		}
8463355Sache		handler = &jmploc;
8472913Sache		redirect(cmd->ncmd.redirect, mode);
84841787Smckay		if (cmdentry.special)
84941787Smckay			listsetvar(cmdenviron);
85041787Smckay		commandname = argv[0];
85141787Smckay		argptr = argv + 1;
8522913Sache		nextopt_optptr = NULL;		/* initialize nextopt */
85341787Smckay		builtin_flags = flags;
8542913Sache		exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
8552913Sache		flushall();
85641787Smckaycmddone:
8572913Sache		cmdenviron = NULL;
8582913Sache		out1 = &output;
8592913Sache		out2 = &errout;
8602913Sache		freestdout();
86141787Smckay		if (e != EXSHELLPROC) {
86241787Smckay			commandname = savecmdname;
8631390Ssos			if (flags & EV_EXIT) {
86415054Sache				exitshell(exitstatus);
8654Srgrimes			}
86634961Sphk		}
86733690Sphk		handler = savehandler;
86833690Sphk		if (e != -1) {
86933690Sphk			if ((e != EXERROR && e != EXEXEC)
87033690Sphk			    || cmdentry.special)
87133690Sphk				exraise(e);
87233690Sphk			popfilesupto(savetopfile);
87333690Sphk			FORCEINTON;
87433690Sphk		}
8752913Sache		if (cmdentry.u.index != EXECCMD)
8762913Sache			popredir();
8772913Sache		if (flags == EV_BACKCMD) {
87841787Smckay			backcmd->buf = memout.buf;
87941787Smckay			backcmd->nleft = memout.nextc - memout.buf;
8804Srgrimes			memout.buf = NULL;
8814Srgrimes		}
8824Srgrimes	} else {
88341787Smckay#ifdef DEBUG
8844Srgrimes		trputs("normal command:  ");  trargs(argv);
8854180Sbde#endif
8864180Sbde		redirect(cmd->ncmd.redirect, 0);
8874Srgrimes		for (sp = varlist.list ; sp ; sp = sp->next)
8882913Sache			setvareq(sp->text, VEXPORT|VSTACK);
88911872Sphk		envp = environment();
8904Srgrimes		shellexec(argv, envp, pathval(), cmdentry.u.index);
8913366Sache		/*NOTREACHED*/
8923366Sache	}
8933366Sache	goto out;
8942913Sache
89534961Sphkparent:	/* parent process gets here (if we forked) */
8962913Sache	if (mode == 0) {	/* argument to fork */
8974Srgrimes		INTOFF;
8985291Sbde		exitstatus = waitforjob(jp, &realstatus);
8992913Sache		INTON;
9004Srgrimes		if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) {
90141787Smckay			evalskip = SKIPBREAK;
9021390Ssos			skipcount = loopnest;
90315054Sache		}
9042913Sache	} else if (mode == 2) {
90513445Sphk		backcmd->fd = pip[0];
90613445Sphk		close(pip[1]);
90713445Sphk		backcmd->jp = jp;
9082913Sache	}
90941787Smckay
9102913Sacheout:
91113350Sache	if (lastarg)
91213350Sache		setvar("_", lastarg, 0);
91313350Sache	if (do_clearcmdentry)
91413350Sache		clearcmdentry(0);
9152913Sache	popstackmark(&smark);
9162913Sache}
91713453Sache
91813350Sache
91913445Sphk
9203355Sache/*
92113402Sbde * Search for a command.  This is called before we fork so that the
92213402Sbde * location of the command will be available in the parent as well as
9232913Sache * the child.  The check for "goodname" is an overly conservative
92413402Sbde * check that the name will not be subject to expansion.
92513402Sbde */
92613402Sbde
92713402SbdeSTATIC void
92813402Sbdeprehash(union node *n)
92913402Sbde{
93013402Sbde	struct cmdentry entry;
93113402Sbde
93213445Sphk	if (n && n->type == NCMD && n->ncmd.args)
93313445Sphk		if (goodname(n->ncmd.args->narg.text))
9342913Sache			find_command(n->ncmd.args->narg.text, &entry, 0,
9355291Sbde				     pathval());
93615345Snate}
9374Srgrimes
9384Srgrimes
93927560Sfsmp
9404Srgrimes/*
9415291Sbde * Builtin commands.  Builtin commands whose functions are closely
9424Srgrimes * tied to evaluation are implemented here.
9435291Sbde */
9445291Sbde
9454Srgrimes/*
9465291Sbde * No command given, or a bltin command with no arguments.
94726949Sfsmp */
94834571Stegge
94945900Speterint
95025164Speterbltincmd(int argc __unused, char **argv __unused)
9514Srgrimes{
95215345Snate	/*
95315345Snate	 * Preserve exitstatus of a previous possible redirection
95415345Snate	 * as POSIX mandates
95515345Snate	 */
95615345Snate	return exitstatus;
95715345Snate}
95815345Snate
95915345Snate
96015345Snate/*
96115345Snate * Handle break and continue commands.  Break, continue, and return are
96215345Snate * all handled by setting the evalskip flag.  The evaluation routines
96315345Snate * above all check this flag, and if it is set they start skipping
96415345Snate * commands rather than executing them.  The variable skipcount is
96515345Snate * the number of loops to break/continue, or the number of function
9664Srgrimes * levels to return.  (The latter is always 1.)  It should probably
9675291Sbde * be an error to break out of more loops than exist, but it isn't
96826949Sfsmp * in the standard shell so we don't make it one here.
96927563Sfsmp */
97038888Stegge
97134571Steggeint
97234571Steggebreakcmd(int argc, char **argv)
97334571Stegge{
97434571Stegge	int n = argc > 1 ? number(argv[1]) : 1;
97534571Stegge
97634571Stegge	if (n > loopnest)
97734571Stegge		n = loopnest;
97834571Stegge	if (n > 0) {
97934571Stegge		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
98034571Stegge		skipcount = n;
98134571Stegge	}
98227563Sfsmp	return 0;
98327563Sfsmp}
98445897Speter
98545897Speter/*
98634571Stegge * The `command' command.
98734571Stegge */
98827696Sfsmpint
98927563Sfsmpcommandcmd(int argc, char **argv)
99045897Speter{
99145897Speter	static char stdpath[] = _PATH_STDPATH;
9924Srgrimes	struct jmploc loc, *old;
99327696Sfsmp	struct strlist *sp;
99425164Speter	char *path;
99526949Sfsmp	int ch;
9965291Sbde	int cmd = -1;
9975291Sbde
9985291Sbde	for (sp = cmdenviron; sp ; sp = sp->next)
99915345Snate		setvareq(sp->text, VEXPORT|VSTACK);
100015345Snate	path = pathval();
100115345Snate
100215345Snate	optind = optreset = 1;
10035291Sbde	opterr = 0;
10045291Sbde	while ((ch = getopt(argc, argv, "pvV")) != -1) {
10055291Sbde		switch (ch) {
100627520Sfsmp		case 'p':
100726949Sfsmp			path = stdpath;
100838888Stegge			break;
100927520Sfsmp		case 'v':
101027563Sfsmp			cmd = TYPECMD_SMALLV;
101128487Sfsmp			break;
101245897Speter		case 'V':
101345897Speter			cmd = TYPECMD_BIGV;
101428487Sfsmp			break;
101527616Sfsmp		case '?':
101627616Sfsmp		default:
101727616Sfsmp			error("unknown option: -%c", optopt);
10182074Swollman		}
101927616Sfsmp	}
102027520Sfsmp	argc -= optind;
102115345Snate	argv += optind;
102234571Stegge
102334571Stegge	if (cmd != -1) {
102434571Stegge		if (argc != 1)
102534571Stegge			error("wrong number of arguments");
102634571Stegge		return typecmd_impl(2, argv - 1, cmd, path);
102734571Stegge	}
102835035Stegge	if (argc != 0) {
102934571Stegge		old = handler;
103034571Stegge		handler = &loc;
103134571Stegge		if (setjmp(handler->loc) == 0)
103234571Stegge			shellexec(argv, environment(), path, 0);
103334571Stegge		handler = old;
103434571Stegge		if (exception == EXEXEC)
103534571Stegge			exit(exerrno);
103634571Stegge		exraise(exception);
103745897Speter	}
103834571Stegge
103934571Stegge	/*
104034571Stegge	 * Do nothing successfully if no command was specified;
104134571Stegge	 * ksh also does this.
104234571Stegge	 */
104334571Stegge	exit(0);
104445897Speter}
104545897Speter
104634571Stegge
104734571Stegge/*
104834571Stegge * The return command.
104934571Stegge */
105034571Stegge
105134571Steggeint
105234571Steggereturncmd(int argc, char **argv)
105334571Stegge{
105434571Stegge	int ret = argc > 1 ? number(argv[1]) : oexitstatus;
105534571Stegge
10564Srgrimes	if (funcnest) {
10574Srgrimes		evalskip = SKIPFUNC;
105834571Stegge		skipcount = 1;
105934571Stegge	} else {
106034571Stegge		/* skip the rest of the file */
106134571Stegge		evalskip = SKIPFILE;
106234571Stegge		skipcount = 1;
106334571Stegge	}
106434571Stegge	return ret;
106534571Stegge}
106634571Stegge
106734571Stegge
106834571Steggeint
106934571Steggefalsecmd(int argc __unused, char **argv __unused)
107034571Stegge{
107134571Stegge	return 1;
107234571Stegge}
107334571Stegge
107434571Stegge
107534571Steggeint
107634571Steggetruecmd(int argc __unused, char **argv __unused)
107734571Stegge{
107834571Stegge	return 0;
107934571Stegge}
108034571Stegge
108134571Stegge
108234571Steggeint
108334571Steggeexeccmd(int argc, char **argv)
108434571Stegge{
108534571Stegge	if (argc > 1) {
108634571Stegge		struct strlist *sp;
108734571Stegge
108834571Stegge		iflag = 0;		/* exit on error */
10894Srgrimes		mflag = 0;
10901549Srgrimes		optschanged();
10911549Srgrimes		for (sp = cmdenviron; sp ; sp = sp->next)
10925291Sbde			setvareq(sp->text, VEXPORT|VSTACK);
10932074Swollman		shellexec(argv + 1, environment(), pathval(), 0);
10945291Sbde
10952074Swollman	}
10965291Sbde	return 0;
10971549Srgrimes}
109815508Sbde
109915508Sbde
110015508Sbdeint
110115508Sbdetimescmd(int argc __unused, char **argv __unused)
110215508Sbde{
110315508Sbde	struct rusage ru;
110415508Sbde	long shumins, shsmins, chumins, chsmins;
110515508Sbde	double shusecs, shssecs, chusecs, chssecs;
110615508Sbde
110715508Sbde	if (getrusage(RUSAGE_SELF, &ru) < 0)
110815508Sbde		return 1;
110915508Sbde	shumins = ru.ru_utime.tv_sec / 60;
111048888Sbde	shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
111117353Sbde	shsmins = ru.ru_stime.tv_sec / 60;
111233690Sphk	shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
111315508Sbde	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
111415508Sbde		return 1;
111540610Sphk	chumins = ru.ru_utime.tv_sec / 60;
111646054Sphk	chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
111715508Sbde	chsmins = ru.ru_stime.tv_sec / 60;
111815508Sbde	chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
111915508Sbde	out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
112015508Sbde	    shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
112115508Sbde	return 0;
112248888Sbde}
112315508Sbde