xref: /netbsd-src/bin/sh/eval.c (revision 1c3b098334123732efc3e38acf3c8a83909e00b2)
1*1c3b0983Skre /*	$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
38a45947b2Schristos static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
3949f0ad86Scgd #else
40*1c3b0983Skre __RCSID("$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $");
41b5f33fc1Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4435fbf8ddStron #include <stdbool.h>
45e314f958Sdsl #include <stdlib.h>
4607bae7edSchristos #include <signal.h>
471aafd1a4Sagc #include <stdio.h>
48f87bc150Schristos #include <string.h>
49eb956cbdSchristos #include <errno.h>
500e82f4ebSdsl #include <limits.h>
5107bae7edSchristos #include <unistd.h>
5200a6315eSdsl #include <sys/fcntl.h>
537792ef1aSchristos #include <sys/stat.h>
54c02b3bbdSchristos #include <sys/times.h>
550923f4beSrafal #include <sys/param.h>
56edcb4544Schristos #include <sys/types.h>
57edcb4544Schristos #include <sys/wait.h>
58e314f958Sdsl #include <sys/sysctl.h>
59edcb4544Schristos 
6061f28255Scgd /*
6161f28255Scgd  * Evaluate a command.
6261f28255Scgd  */
6361f28255Scgd 
6461f28255Scgd #include "shell.h"
6561f28255Scgd #include "nodes.h"
6661f28255Scgd #include "syntax.h"
6761f28255Scgd #include "expand.h"
6861f28255Scgd #include "parser.h"
6961f28255Scgd #include "jobs.h"
7061f28255Scgd #include "eval.h"
7161f28255Scgd #include "builtins.h"
7261f28255Scgd #include "options.h"
7361f28255Scgd #include "exec.h"
7461f28255Scgd #include "redir.h"
7561f28255Scgd #include "input.h"
7661f28255Scgd #include "output.h"
7761f28255Scgd #include "trap.h"
7861f28255Scgd #include "var.h"
7961f28255Scgd #include "memalloc.h"
8061f28255Scgd #include "error.h"
8107bae7edSchristos #include "show.h"
8261f28255Scgd #include "mystring.h"
836f482334Schristos #include "main.h"
847accaec4Schristos #ifndef SMALL
85bc8cfb7dSchristos #include "nodenames.h"
8637ed7877Sjtc #include "myhistedit.h"
87242337bcScgd #endif
8861f28255Scgd 
8961f28255Scgd 
9013fc5c1bSkre STATIC struct skipsave s_k_i_p;
9113fc5c1bSkre #define	evalskip	(s_k_i_p.state)
9213fc5c1bSkre #define	skipcount	(s_k_i_p.count)
9313fc5c1bSkre 
9401f35fccSchristos STATIC int loopnest;		/* current loop nesting level */
9501f35fccSchristos STATIC int funcnest;		/* depth of function calls */
961847bab5Schristos STATIC int builtin_flags;	/* evalcommand flags for builtins */
9701f35fccSchristos /*
9801f35fccSchristos  * Base function nesting level inside a dot command.  Set to 0 initially
9901f35fccSchristos  * and to (funcnest + 1) before every dot command to enable
10001f35fccSchristos  *   1) detection of being in a file sourced by a dot command and
10101f35fccSchristos  *   2) counting of function nesting in that file for the implementation
10201f35fccSchristos  *      of the return command.
10301f35fccSchristos  * The value is reset to its previous value after the dot command.
10401f35fccSchristos  */
10501f35fccSchristos STATIC int dot_funcnest;
10661f28255Scgd 
10761f28255Scgd 
1084498b1feSmatt const char *commandname;
10961f28255Scgd struct strlist *cmdenviron;
11061f28255Scgd int exitstatus;			/* exit status of last command */
111c02b3bbdSchristos int back_exitstatus;		/* exit status of backquoted command */
11261f28255Scgd 
11361f28255Scgd 
114c02b3bbdSchristos STATIC void evalloop(union node *, int);
115c02b3bbdSchristos STATIC void evalfor(union node *, int);
116c02b3bbdSchristos STATIC void evalcase(union node *, int);
117c02b3bbdSchristos STATIC void evalsubshell(union node *, int);
118c02b3bbdSchristos STATIC void expredir(union node *);
1198a9a9619Skre STATIC void evalredir(union node *, int);
120c02b3bbdSchristos STATIC void evalpipe(union node *);
121c02b3bbdSchristos STATIC void evalcommand(union node *, int, struct backcmd *);
122c02b3bbdSchristos STATIC void prehash(union node *);
12361f28255Scgd 
12401f35fccSchristos STATIC char *find_dot_file(char *);
12561f28255Scgd 
12661f28255Scgd /*
12761f28255Scgd  * Called to reset things after an exception.
12861f28255Scgd  */
12961f28255Scgd 
13061f28255Scgd #ifdef mkinit
13161f28255Scgd INCLUDE "eval.h"
13261f28255Scgd 
13361f28255Scgd RESET {
13401f35fccSchristos 	reset_eval();
13561f28255Scgd }
13661f28255Scgd 
13761f28255Scgd SHELLPROC {
13861f28255Scgd 	exitstatus = 0;
13961f28255Scgd }
14061f28255Scgd #endif
14161f28255Scgd 
14201f35fccSchristos void
14301f35fccSchristos reset_eval(void)
14401f35fccSchristos {
14501f35fccSchristos 	evalskip = SKIPNONE;
14601f35fccSchristos 	dot_funcnest = 0;
14701f35fccSchristos 	loopnest = 0;
14801f35fccSchristos 	funcnest = 0;
14901f35fccSchristos }
15001f35fccSchristos 
15100a6315eSdsl static int
15200a6315eSdsl sh_pipe(int fds[2])
15300a6315eSdsl {
15400a6315eSdsl 	int nfd;
15500a6315eSdsl 
15600a6315eSdsl 	if (pipe(fds))
15700a6315eSdsl 		return -1;
15800a6315eSdsl 
15900a6315eSdsl 	if (fds[0] < 3) {
16000a6315eSdsl 		nfd = fcntl(fds[0], F_DUPFD, 3);
16100a6315eSdsl 		if (nfd != -1) {
16200a6315eSdsl 			close(fds[0]);
16300a6315eSdsl 			fds[0] = nfd;
16400a6315eSdsl 		}
16500a6315eSdsl 	}
16600a6315eSdsl 
16700a6315eSdsl 	if (fds[1] < 3) {
16800a6315eSdsl 		nfd = fcntl(fds[1], F_DUPFD, 3);
16900a6315eSdsl 		if (nfd != -1) {
17000a6315eSdsl 			close(fds[1]);
17100a6315eSdsl 			fds[1] = nfd;
17200a6315eSdsl 		}
17300a6315eSdsl 	}
17400a6315eSdsl 	return 0;
17500a6315eSdsl }
17661f28255Scgd 
17761f28255Scgd 
17861f28255Scgd /*
17922b6bc1fSmsaitoh  * The eval command.
18061f28255Scgd  */
18161f28255Scgd 
1825dad1439Scgd int
183c02b3bbdSchristos evalcmd(int argc, char **argv)
18461f28255Scgd {
18561f28255Scgd 	char *p;
18661f28255Scgd 	char *concat;
18761f28255Scgd 	char **ap;
18861f28255Scgd 
18961f28255Scgd 	if (argc > 1) {
19061f28255Scgd 		p = argv[1];
19161f28255Scgd 		if (argc > 2) {
19261f28255Scgd 			STARTSTACKSTR(concat);
19361f28255Scgd 			ap = argv + 2;
19461f28255Scgd 			for (;;) {
19561f28255Scgd 				while (*p)
19661f28255Scgd 					STPUTC(*p++, concat);
19761f28255Scgd 				if ((p = *ap++) == NULL)
19861f28255Scgd 					break;
19961f28255Scgd 				STPUTC(' ', concat);
20061f28255Scgd 			}
20161f28255Scgd 			STPUTC('\0', concat);
20261f28255Scgd 			p = grabstackstr(concat);
20361f28255Scgd 		}
2041847bab5Schristos 		evalstring(p, builtin_flags & EV_TESTED);
205b8d196a5Skre 	} else
206b8d196a5Skre 		exitstatus = 0;
20761f28255Scgd 	return exitstatus;
20861f28255Scgd }
20961f28255Scgd 
21061f28255Scgd 
21161f28255Scgd /*
21261f28255Scgd  * Execute a command or commands contained in a string.
21361f28255Scgd  */
21461f28255Scgd 
21561f28255Scgd void
21650a5715bSkre evalstring(const char *s, int flag)
21761f28255Scgd {
21861f28255Scgd 	union node *n;
21961f28255Scgd 	struct stackmark smark;
2208a9a9619Skre 	int last;
2218a9a9619Skre 	int any;
2228a9a9619Skre 
2238a9a9619Skre 	last = flag & EV_EXIT;
2248a9a9619Skre 	flag &= ~EV_EXIT;
22561f28255Scgd 
22661f28255Scgd 	setstackmark(&smark);
227727a69dcSkre 	setinputstring(s, 1, line_number);
2286f482334Schristos 
2298a9a9619Skre 	any = 0;	/* to determine if exitstatus will have been set */
23061f28255Scgd 	while ((n = parsecmd(0)) != NEOF) {
23120013177Skre 		XTRACE(DBG_EVAL, ("evalstring: "), showtree(n));
2328a9a9619Skre 		if (n && nflag == 0) {
2338a9a9619Skre 			if (last && at_eof())
2348a9a9619Skre 				evaltree(n, flag | EV_EXIT);
2358a9a9619Skre 			else
236120267c3Skre 				evaltree(n, flag);
2378a9a9619Skre 			any = 1;
238c43d5e9fSkre 			if (evalskip)
239c43d5e9fSkre 				break;
2408a9a9619Skre 		}
24170696c01Skre 		rststackmark(&smark);
24261f28255Scgd 	}
24361f28255Scgd 	popfile();
24461f28255Scgd 	popstackmark(&smark);
2458a9a9619Skre 	if (!any)
2468a9a9619Skre 		exitstatus = 0;
2478a9a9619Skre 	if (last)
2488a9a9619Skre 		exraise(EXEXIT);
24961f28255Scgd }
25061f28255Scgd 
25161f28255Scgd 
25261f28255Scgd 
25361f28255Scgd /*
25461f28255Scgd  * Evaluate a parse tree.  The value is left in the global variable
25561f28255Scgd  * exitstatus.
25661f28255Scgd  */
25761f28255Scgd 
25861f28255Scgd void
259c02b3bbdSchristos evaltree(union node *n, int flags)
26061f28255Scgd {
26135fbf8ddStron 	bool do_etest;
2625e63d1ceSkre 	int sflags = flags & ~EV_EXIT;
2638a9a9619Skre 	union node *next;
2648a9a9619Skre 	struct stackmark smark;
26535fbf8ddStron 
26635fbf8ddStron 	do_etest = false;
267a584b40fSchristos 	if (n == NULL || nflag) {
26820013177Skre 		VTRACE(DBG_EVAL, ("evaltree(%s) called\n",
26920013177Skre 		    n == NULL ? "NULL" : "-n"));
270a584b40fSchristos 		if (nflag == 0)
27137ed7877Sjtc 			exitstatus = 0;
2728a9a9619Skre 		goto out2;
27361f28255Scgd 	}
2748a9a9619Skre 
2758a9a9619Skre 	setstackmark(&smark);
2768a9a9619Skre 	do {
2777accaec4Schristos #ifndef SMALL
27837ed7877Sjtc 		displayhist = 1; /* show history substitutions done with fc */
279e3c63ad9Scgd #endif
2808a9a9619Skre 		next = NULL;
28120013177Skre 		CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n",
282bc8cfb7dSchristos 		    getpid(), n, NODETYPENAME(n->type), n->type, flags));
2834a370dceSkre 	/*
28413fc5c1bSkre 		if (n->type != NCMD && traps_invalid)
28513fc5c1bSkre 			free_traps();
2864a370dceSkre 	*/
28761f28255Scgd 		switch (n->type) {
28861f28255Scgd 		case NSEMI:
2898a9a9619Skre 			evaltree(n->nbinary.ch1, sflags);
290a584b40fSchristos 			if (nflag || evalskip)
2918a9a9619Skre 				goto out1;
2928a9a9619Skre 			next = n->nbinary.ch2;
29361f28255Scgd 			break;
29461f28255Scgd 		case NAND:
295120267c3Skre 			evaltree(n->nbinary.ch1, EV_TESTED);
296a584b40fSchristos 			if (nflag || evalskip || exitstatus != 0)
2978a9a9619Skre 				goto out1;
2988a9a9619Skre 			next = n->nbinary.ch2;
29961f28255Scgd 			break;
30061f28255Scgd 		case NOR:
301120267c3Skre 			evaltree(n->nbinary.ch1, EV_TESTED);
302a584b40fSchristos 			if (nflag || evalskip || exitstatus == 0)
3038a9a9619Skre 				goto out1;
3048a9a9619Skre 			next = n->nbinary.ch2;
30561f28255Scgd 			break;
30661f28255Scgd 		case NREDIR:
3074a370dceSkre 			if (traps_invalid)
3084a370dceSkre 				free_traps();
3098a9a9619Skre 			evalredir(n, flags);
31061f28255Scgd 			break;
31161f28255Scgd 		case NSUBSHELL:
312120267c3Skre 			evalsubshell(n, flags);
3139cae530bStron 			do_etest = !(flags & EV_TESTED);
31461f28255Scgd 			break;
31561f28255Scgd 		case NBACKGND:
3164a370dceSkre 			if (traps_invalid)
3174a370dceSkre 				free_traps();
318120267c3Skre 			evalsubshell(n, flags);
31961f28255Scgd 			break;
32061f28255Scgd 		case NIF: {
3214a370dceSkre 			if (traps_invalid)
3224a370dceSkre 				free_traps();
323120267c3Skre 			evaltree(n->nif.test, EV_TESTED);
324a584b40fSchristos 			if (nflag || evalskip)
3258a9a9619Skre 				goto out1;
326b09ffc42Schristos 			if (exitstatus == 0)
3278a9a9619Skre 				next = n->nif.ifpart;
3282e197048Schristos 			else if (n->nif.elsepart)
3298a9a9619Skre 				next = n->nif.elsepart;
3300b9acf16Spk 			else
3310b9acf16Spk 				exitstatus = 0;
33261f28255Scgd 			break;
33361f28255Scgd 		}
33461f28255Scgd 		case NWHILE:
33561f28255Scgd 		case NUNTIL:
3364a370dceSkre 			if (traps_invalid)
3374a370dceSkre 				free_traps();
3385e63d1ceSkre 			evalloop(n, sflags);
33961f28255Scgd 			break;
34061f28255Scgd 		case NFOR:
3414a370dceSkre 			if (traps_invalid)
3424a370dceSkre 				free_traps();
3435e63d1ceSkre 			evalfor(n, sflags);
34461f28255Scgd 			break;
34561f28255Scgd 		case NCASE:
3464a370dceSkre 			if (traps_invalid)
3474a370dceSkre 				free_traps();
3485e63d1ceSkre 			evalcase(n, sflags);
34961f28255Scgd 			break;
35061f28255Scgd 		case NDEFUN:
3514a370dceSkre 			if (traps_invalid)
3524a370dceSkre 				free_traps();
3538a9a9619Skre 			CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n",
3548a9a9619Skre 			    n->narg.text, n->narg.lineno,
3558a9a9619Skre 			    fnline1 ? " LINENO=1" : ""));
356727a69dcSkre 			defun(n->narg.text, n->narg.next, n->narg.lineno);
35761f28255Scgd 			exitstatus = 0;
35861f28255Scgd 			break;
35937ed7877Sjtc 		case NNOT:
360120267c3Skre 			evaltree(n->nnot.com, EV_TESTED);
36137ed7877Sjtc 			exitstatus = !exitstatus;
36237ed7877Sjtc 			break;
363aa563ca4Skre 		case NDNOT:
364120267c3Skre 			evaltree(n->nnot.com, EV_TESTED);
365aa563ca4Skre 			if (exitstatus != 0)
366aa563ca4Skre 				exitstatus = 1;
367aa563ca4Skre 			break;
36861f28255Scgd 		case NPIPE:
3694a370dceSkre 			if (traps_invalid)
3704a370dceSkre 				free_traps();
3714783843fSchristos 			evalpipe(n);
3729cae530bStron 			do_etest = !(flags & EV_TESTED);
37361f28255Scgd 			break;
37461f28255Scgd 		case NCMD:
3759f61b804Splunky 			evalcommand(n, flags, NULL);
3769cae530bStron 			do_etest = !(flags & EV_TESTED);
37761f28255Scgd 			break;
37861f28255Scgd 		default:
379a584b40fSchristos #ifdef NODETYPENAME
3808a9a9619Skre 			out1fmt("Node type = %d(%s)\n",
3818a9a9619Skre 				n->type, NODETYPENAME(n->type));
382a584b40fSchristos #else
38361f28255Scgd 			out1fmt("Node type = %d\n", n->type);
384a584b40fSchristos #endif
38561f28255Scgd 			flushout(&output);
38661f28255Scgd 			break;
38761f28255Scgd 		}
3888a9a9619Skre 		n = next;
38970696c01Skre 		rststackmark(&smark);
3908a9a9619Skre 	} while(n != NULL);
3918a9a9619Skre  out1:
3928a9a9619Skre 	popstackmark(&smark);
3938a9a9619Skre  out2:
39461f28255Scgd 	if (pendingsigs)
39561f28255Scgd 		dotrap();
3968a9a9619Skre 	if (eflag && exitstatus != 0 && do_etest)
39761f28255Scgd 		exitshell(exitstatus);
3988a9a9619Skre 	if (flags & EV_EXIT)
3998a9a9619Skre 		exraise(EXEXIT);
40061f28255Scgd }
40161f28255Scgd 
40261f28255Scgd 
40361f28255Scgd STATIC void
404c02b3bbdSchristos evalloop(union node *n, int flags)
40561f28255Scgd {
40661f28255Scgd 	int status;
40761f28255Scgd 
40861f28255Scgd 	loopnest++;
40961f28255Scgd 	status = 0;
410bc8cfb7dSchristos 
41120013177Skre 	CTRACE(DBG_EVAL,  ("evalloop %s:", NODETYPENAME(n->type)));
41220013177Skre 	VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1));
41320013177Skre 	VXTRACE(DBG_EVAL, ("evalloop    do: "), showtree(n->nbinary.ch2));
41420013177Skre 	VTRACE(DBG_EVAL,  ("evalloop  done\n"));
41520013177Skre 	CTRACE(DBG_EVAL,  ("\n"));
416bc8cfb7dSchristos 
41761f28255Scgd 	for (;;) {
418120267c3Skre 		evaltree(n->nbinary.ch1, EV_TESTED);
419a584b40fSchristos 		if (nflag)
420a584b40fSchristos 			break;
42161f28255Scgd 		if (evalskip) {
42261f28255Scgd  skipping:		if (evalskip == SKIPCONT && --skipcount <= 0) {
42301f35fccSchristos 				evalskip = SKIPNONE;
42461f28255Scgd 				continue;
42561f28255Scgd 			}
42661f28255Scgd 			if (evalskip == SKIPBREAK && --skipcount <= 0)
42701f35fccSchristos 				evalskip = SKIPNONE;
428c531b568Skre 			if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
429c531b568Skre 				status = exitstatus;
43061f28255Scgd 			break;
43161f28255Scgd 		}
43261f28255Scgd 		if (n->type == NWHILE) {
43361f28255Scgd 			if (exitstatus != 0)
43461f28255Scgd 				break;
43561f28255Scgd 		} else {
43661f28255Scgd 			if (exitstatus == 0)
43761f28255Scgd 				break;
43861f28255Scgd 		}
439120267c3Skre 		evaltree(n->nbinary.ch2, flags & EV_TESTED);
44061f28255Scgd 		status = exitstatus;
44161f28255Scgd 		if (evalskip)
44261f28255Scgd 			goto skipping;
44361f28255Scgd 	}
44461f28255Scgd 	loopnest--;
44561f28255Scgd 	exitstatus = status;
44661f28255Scgd }
44761f28255Scgd 
44861f28255Scgd 
44961f28255Scgd 
45061f28255Scgd STATIC void
451c02b3bbdSchristos evalfor(union node *n, int flags)
45261f28255Scgd {
45361f28255Scgd 	struct arglist arglist;
45461f28255Scgd 	union node *argp;
45561f28255Scgd 	struct strlist *sp;
45661f28255Scgd 	struct stackmark smark;
457a584b40fSchristos 	int status;
458a584b40fSchristos 
459a584b40fSchristos 	status = nflag ? exitstatus : 0;
46061f28255Scgd 
46161f28255Scgd 	setstackmark(&smark);
46261f28255Scgd 	arglist.lastp = &arglist.list;
46361f28255Scgd 	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
464c6cbc16dSdsl 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
46561f28255Scgd 		if (evalskip)
46661f28255Scgd 			goto out;
46761f28255Scgd 	}
46861f28255Scgd 	*arglist.lastp = NULL;
46961f28255Scgd 
47061f28255Scgd 	loopnest++;
47161f28255Scgd 	for (sp = arglist.list ; sp ; sp = sp->next) {
472a41dbcb0Skre 		line_number = n->nfor.lineno;
473a3881786Skre 		if (xflag) {
474ffc64c63Skre 			outxstr(expandstr(ps4val(), line_number));
475ffc64c63Skre 			outxstr("for ");
476ffc64c63Skre 			outxstr(n->nfor.var);
477ffc64c63Skre 			outxc('=');
478ffc64c63Skre 			outxshstr(sp->text);
479ffc64c63Skre 			outxc('\n');
480ffc64c63Skre 			flushout(outx);
481a3881786Skre 		}
482a3881786Skre 
48361f28255Scgd 		setvar(n->nfor.var, sp->text, 0);
484120267c3Skre 		evaltree(n->nfor.body, flags & EV_TESTED);
485c02b3bbdSchristos 		status = exitstatus;
486a584b40fSchristos 		if (nflag)
487a584b40fSchristos 			break;
48861f28255Scgd 		if (evalskip) {
48961f28255Scgd 			if (evalskip == SKIPCONT && --skipcount <= 0) {
49001f35fccSchristos 				evalskip = SKIPNONE;
49161f28255Scgd 				continue;
49261f28255Scgd 			}
49361f28255Scgd 			if (evalskip == SKIPBREAK && --skipcount <= 0)
49401f35fccSchristos 				evalskip = SKIPNONE;
49561f28255Scgd 			break;
49661f28255Scgd 		}
49761f28255Scgd 	}
49861f28255Scgd 	loopnest--;
499c02b3bbdSchristos 	exitstatus = status;
50061f28255Scgd  out:
50161f28255Scgd 	popstackmark(&smark);
50261f28255Scgd }
50361f28255Scgd 
50461f28255Scgd 
50561f28255Scgd 
50661f28255Scgd STATIC void
507c02b3bbdSchristos evalcase(union node *n, int flags)
50861f28255Scgd {
5097d41ae4eSkre 	union node *cp, *ncp;
51061f28255Scgd 	union node *patp;
51161f28255Scgd 	struct arglist arglist;
51261f28255Scgd 	struct stackmark smark;
513c02b3bbdSchristos 	int status = 0;
51461f28255Scgd 
51561f28255Scgd 	setstackmark(&smark);
51661f28255Scgd 	arglist.lastp = &arglist.list;
517727a69dcSkre 	line_number = n->ncase.lineno;
51837ed7877Sjtc 	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
51961f28255Scgd 	for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
52061f28255Scgd 		for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
521727a69dcSkre 			line_number = patp->narg.lineno;
52261f28255Scgd 			if (casematch(patp, arglist.list->text)) {
5237d41ae4eSkre 				while (cp != NULL && evalskip == 0 &&
5247d41ae4eSkre 				    nflag == 0) {
5257d41ae4eSkre 					if (cp->type == NCLISTCONT)
5267d41ae4eSkre 						ncp = cp->nclist.next;
5277d41ae4eSkre 					else
5287d41ae4eSkre 						ncp = NULL;
529727a69dcSkre 					line_number = cp->nclist.lineno;
530120267c3Skre 					evaltree(cp->nclist.body, flags);
531c02b3bbdSchristos 					status = exitstatus;
5327d41ae4eSkre 					cp = ncp;
53361f28255Scgd 				}
53461f28255Scgd 				goto out;
53561f28255Scgd 			}
53661f28255Scgd 		}
53761f28255Scgd 	}
53861f28255Scgd  out:
539c02b3bbdSchristos 	exitstatus = status;
54061f28255Scgd 	popstackmark(&smark);
54161f28255Scgd }
54261f28255Scgd 
54361f28255Scgd 
54461f28255Scgd 
54561f28255Scgd /*
54661f28255Scgd  * Kick off a subshell to evaluate a tree.
54761f28255Scgd  */
54861f28255Scgd 
54961f28255Scgd STATIC void
550c02b3bbdSchristos evalsubshell(union node *n, int flags)
55161f28255Scgd {
5528a9a9619Skre 	struct job *jp= NULL;
55361f28255Scgd 	int backgnd = (n->type == NBACKGND);
55461f28255Scgd 
55561f28255Scgd 	expredir(n->nredir.redirect);
5563b297678Skre 	if (xflag && n->nredir.redirect) {
5573b297678Skre 		union node *rn;
5583b297678Skre 
559ffc64c63Skre 		outxstr(expandstr(ps4val(), line_number));
560ffc64c63Skre 		outxstr("using redirections:");
5613b297678Skre 		for (rn = n->nredir.redirect; rn; rn = rn->nfile.next)
562ffc64c63Skre 			(void) outredir(outx, rn, ' ');
5638a9a9619Skre 		outxstr(" do subshell ("/*)*/);
5648a9a9619Skre 		if (backgnd)
5658a9a9619Skre 			outxstr(/*(*/") &");
5668a9a9619Skre 		outxc('\n');
567ffc64c63Skre 		flushout(outx);
5683b297678Skre 	}
569e8999de4Skre 	INTOFF;
5708821fc2cSkre 	if ((!backgnd && flags & EV_EXIT && !have_traps() && !anyjobs()) ||
5718a9a9619Skre 	    forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) {
57261f28255Scgd 		if (backgnd)
57361f28255Scgd 			flags &=~ EV_TESTED;
574e8999de4Skre 		INTON;
575a6dacc22Skre 		redirect(n->nredir.redirect, REDIR_KEEP);
5768a9a9619Skre 		evaltree(n->nredir.n, flags | EV_EXIT);   /* never returns */
577aec4b6e2Skre 	} else if (backgnd) {
578aec4b6e2Skre 		jobstarted(jp);
579e8999de4Skre 		exitstatus = 0;
580aec4b6e2Skre 	} else
5818a9a9619Skre 		exitstatus = waitforjob(jp);
58261f28255Scgd 	INTON;
5838a9a9619Skre 
5843b297678Skre 	if (!backgnd && xflag && n->nredir.redirect) {
585ffc64c63Skre 		outxstr(expandstr(ps4val(), line_number));
5868a9a9619Skre 		outxstr(/*(*/") done subshell\n");
587ffc64c63Skre 		flushout(outx);
5883b297678Skre 	}
58961f28255Scgd }
59061f28255Scgd 
59161f28255Scgd 
59261f28255Scgd 
59361f28255Scgd /*
59461f28255Scgd  * Compute the names of the files in a redirection list.
59561f28255Scgd  */
59661f28255Scgd 
59761f28255Scgd STATIC void
598c02b3bbdSchristos expredir(union node *n)
59961f28255Scgd {
60048250187Stls 	union node *redir;
60161f28255Scgd 
60261f28255Scgd 	for (redir = n ; redir ; redir = redir->nfile.next) {
60361f28255Scgd 		struct arglist fn;
6041fad4bb6Schristos 
60561f28255Scgd 		fn.lastp = &fn.list;
606918ce04fSjtc 		switch (redir->type) {
6076e50d7a8Schristos 		case NFROMTO:
608918ce04fSjtc 		case NFROM:
609918ce04fSjtc 		case NTO:
610f629aa28Schristos 		case NCLOBBER:
611918ce04fSjtc 		case NAPPEND:
61237ed7877Sjtc 			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
61361f28255Scgd 			redir->nfile.expfname = fn.list->text;
614918ce04fSjtc 			break;
615918ce04fSjtc 		case NFROMFD:
616918ce04fSjtc 		case NTOFD:
617918ce04fSjtc 			if (redir->ndup.vname) {
6183892a5b0Skre 				expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
619918ce04fSjtc 				fixredir(redir, fn.list->text, 1);
620918ce04fSjtc 			}
621918ce04fSjtc 			break;
62216d85571Skre 		case NHERE:
62316d85571Skre 			redir->nhere.text = redir->nhere.doc->narg.text;
62416d85571Skre 			break;
62516d85571Skre 		case NXHERE:
62616d85571Skre 			redir->nhere.text = expandhere(redir->nhere.doc);
62716d85571Skre 			break;
62861f28255Scgd 		}
62961f28255Scgd 	}
63061f28255Scgd }
63161f28255Scgd 
6328a9a9619Skre /*
6338a9a9619Skre  * Perform redirections for a compound command, and then do it (and restore)
6348a9a9619Skre  */
6358a9a9619Skre STATIC void
6368a9a9619Skre evalredir(union node *n, int flags)
6378a9a9619Skre {
6388a9a9619Skre 	struct jmploc jmploc;
6398a9a9619Skre 	struct jmploc * const savehandler = handler;
6408a9a9619Skre 	volatile int in_redirect = 1;
6418a9a9619Skre 	const char * volatile PS4 = NULL;
6428a9a9619Skre 
6438a9a9619Skre 	expredir(n->nredir.redirect);
6448a9a9619Skre 
6458a9a9619Skre 	if (xflag && n->nredir.redirect) {
6468a9a9619Skre 		union node *rn;
6478a9a9619Skre 
6488a9a9619Skre 		outxstr(PS4 = expandstr(ps4val(), line_number));
6498a9a9619Skre 		outxstr("using redirections:");
6508a9a9619Skre 		for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next)
6518a9a9619Skre 			(void) outredir(outx, rn, ' ');
6528a9a9619Skre 		outxstr(" do {\n");	/* } */
6538a9a9619Skre 		flushout(outx);
6548a9a9619Skre 	}
6558a9a9619Skre 
6568a9a9619Skre 	if (setjmp(jmploc.loc)) {
6578a9a9619Skre 		int e;
6588a9a9619Skre 
6598a9a9619Skre 		handler = savehandler;
6608a9a9619Skre 		e = exception;
661*1c3b0983Skre 		popredir(POPREDIR_UNDO);
6620db5b60cSkre 		if (PS4 != NULL) {
6638a9a9619Skre 			outxstr(PS4);
6648a9a9619Skre 			/* { */ outxstr("} failed\n");
6658a9a9619Skre 			flushout(outx);
6668a9a9619Skre 		}
6678a9a9619Skre 		if (e == EXERROR || e == EXEXEC) {
6688a9a9619Skre 			if (in_redirect) {
6698a9a9619Skre 				exitstatus = 2;
6708a9a9619Skre 				return;
6718a9a9619Skre 			}
6728a9a9619Skre 		}
6738a9a9619Skre 		longjmp(handler->loc, 1);
6748a9a9619Skre 	} else {
6758a9a9619Skre 		INTOFF;
6768a9a9619Skre 		handler = &jmploc;
6778a9a9619Skre 		redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP);
6788a9a9619Skre 		in_redirect = 0;
6798a9a9619Skre 		INTON;
6808a9a9619Skre 		evaltree(n->nredir.n, flags);
6818a9a9619Skre 	}
6828a9a9619Skre 	INTOFF;
6838a9a9619Skre 	handler = savehandler;
684*1c3b0983Skre 	popredir(POPREDIR_UNDO);
6858a9a9619Skre 	INTON;
6868a9a9619Skre 
6870db5b60cSkre 	if (PS4 != NULL) {
6888a9a9619Skre 		outxstr(PS4);
6898a9a9619Skre 		/* { */ outxstr("} done\n");
6908a9a9619Skre 		flushout(outx);
6918a9a9619Skre 	}
6928a9a9619Skre }
69361f28255Scgd 
69461f28255Scgd 
69561f28255Scgd /*
69661f28255Scgd  * Evaluate a pipeline.  All the processes in the pipeline are children
69761f28255Scgd  * of the process creating the pipeline.  (This differs from some versions
69861f28255Scgd  * of the shell, which make the last process in a pipeline the parent
69961f28255Scgd  * of all the rest.)
70061f28255Scgd  */
70161f28255Scgd 
70261f28255Scgd STATIC void
703c02b3bbdSchristos evalpipe(union node *n)
70461f28255Scgd {
70561f28255Scgd 	struct job *jp;
70661f28255Scgd 	struct nodelist *lp;
70761f28255Scgd 	int pipelen;
70861f28255Scgd 	int prevfd;
70961f28255Scgd 	int pip[2];
71061f28255Scgd 
71120013177Skre 	CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n));
71261f28255Scgd 	pipelen = 0;
71361f28255Scgd 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
71461f28255Scgd 		pipelen++;
71561f28255Scgd 	INTOFF;
71661f28255Scgd 	jp = makejob(n, pipelen);
71761f28255Scgd 	prevfd = -1;
71861f28255Scgd 	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
71961f28255Scgd 		prehash(lp->n);
72061f28255Scgd 		pip[1] = -1;
72161f28255Scgd 		if (lp->next) {
72200a6315eSdsl 			if (sh_pipe(pip) < 0) {
723e80f354dSchristos 				if (prevfd >= 0)
72461f28255Scgd 					close(prevfd);
7258a9a9619Skre 				error("Pipe call failed: %s", strerror(errno));
72661f28255Scgd 			}
72761f28255Scgd 		}
7288a9a9619Skre 		if (forkshell(jp, lp->n,
7298a9a9619Skre 		    n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
73061f28255Scgd 			INTON;
7311fad4bb6Schristos 			if (prevfd > 0)
7321fad4bb6Schristos 				movefd(prevfd, 0);
73361f28255Scgd 			if (pip[1] >= 0) {
73461f28255Scgd 				close(pip[0]);
7351fad4bb6Schristos 				movefd(pip[1], 1);
73661f28255Scgd 			}
7374783843fSchristos 			evaltree(lp->n, EV_EXIT);
73861f28255Scgd 		}
73961f28255Scgd 		if (prevfd >= 0)
74061f28255Scgd 			close(prevfd);
74161f28255Scgd 		prevfd = pip[0];
74261f28255Scgd 		close(pip[1]);
74361f28255Scgd 	}
74461f28255Scgd 	if (n->npipe.backgnd == 0) {
74561f28255Scgd 		exitstatus = waitforjob(jp);
74620013177Skre 		CTRACE(DBG_EVAL, ("evalpipe:  job done exit status %d\n",
74720013177Skre 		    exitstatus));
748aec4b6e2Skre 	} else {
749aec4b6e2Skre 		jobstarted(jp);
750a584b40fSchristos 		exitstatus = 0;
751aec4b6e2Skre 	}
7522aa6ebd4Smycroft 	INTON;
75361f28255Scgd }
75461f28255Scgd 
75561f28255Scgd 
75661f28255Scgd 
75761f28255Scgd /*
75861f28255Scgd  * Execute a command inside back quotes.  If it's a builtin command, we
75961f28255Scgd  * want to save its output in a block obtained from malloc.  Otherwise
76061f28255Scgd  * we fork off a subprocess and get the output of the command via a pipe.
76161f28255Scgd  * Should be called with interrupts off.
76261f28255Scgd  */
76361f28255Scgd 
76461f28255Scgd void
765c02b3bbdSchristos evalbackcmd(union node *n, struct backcmd *result)
76661f28255Scgd {
76761f28255Scgd 	int pip[2];
76861f28255Scgd 	struct job *jp;
7698a9a9619Skre 	struct stackmark smark;		/* unnecessary (because we fork) */
77061f28255Scgd 
77161f28255Scgd 	result->fd = -1;
77261f28255Scgd 	result->buf = NULL;
77361f28255Scgd 	result->nleft = 0;
77461f28255Scgd 	result->jp = NULL;
7758a9a9619Skre 
7768a9a9619Skre 	if (nflag || n == NULL)
77737ed7877Sjtc 		goto out;
7788a9a9619Skre 
7798a9a9619Skre 	setstackmark(&smark);
7808a9a9619Skre 
78102048e84Schristos #ifdef notyet
78202048e84Schristos 	/*
78302048e84Schristos 	 * For now we disable executing builtins in the same
78402048e84Schristos 	 * context as the shell, because we are not keeping
78502048e84Schristos 	 * enough state to recover from changes that are
78602048e84Schristos 	 * supposed only to affect subshells. eg. echo "`cd /`"
78702048e84Schristos 	 */
78837ed7877Sjtc 	if (n->type == NCMD) {
7898a9a9619Skre 		exitstatus = oexitstatus;	/* XXX o... no longer exists */
7904783843fSchristos 		evalcommand(n, EV_BACKCMD, result);
79102048e84Schristos 	} else
79202048e84Schristos #endif
79302048e84Schristos 	{
794d84d3616Smycroft 		INTOFF;
79500a6315eSdsl 		if (sh_pipe(pip) < 0)
79661f28255Scgd 			error("Pipe call failed");
79761f28255Scgd 		jp = makejob(n, 1);
7984783843fSchristos 		if (forkshell(jp, n, FORK_NOJOB) == 0) {
79961f28255Scgd 			FORCEINTON;
80061f28255Scgd 			close(pip[0]);
8011fad4bb6Schristos 			movefd(pip[1], 1);
8024783843fSchristos 			evaltree(n, EV_EXIT);
803c02b3bbdSchristos 			/* NOTREACHED */
80461f28255Scgd 		}
80561f28255Scgd 		close(pip[1]);
80661f28255Scgd 		result->fd = pip[0];
80761f28255Scgd 		result->jp = jp;
808d84d3616Smycroft 		INTON;
80961f28255Scgd 	}
81061f28255Scgd 	popstackmark(&smark);
8118a9a9619Skre  out:
81220013177Skre 	CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
81361f28255Scgd 		result->fd, result->buf, result->nleft, result->jp));
81461f28255Scgd }
81561f28255Scgd 
816e2f17f9aSkre const char *
817e314f958Sdsl syspath(void)
818e314f958Sdsl {
819e314f958Sdsl 	static char *sys_path = NULL;
820e314f958Sdsl 	static int mib[] = {CTL_USER, USER_CS_PATH};
821027df12dSchristos 	static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
82253ed48f3Sagc 	size_t len;
823e314f958Sdsl 
824e314f958Sdsl 	if (sys_path == NULL) {
825e314f958Sdsl 		if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
826e314f958Sdsl 		    (sys_path = ckmalloc(len + 5)) != NULL &&
827e314f958Sdsl 		    sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
828e314f958Sdsl 			memcpy(sys_path, "PATH=", 5);
829e314f958Sdsl 		} else {
830e314f958Sdsl 			ckfree(sys_path);
831e314f958Sdsl 			/* something to keep things happy */
832027df12dSchristos 			sys_path = def_path;
833e314f958Sdsl 		}
834e314f958Sdsl 	}
835e314f958Sdsl 	return sys_path;
836e314f958Sdsl }
837e314f958Sdsl 
838e314f958Sdsl static int
839e314f958Sdsl parse_command_args(int argc, char **argv, int *use_syspath)
840e314f958Sdsl {
841e314f958Sdsl 	int sv_argc = argc;
842e314f958Sdsl 	char *cp, c;
843e314f958Sdsl 
844e314f958Sdsl 	*use_syspath = 0;
845e314f958Sdsl 
846e314f958Sdsl 	for (;;) {
847e314f958Sdsl 		argv++;
848e314f958Sdsl 		if (--argc == 0)
849e314f958Sdsl 			break;
850e314f958Sdsl 		cp = *argv;
851e314f958Sdsl 		if (*cp++ != '-')
852e314f958Sdsl 			break;
853e314f958Sdsl 		if (*cp == '-' && cp[1] == 0) {
854e314f958Sdsl 			argv++;
855e314f958Sdsl 			argc--;
856e314f958Sdsl 			break;
857e314f958Sdsl 		}
858e314f958Sdsl 		while ((c = *cp++)) {
859e314f958Sdsl 			switch (c) {
860e314f958Sdsl 			case 'p':
861e314f958Sdsl 				*use_syspath = 1;
862e314f958Sdsl 				break;
863e314f958Sdsl 			default:
864e314f958Sdsl 				/* run 'typecmd' for other options */
865e314f958Sdsl 				return 0;
866e314f958Sdsl 			}
867e314f958Sdsl 		}
868e314f958Sdsl 	}
869e314f958Sdsl 	return sv_argc - argc;
870e314f958Sdsl }
87161f28255Scgd 
872edcb4544Schristos int vforked = 0;
87361f28255Scgd 
87461f28255Scgd /*
87561f28255Scgd  * Execute a simple command.
87661f28255Scgd  */
87761f28255Scgd 
87861f28255Scgd STATIC void
87927293760Schristos evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
88061f28255Scgd {
88161f28255Scgd 	struct stackmark smark;
88261f28255Scgd 	union node *argp;
88361f28255Scgd 	struct arglist arglist;
88461f28255Scgd 	struct arglist varlist;
88527293760Schristos 	volatile int flags = flgs;
88627293760Schristos 	char ** volatile argv;
88727293760Schristos 	volatile int argc;
88861f28255Scgd 	char **envp;
88961f28255Scgd 	int varflag;
89061f28255Scgd 	struct strlist *sp;
89127293760Schristos 	volatile int mode;
89261f28255Scgd 	int pip[2];
89361f28255Scgd 	struct cmdentry cmdentry;
89427293760Schristos 	struct job * volatile jp;
89561f28255Scgd 	struct jmploc jmploc;
896df8cbb18Schristos 	struct jmploc *volatile savehandler = NULL;
8974498b1feSmatt 	const char *volatile savecmdname;
89861f28255Scgd 	volatile struct shparam saveparam;
89961f28255Scgd 	struct localvar *volatile savelocalvars;
900a582e232Skre 	struct parsefile *volatile savetopfile;
90161f28255Scgd 	volatile int e;
90227293760Schristos 	char * volatile lastarg;
90327293760Schristos 	const char * volatile path = pathval();
90450cde64bSlukem 	volatile int temp_path;
905727a69dcSkre 	const int savefuncline = funclinebase;
906727a69dcSkre 	const int savefuncabs = funclineabs;
907d7fa5d2fSkre 	volatile int cmd_flags = 0;
90861f28255Scgd 
909edcb4544Schristos 	vforked = 0;
91061f28255Scgd 	/* First expand the arguments. */
911ffc64c63Skre 	CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags,
912ffc64c63Skre 	    cmd->ncmd.args ? cmd->ncmd.args->narg.text : ""));
91361f28255Scgd 	setstackmark(&smark);
914c02b3bbdSchristos 	back_exitstatus = 0;
915c02b3bbdSchristos 
916727a69dcSkre 	line_number = cmd->ncmd.lineno;
917fd38bbe2Skre 
91861f28255Scgd 	arglist.lastp = &arglist.list;
91961f28255Scgd 	varflag = 1;
920c6cbc16dSdsl 	/* Expand arguments, ignoring the initial 'name=value' ones */
92161f28255Scgd 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
922021ba509Skre 		if (varflag && isassignment(argp->narg.text))
92361f28255Scgd 			continue;
92461f28255Scgd 		varflag = 0;
925021ba509Skre 		line_number = argp->narg.lineno;
926021ba509Skre 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
92761f28255Scgd 	}
92861f28255Scgd 	*arglist.lastp = NULL;
929c02b3bbdSchristos 
93061f28255Scgd 	expredir(cmd->ncmd.redirect);
931c02b3bbdSchristos 
932c6cbc16dSdsl 	/* Now do the initial 'name=value' ones we skipped above */
933c02b3bbdSchristos 	varlist.lastp = &varlist.list;
934c02b3bbdSchristos 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
935727a69dcSkre 		line_number = argp->narg.lineno;
936021ba509Skre 		if (!isassignment(argp->narg.text))
937c02b3bbdSchristos 			break;
938c6d0f408Skre 		/* EXP_CASE handles CTL* chars in expansions properly */
939c6d0f408Skre 		expandarg(argp, &varlist, EXP_VARTILDE | EXP_CASE);
940c02b3bbdSchristos 	}
941c02b3bbdSchristos 	*varlist.lastp = NULL;
942c02b3bbdSchristos 
94361f28255Scgd 	argc = 0;
94461f28255Scgd 	for (sp = arglist.list ; sp ; sp = sp->next)
94561f28255Scgd 		argc++;
94661f28255Scgd 	argv = stalloc(sizeof (char *) * (argc + 1));
94737ed7877Sjtc 
94837ed7877Sjtc 	for (sp = arglist.list ; sp ; sp = sp->next) {
94920013177Skre 		VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text));
95061f28255Scgd 		*argv++ = sp->text;
95137ed7877Sjtc 	}
95261f28255Scgd 	*argv = NULL;
95361f28255Scgd 	lastarg = NULL;
95461f28255Scgd 	if (iflag && funcnest == 0 && argc > 0)
95561f28255Scgd 		lastarg = argv[-1];
95661f28255Scgd 	argv -= argc;
95761f28255Scgd 
95861f28255Scgd 	/* Print the command if xflag is set. */
95961f28255Scgd 	if (xflag) {
960e314f958Sdsl 		char sep = 0;
9613b297678Skre 		union node *rn;
9623b297678Skre 
963ffc64c63Skre 		outxstr(expandstr(ps4val(), line_number));
96461f28255Scgd 		for (sp = varlist.list ; sp ; sp = sp->next) {
965f87bc150Schristos 			char *p;
966f87bc150Schristos 
967e314f958Sdsl 			if (sep != 0)
968ffc64c63Skre 				outxc(sep);
969f87bc150Schristos 
970f87bc150Schristos 			/*
971f87bc150Schristos 			 * The "var=" part should not be quoted, regardless
972f87bc150Schristos 			 * of the value, or it would not represent an
973f87bc150Schristos 			 * assignment, but rather a command
974f87bc150Schristos 			 */
975f87bc150Schristos 			p = strchr(sp->text, '=');
976f87bc150Schristos 			if (p != NULL) {
977f87bc150Schristos 				*p = '\0';	/*XXX*/
978ffc64c63Skre 				outxshstr(sp->text);
979ffc64c63Skre 				outxc('=');
980f87bc150Schristos 				*p++ = '=';	/*XXX*/
981f87bc150Schristos 			} else
982f87bc150Schristos 				p = sp->text;
983ffc64c63Skre 			outxshstr(p);
984e314f958Sdsl 			sep = ' ';
98561f28255Scgd 		}
98661f28255Scgd 		for (sp = arglist.list ; sp ; sp = sp->next) {
987e314f958Sdsl 			if (sep != 0)
988ffc64c63Skre 				outxc(sep);
989ffc64c63Skre 			outxshstr(sp->text);
990e314f958Sdsl 			sep = ' ';
99161f28255Scgd 		}
9923b297678Skre 		for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next)
993ffc64c63Skre 			if (outredir(outx, rn, sep))
9943b297678Skre 				sep = ' ';
995ffc64c63Skre 		outxc('\n');
996ffc64c63Skre 		flushout(outx);
99761f28255Scgd 	}
99861f28255Scgd 
99961f28255Scgd 	/* Now locate the command. */
100061f28255Scgd 	if (argc == 0) {
1001d7fa5d2fSkre 		/*
1002d7fa5d2fSkre 		 * the empty command begins as a normal builtin, and
1003d7fa5d2fSkre 		 * remains that way while redirects are processed, then
1004d7fa5d2fSkre 		 * will become special before we get to doing the
1005d7fa5d2fSkre 		 * var assigns.
1006d7fa5d2fSkre 		 */
1007d7fa5d2fSkre 		cmdentry.cmdtype = CMDBUILTIN;
1008c02b3bbdSchristos 		cmdentry.u.bltin = bltincmd;
1009418b4863Smsaitoh 		VTRACE(DBG_CMDS, ("No command name, assume \"command\"\n"));
101061f28255Scgd 	} else {
1011a45947b2Schristos 		static const char PATH[] = "PATH=";
1012a45947b2Schristos 
1013a45947b2Schristos 		/*
1014a45947b2Schristos 		 * Modify the command lookup path, if a PATH= assignment
1015a45947b2Schristos 		 * is present
1016a45947b2Schristos 		 */
1017a45947b2Schristos 		for (sp = varlist.list; sp; sp = sp->next)
1018a45947b2Schristos 			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
1019a45947b2Schristos 				path = sp->text + sizeof(PATH) - 1;
1020a45947b2Schristos 
1021e314f958Sdsl 		do {
1022e314f958Sdsl 			int argsused, use_syspath;
1023727a69dcSkre 
1024e314f958Sdsl 			find_command(argv[0], &cmdentry, cmd_flags, path);
102539879a1cSkre 			VTRACE(DBG_CMDS, ("Command %s type %d\n", argv[0],
102639879a1cSkre 			    cmdentry.cmdtype));
102716ccf0eeSkre #if 0
102816ccf0eeSkre 			/*
102916ccf0eeSkre 			 * This short circuits all of the processing that
103016ccf0eeSkre 			 * should be done (including processing the
103116ccf0eeSkre 			 * redirects), so just don't ...
103216ccf0eeSkre 			 *
103316ccf0eeSkre 			 * (eventually this whole #if'd block will vanish)
103416ccf0eeSkre 			 */
1035e314f958Sdsl 			if (cmdentry.cmdtype == CMDUNKNOWN) {
1036886c875aSchristos 				exitstatus = 127;
103761f28255Scgd 				flushout(&errout);
1038e314f958Sdsl 				goto out;
103961f28255Scgd 			}
104016ccf0eeSkre #endif
1041e314f958Sdsl 
1042e314f958Sdsl 			/* implement the 'command' builtin here */
1043e314f958Sdsl 			if (cmdentry.cmdtype != CMDBUILTIN ||
1044e314f958Sdsl 			    cmdentry.u.bltin != bltincmd)
104561f28255Scgd 				break;
104639879a1cSkre 			VTRACE(DBG_CMDS, ("Command \"command\"\n"));
1047e314f958Sdsl 			cmd_flags |= DO_NOFUNC;
1048e314f958Sdsl 			argsused = parse_command_args(argc, argv, &use_syspath);
1049e314f958Sdsl 			if (argsused == 0) {
1050765ad10cSkre 				/* use 'type' builtin to display info */
105139879a1cSkre 				VTRACE(DBG_CMDS,
105239879a1cSkre 				    ("Command \"command\" -> \"type\"\n"));
1053e314f958Sdsl 				cmdentry.u.bltin = typecmd;
105461f28255Scgd 				break;
105561f28255Scgd 			}
1056e314f958Sdsl 			argc -= argsused;
1057e314f958Sdsl 			argv += argsused;
1058e314f958Sdsl 			if (use_syspath)
1059e314f958Sdsl 				path = syspath() + 5;
1060e314f958Sdsl 		} while (argc != 0);
1061e314f958Sdsl 		if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
1062e314f958Sdsl 			/* posix mandates that 'command <splbltin>' act as if
1063e314f958Sdsl 			   <splbltin> was a normal builtin */
1064e314f958Sdsl 			cmdentry.cmdtype = CMDBUILTIN;
106561f28255Scgd 	}
106661f28255Scgd 
10676b3f9583Skre 	/*
10686b3f9583Skre 	 * When traps are invalid, we permit the following:
10696b3f9583Skre 	 *	trap
10706b3f9583Skre 	 *	command trap
10716b3f9583Skre 	 *	eval trap
10726b3f9583Skre 	 *	command eval trap
10736b3f9583Skre 	 *	eval command trap
10746b3f9583Skre 	 * without zapping the traps completely, in all other cases we do.
10754a370dceSkre 	 * Function calls also do not zap the traps (but commands they execute
10764a370dceSkre 	 * probably will) - this allows a function like
10774a370dceSkre 	 *	trapstate() { trap -p; }
10784a370dceSkre 	 * called as save_traps=$(trapstate).
10796b3f9583Skre 	 *
10806b3f9583Skre 	 * The test here permits eval "anything" but when evalstring() comes
10816b3f9583Skre 	 * back here again, the "anything" will be validated.
10826b3f9583Skre 	 * This means we can actually do:
10836b3f9583Skre 	 *	eval eval eval command eval eval command trap
10846b3f9583Skre 	 * as long as we end up with just "trap"
10856b3f9583Skre 	 *
10866b3f9583Skre 	 * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN
10876b3f9583Skre 	 *
10886b3f9583Skre 	 * trapcmd() takes care of doing free_traps() if it is needed there.
10896b3f9583Skre 	 */
10906b3f9583Skre 	if (traps_invalid &&
10914a370dceSkre 	    cmdentry.cmdtype != CMDFUNCTION &&
10926b3f9583Skre 	    ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) ||
10936b3f9583Skre 	     (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd)))
109413fc5c1bSkre 		free_traps();
109513fc5c1bSkre 
109661f28255Scgd 	/* Fork off a child process if necessary. */
10976fc2ffa0Skre 	if (cmd->ncmd.backgnd
109816ccf0eeSkre 	  || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
10996fc2ffa0Skre 	     && (have_traps() || (flags & EV_EXIT) == 0))
11006fc2ffa0Skre #ifdef notyet			/* EV_BACKCMD is never set currently */
11016fc2ffa0Skre 			/* this will need more work if/when it gets used */
11026fc2ffa0Skre 	  || ((flags & EV_BACKCMD) != 0
11036fc2ffa0Skre 	     && (cmdentry.cmdtype != CMDBUILTIN
11046fc2ffa0Skre 	         && cmdentry.cmdtype != CMDSPLBLTIN)
1105c02b3bbdSchristos 	       || cmdentry.u.bltin == dotcmd
11066fc2ffa0Skre 	       || cmdentry.u.bltin == evalcmd)
11076fc2ffa0Skre #endif
11086fc2ffa0Skre 	 ) {
110935975338Schristos 		INTOFF;
111061f28255Scgd 		jp = makejob(cmd, 1);
111161f28255Scgd 		mode = cmd->ncmd.backgnd;
111261f28255Scgd 		if (flags & EV_BACKCMD) {
111361f28255Scgd 			mode = FORK_NOJOB;
111400a6315eSdsl 			if (sh_pipe(pip) < 0)
111561f28255Scgd 				error("Pipe call failed");
111661f28255Scgd 		}
1117edcb4544Schristos #ifdef DO_SHAREDVFORK
1118edcb4544Schristos 		/* It is essential that if DO_SHAREDVFORK is defined that the
1119edcb4544Schristos 		 * child's address space is actually shared with the parent as
1120edcb4544Schristos 		 * we rely on this.
1121edcb4544Schristos 		 */
1122a6dacc22Skre 		if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL &&
1123a6dacc22Skre 		    (!cmd->ncmd.backgnd || cmd->ncmd.redirect == NULL)) {
1124edcb4544Schristos 			pid_t	pid;
11251468e9a3Schristos 			int serrno;
1126edcb4544Schristos 
1127edcb4544Schristos 			savelocalvars = localvars;
1128edcb4544Schristos 			localvars = NULL;
1129edcb4544Schristos 			vforked = 1;
11305dd8362aSkre 	VFORK_BLOCK
1131edcb4544Schristos 			switch (pid = vfork()) {
1132edcb4544Schristos 			case -1:
11331468e9a3Schristos 				serrno = errno;
113420013177Skre 				VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n",
113520013177Skre 				    serrno));
1136edcb4544Schristos 				INTON;
11371468e9a3Schristos 				error("Cannot vfork (%s)", strerror(serrno));
1138edcb4544Schristos 				break;
1139edcb4544Schristos 			case 0:
1140edcb4544Schristos 				/* Make sure that exceptions only unwind to
1141edcb4544Schristos 				 * after the vfork(2)
1142edcb4544Schristos 				 */
11435dd8362aSkre 				SHELL_FORKED();
1144edcb4544Schristos 				if (setjmp(jmploc.loc)) {
1145b8bee70dSkre 					VTRACE(DBG_EVAL|DBG_ERRS|
1146b8bee70dSkre 					  DBG_PROCS|DBG_CMDS|DBG_TRAP,
1147b8bee70dSkre 					  ("vfork child exit exception:%d "
1148b8bee70dSkre 					   "exitstatus:%d exerrno:%d\n",
1149b8bee70dSkre 					   exception, exitstatus, exerrno));
1150b8bee70dSkre 
1151edcb4544Schristos 					if (exception == EXSHELLPROC) {
115220013177Skre 						/*
115320013177Skre 						 * We can't progress with the
115420013177Skre 						 * vfork, so, set vforked = 2
115520013177Skre 						 * so the parent knows,
115620013177Skre 						 * and _exit();
1157edcb4544Schristos 						 */
1158edcb4544Schristos 						vforked = 2;
1159edcb4544Schristos 						_exit(0);
1160edcb4544Schristos 					} else {
1161ce53a30dSkre 						_exit(exception == EXEXIT ?
1162ce53a30dSkre 						    exitstatus : exerrno);
1163edcb4544Schristos 					}
1164edcb4544Schristos 				}
1165edcb4544Schristos 				savehandler = handler;
1166edcb4544Schristos 				handler = &jmploc;
1167522d1631Skre 				listmklocal(varlist.list,
1168522d1631Skre 				    VDOEXPORT | VEXPORT | VNOFUNC);
11694783843fSchristos 				forkchild(jp, cmd, mode, vforked);
1170edcb4544Schristos 				break;
1171edcb4544Schristos 			default:
11725dd8362aSkre 				VFORK_UNDO();
117320013177Skre 						/* restore from vfork(2) */
117439879a1cSkre 				CTRACE(DBG_PROCS|DBG_CMDS,
117539879a1cSkre 				    ("parent after vfork - vforked=%d\n",
117639879a1cSkre 				      vforked));
117720013177Skre 				handler = savehandler;
1178edcb4544Schristos 				poplocalvars();
1179edcb4544Schristos 				localvars = savelocalvars;
1180edcb4544Schristos 				if (vforked == 2) {
1181edcb4544Schristos 					vforked = 0;
1182edcb4544Schristos 
1183edcb4544Schristos 					(void)waitpid(pid, NULL, 0);
118420013177Skre 					/*
118520013177Skre 					 * We need to progress in a
118620013177Skre 					 * normal fork fashion
118720013177Skre 					 */
1188edcb4544Schristos 					goto normal_fork;
1189edcb4544Schristos 				}
119020013177Skre 				/*
119120013177Skre 				 * Here the child has left home,
119220013177Skre 				 * getting on with its life, so
119320013177Skre 				 * so must we...
119420013177Skre 				 */
1195edcb4544Schristos 				vforked = 0;
11964783843fSchristos 				forkparent(jp, cmd, mode, pid);
1197edcb4544Schristos 				goto parent;
1198edcb4544Schristos 			}
11995dd8362aSkre 	VFORK_END
1200edcb4544Schristos 		} else {
1201edcb4544Schristos  normal_fork:
1202edcb4544Schristos #endif
12034783843fSchristos 			if (forkshell(jp, cmd, mode) != 0)
120461f28255Scgd 				goto parent;	/* at end of routine */
120539879a1cSkre 			CTRACE(DBG_PROCS|DBG_CMDS, ("Child sets EV_EXIT\n"));
12068a9a9619Skre 			flags |= EV_EXIT;
12076166cc6eSchristos 			FORCEINTON;
1208edcb4544Schristos #ifdef DO_SHAREDVFORK
1209edcb4544Schristos 		}
1210edcb4544Schristos #endif
121161f28255Scgd 		if (flags & EV_BACKCMD) {
1212edcb4544Schristos 			if (!vforked) {
121361f28255Scgd 				FORCEINTON;
1214edcb4544Schristos 			}
121561f28255Scgd 			close(pip[0]);
12161fad4bb6Schristos 			movefd(pip[1], 1);
121761f28255Scgd 		}
121861f28255Scgd 		flags |= EV_EXIT;
121961f28255Scgd 	}
122061f28255Scgd 
122161f28255Scgd 	/* This is the child process if a fork occurred. */
122261f28255Scgd 	/* Execute the command. */
1223e314f958Sdsl 	switch (cmdentry.cmdtype) {
12248a9a9619Skre 		volatile int saved;
122529a26373Skre 		struct funcdef * volatile savefunc;
12265a21c2d7Skre 		const char * volatile savectx;
12278a9a9619Skre 
1228e314f958Sdsl 	case CMDFUNCTION:
1229f07d3f9bSkre 		VXTRACE(DBG_EVAL, ("Shell function%s:  ",vforked?" VF":""),
1230f07d3f9bSkre 		    trargs(argv));
1231*1c3b0983Skre 		redirect(cmd->ncmd.redirect, REDIR_KEEP | (saved =
1232*1c3b0983Skre 			!(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0));
123361f28255Scgd 		saveparam = shellparam;
123461f28255Scgd 		shellparam.malloc = 0;
1235ccce082dSchristos 		shellparam.reset = 1;
123661f28255Scgd 		shellparam.nparam = argc - 1;
123761f28255Scgd 		shellparam.p = argv + 1;
123861f28255Scgd 		shellparam.optnext = NULL;
123961f28255Scgd 		INTOFF;
124061f28255Scgd 		savelocalvars = localvars;
12415a21c2d7Skre 		savectx = currentcontext;
124261f28255Scgd 		localvars = NULL;
124329a26373Skre 		reffunc(savefunc = cmdentry.u.func);
124461f28255Scgd 		INTON;
124561f28255Scgd 		if (setjmp(jmploc.loc)) {
12463d424690Schristos 			if (exception == EXSHELLPROC) {
12473d424690Schristos 				freeparam((volatile struct shparam *)
12483d424690Schristos 				    &saveparam);
12493d424690Schristos 			} else {
125061f28255Scgd 				freeparam(&shellparam);
125161f28255Scgd 				shellparam = saveparam;
125261f28255Scgd 			}
12538a9a9619Skre 			if (saved)
1254*1c3b0983Skre 				popredir(POPREDIR_UNDO);
125529a26373Skre 			unreffunc(savefunc);
125661f28255Scgd 			poplocalvars();
125761f28255Scgd 			localvars = savelocalvars;
1258727a69dcSkre 			funclinebase = savefuncline;
1259727a69dcSkre 			funclineabs = savefuncabs;
12605a21c2d7Skre 			currentcontext = savectx;
126161f28255Scgd 			handler = savehandler;
126261f28255Scgd 			longjmp(handler->loc, 1);
126361f28255Scgd 		}
126461f28255Scgd 		savehandler = handler;
126561f28255Scgd 		handler = &jmploc;
1266727a69dcSkre 		if (cmdentry.u.func) {
1267727a69dcSkre 			if (cmdentry.lno_frel)
1268727a69dcSkre 				funclinebase = cmdentry.lineno - 1;
1269727a69dcSkre 			else
1270727a69dcSkre 				funclinebase = 0;
1271727a69dcSkre 			funclineabs = cmdentry.lineno;
12725a21c2d7Skre 			currentcontext = argv[0];
1273727a69dcSkre 
1274727a69dcSkre 			VTRACE(DBG_EVAL,
1275727a69dcSkre 			  ("function: node: %d '%s' # %d%s; funclinebase=%d\n",
1276c7c0722aSkre 			    getfuncnode(cmdentry.u.func)->type,
1277c7c0722aSkre 			    NODETYPENAME(getfuncnode(cmdentry.u.func)->type),
1278727a69dcSkre 			    cmdentry.lineno, cmdentry.lno_frel?" (=1)":"",
1279727a69dcSkre 			    funclinebase));
1280727a69dcSkre 		}
1281522d1631Skre 		listmklocal(varlist.list, VDOEXPORT | VEXPORT);
1282e314f958Sdsl 		/* stop shell blowing its stack */
1283e314f958Sdsl 		if (++funcnest > 1000)
1284e314f958Sdsl 			error("too many nested function calls");
12858a9a9619Skre 		evaltree(getfuncnode(cmdentry.u.func),
12868a9a9619Skre 		    flags & (EV_TESTED|EV_EXIT));
128761f28255Scgd 		funcnest--;
128861f28255Scgd 		INTOFF;
1289c7c0722aSkre 		unreffunc(cmdentry.u.func);
129061f28255Scgd 		poplocalvars();
129161f28255Scgd 		localvars = savelocalvars;
1292727a69dcSkre 		funclinebase = savefuncline;
1293727a69dcSkre 		funclineabs = savefuncabs;
12945a21c2d7Skre 		currentcontext = savectx;
129561f28255Scgd 		freeparam(&shellparam);
129661f28255Scgd 		shellparam = saveparam;
129761f28255Scgd 		handler = savehandler;
12988a9a9619Skre 		if (saved)
1299*1c3b0983Skre 			popredir(POPREDIR_UNDO);
130061f28255Scgd 		INTON;
130161f28255Scgd 		if (evalskip == SKIPFUNC) {
130201f35fccSchristos 			evalskip = SKIPNONE;
130361f28255Scgd 			skipcount = 0;
130461f28255Scgd 		}
130561f28255Scgd 		if (flags & EV_EXIT)
130661f28255Scgd 			exitshell(exitstatus);
1307e314f958Sdsl 		break;
1308e314f958Sdsl 
1309e314f958Sdsl 	case CMDSPLBLTIN:
1310d7fa5d2fSkre 		VTRACE(DBG_EVAL, ("special "));
1311d7fa5d2fSkre 	case CMDBUILTIN:
1312d7fa5d2fSkre 		VXTRACE(DBG_EVAL, ("builtin command [%d]%s:  ", argc,
1313d7fa5d2fSkre 		    vforked ? " VF" : ""), trargs(argv));
1314*1c3b0983Skre 
1315*1c3b0983Skre 		if (cmdentry.u.bltin == execcmd) {
1316*1c3b0983Skre 			char **ap;
1317*1c3b0983Skre 
1318*1c3b0983Skre 			/*
1319*1c3b0983Skre 			 * Work out how we should process redirections
1320*1c3b0983Skre 			 * on the "exec" command.   We need REDIR_KEEP
1321*1c3b0983Skre 			 * if we must not set close-on-exec, and REDIR_PUSH
1322*1c3b0983Skre 			 * if we need to be able to undo them (in the
1323*1c3b0983Skre 			 * exec command, only on  some kind of error).
1324*1c3b0983Skre 			 *
1325*1c3b0983Skre 			 * Skip "exec" (argv[0]) then examine args.
1326*1c3b0983Skre 			 *
1327*1c3b0983Skre 			 * This must be done manually, as nextopt()
1328*1c3b0983Skre 			 * hasn't been init'd for this command yet.
1329*1c3b0983Skre 			 * And it won't be until after redirections are done.
1330*1c3b0983Skre 			 *
1331*1c3b0983Skre 			 * "exec" currently takes no options (except "--"),
1332*1c3b0983Skre 			 * but might one day, and this needs to keep working,
1333*1c3b0983Skre 			 * so do it, kind of, properly.
1334*1c3b0983Skre 			 *
1335*1c3b0983Skre 			 * Note in the common cases argv[1] will be NULL
1336*1c3b0983Skre 			 * (for exec just setting up redirectons) or will
1337*1c3b0983Skre 			 * not start with a '-' ("exec cmd") so normally
1338*1c3b0983Skre 			 * this loop will either never start or will break
1339*1c3b0983Skre 			 * at the first test of the first iteration.
1340*1c3b0983Skre 			 */
1341*1c3b0983Skre 			for (ap = argv + 1; *ap != NULL; ap++) {
1342*1c3b0983Skre 
1343*1c3b0983Skre 				if (ap[0][0] != '-')
1344*1c3b0983Skre 					break;
1345*1c3b0983Skre 
1346*1c3b0983Skre 				if (ap[0][1] == '\0')	/* "exec -" */
1347*1c3b0983Skre 					break;		/* or continue ?? */
1348*1c3b0983Skre 
1349*1c3b0983Skre 				if (ap[0][1] == '-' && ap[0][2] == '\0') {
1350*1c3b0983Skre 					ap++;
1351*1c3b0983Skre 					break;
1352*1c3b0983Skre 				}
1353*1c3b0983Skre 
1354*1c3b0983Skre #if defined(DUMMY_EXAMPLE_CODE) && 0
1355*1c3b0983Skre 				/*
1356*1c3b0983Skre 				 * if options are added to "exec" then
1357*1c3b0983Skre 				 * any which take an arg (like the common
1358*1c3b0983Skre 				 * in other shells "-a cmdname") need to
1359*1c3b0983Skre 				 * be recognised here, lest "cmdname" be
1360*1c3b0983Skre 				 * thought to be the cmd to exec
1361*1c3b0983Skre 				 */
1362*1c3b0983Skre 
1363*1c3b0983Skre 				for (char *op = ap[0] + 1; *op; op++) {
1364*1c3b0983Skre 					switch (*op) {
1365*1c3b0983Skre 					case 'a':
1366*1c3b0983Skre 					case any others similar:
1367*1c3b0983Skre 						/* options needing an optarg */
1368*1c3b0983Skre 						if (op[1] == '\0' && ap[1])
1369*1c3b0983Skre 							ap++;
1370*1c3b0983Skre 						break;
1371*1c3b0983Skre 
1372*1c3b0983Skre 					default:
1373*1c3b0983Skre 						/* options with no optarg */
1374*1c3b0983Skre 						continue;
1375*1c3b0983Skre 					}
1376*1c3b0983Skre 					break;
1377*1c3b0983Skre 				}
1378*1c3b0983Skre #endif /* DUMMY EXAMPLE CODE */
1379*1c3b0983Skre 			}
1380*1c3b0983Skre 
1381*1c3b0983Skre 			if (*ap != NULL)
1382*1c3b0983Skre 				mode = REDIR_KEEP;	/* exec cmd <... */
1383*1c3b0983Skre 			else
1384*1c3b0983Skre 				mode = 0;		/* exec < .... */
1385*1c3b0983Skre 
1386*1c3b0983Skre 			/*
1387*1c3b0983Skre 			 * always save old fd setup in case of error()
1388*1c3b0983Skre 			 * execcmd() will undo this if no error occurs
1389*1c3b0983Skre 			 * (that is, in the case the shell has not vanished)
1390*1c3b0983Skre 			 */
1391*1c3b0983Skre 			mode |= REDIR_PUSH;
1392*1c3b0983Skre 		} else			/* any builtin execpt "exec" */
1393*1c3b0983Skre 			mode = REDIR_PUSH | REDIR_KEEP;
1394*1c3b0983Skre 
139561f28255Scgd 		if (flags == EV_BACKCMD) {
139661f28255Scgd 			memout.nleft = 0;
139761f28255Scgd 			memout.nextc = memout.buf;
139861f28255Scgd 			memout.bufsize = 64;
139961f28255Scgd 			mode |= REDIR_BACKQ;
140061f28255Scgd 		}
1401*1c3b0983Skre 
1402e314f958Sdsl 		e = -1;
14038693718bSdsl 		savecmdname = commandname;
1404a582e232Skre 		savetopfile = getcurrentfile();
1405a582e232Skre 		savehandler = handler;
14060adfd5e0Sjoerg 		temp_path = 0;
1407*1c3b0983Skre 
1408c02b3bbdSchristos 		if (!setjmp(jmploc.loc)) {
1409a582e232Skre 			handler = &jmploc;
1410a582e232Skre 
141120013177Skre 			/*
141220013177Skre 			 * We need to ensure the command hash table isn't
1413afa63a66Schristos 			 * corrupted by temporary PATH assignments.
1414e314f958Sdsl 			 * However we must ensure the 'local' command works!
1415e314f958Sdsl 			 */
1416e314f958Sdsl 			if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
1417e314f958Sdsl 			    cmdentry.u.bltin == typecmd)) {
1418e314f958Sdsl 				savelocalvars = localvars;
1419e314f958Sdsl 				localvars = 0;
1420e314f958Sdsl 				temp_path = 1;
14210adfd5e0Sjoerg 				mklocal(path - 5 /* PATH= */, 0);
14220adfd5e0Sjoerg 			}
1423c02b3bbdSchristos 			redirect(cmd->ncmd.redirect, mode);
1424c02b3bbdSchristos 
1425d7fa5d2fSkre 			/*
1426d7fa5d2fSkre 			 * the empty command is regarded as a normal
1427d7fa5d2fSkre 			 * builtin for the purposes of redirects, but
1428d7fa5d2fSkre 			 * is a special builtin for var assigns.
1429d7fa5d2fSkre 			 * (unless we are the "command" command.)
1430d7fa5d2fSkre 			 */
1431d7fa5d2fSkre 			if (argc == 0 && !(cmd_flags & DO_NOFUNC))
1432d7fa5d2fSkre 				cmdentry.cmdtype = CMDSPLBLTIN;
1433d7fa5d2fSkre 
1434c02b3bbdSchristos 			/* exec is a special builtin, but needs this list... */
1435c02b3bbdSchristos 			cmdenviron = varlist.list;
1436c02b3bbdSchristos 			/* we must check 'readonly' flag for all builtins */
1437c02b3bbdSchristos 			listsetvar(varlist.list,
1438c02b3bbdSchristos 				cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
143961f28255Scgd 			commandname = argv[0];
1440c02b3bbdSchristos 			/* initialize nextopt */
144161f28255Scgd 			argptr = argv + 1;
1442c02b3bbdSchristos 			optptr = NULL;
1443c02b3bbdSchristos 			/* and getopt */
1444c02b3bbdSchristos 			optreset = 1;
1445c02b3bbdSchristos 			optind = 1;
14461847bab5Schristos 			builtin_flags = flags;
1447b65a5b90Skre 			clr_err(out1);	/* discard previous I/O errors */
1448c02b3bbdSchristos 			exitstatus = cmdentry.u.bltin(argc, argv);
1449c02b3bbdSchristos 		} else {
1450c02b3bbdSchristos 			e = exception;
14518a9a9619Skre 			if (e == EXINT)
14528a9a9619Skre 				exitstatus = SIGINT + 128;
14538a9a9619Skre 			else if (e == EXEXEC)
14548a9a9619Skre 				exitstatus = exerrno;
14558a9a9619Skre 			else if (e != EXEXIT)
14568a9a9619Skre 				exitstatus = 2;
1457c02b3bbdSchristos 		}
1458e314f958Sdsl 		handler = savehandler;
145961f28255Scgd 		flushall();
146061f28255Scgd 		out1 = &output;
146161f28255Scgd 		out2 = &errout;
146261f28255Scgd 		freestdout();
1463e314f958Sdsl 		if (temp_path) {
1464e314f958Sdsl 			poplocalvars();
1465e314f958Sdsl 			localvars = savelocalvars;
1466e314f958Sdsl 		}
146723eb2db1Sthorpej 		cmdenviron = NULL;
146861f28255Scgd 		if (e != EXSHELLPROC) {
146961f28255Scgd 			commandname = savecmdname;
1470ee9e50eaSmycroft 			if (flags & EV_EXIT)
147161f28255Scgd 				exitshell(exitstatus);
147261f28255Scgd 		}
147361f28255Scgd 		if (e != -1) {
1474886c875aSchristos 			if ((e != EXERROR && e != EXEXEC)
1475e314f958Sdsl 			    || cmdentry.cmdtype == CMDSPLBLTIN)
147661f28255Scgd 				exraise(e);
1477a582e232Skre 			popfilesupto(savetopfile);
147861f28255Scgd 			FORCEINTON;
147961f28255Scgd 		}
1480*1c3b0983Skre 
1481c02b3bbdSchristos 		if (cmdentry.u.bltin != execcmd)
1482*1c3b0983Skre 			popredir(POPREDIR_UNDO);
1483*1c3b0983Skre 
148461f28255Scgd 		if (flags == EV_BACKCMD) {
148561f28255Scgd 			backcmd->buf = memout.buf;
148661f28255Scgd 			backcmd->nleft = memout.nextc - memout.buf;
148761f28255Scgd 			memout.buf = NULL;
148861f28255Scgd 		}
1489e314f958Sdsl 		break;
1490e314f958Sdsl 
1491e314f958Sdsl 	default:
1492f07d3f9bSkre 		VXTRACE(DBG_EVAL, ("normal command%s:  ", vforked?" VF":""),
1493f07d3f9bSkre 		    trargs(argv));
14945047abd1Schristos 		redirect(cmd->ncmd.redirect,
14955047abd1Schristos 		    (vforked ? REDIR_VFORK : 0) | REDIR_KEEP);
1496edcb4544Schristos 		if (!vforked)
149761f28255Scgd 			for (sp = varlist.list ; sp ; sp = sp->next)
1498522d1631Skre 				setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK);
149961f28255Scgd 		envp = environment();
1500e314f958Sdsl 		shellexec(argv, envp, path, cmdentry.u.index, vforked);
1501e314f958Sdsl 		break;
150261f28255Scgd 	}
150361f28255Scgd 	goto out;
150461f28255Scgd 
150561f28255Scgd  parent:			/* parent process gets here (if we forked) */
150620013177Skre 
15078b2383fcSchristos 	exitstatus = 0;		/* if not altered just below */
1508c02b3bbdSchristos 	if (mode == FORK_FG) {	/* argument to fork */
150961f28255Scgd 		exitstatus = waitforjob(jp);
1510c02b3bbdSchristos 	} else if (mode == FORK_NOJOB) {
151161f28255Scgd 		backcmd->fd = pip[0];
151261f28255Scgd 		close(pip[1]);
151361f28255Scgd 		backcmd->jp = jp;
1514aec4b6e2Skre 	} else
1515aec4b6e2Skre 		jobstarted(jp);
1516aec4b6e2Skre 
15176166cc6eSchristos 	FORCEINTON;
151861f28255Scgd 
151961f28255Scgd  out:
152061f28255Scgd 	if (lastarg)
15214d69dafdSkre 		/* implement $_ for whatever use that really is */
1522ad0b72d9Skre 		(void) setvarsafe("_", lastarg, VNOERROR);
152361f28255Scgd 	popstackmark(&smark);
152461f28255Scgd }
152561f28255Scgd 
152661f28255Scgd 
152761f28255Scgd /*
152861f28255Scgd  * Search for a command.  This is called before we fork so that the
152961f28255Scgd  * location of the command will be available in the parent as well as
153061f28255Scgd  * the child.  The check for "goodname" is an overly conservative
153161f28255Scgd  * check that the name will not be subject to expansion.
153261f28255Scgd  */
153361f28255Scgd 
153461f28255Scgd STATIC void
1535c02b3bbdSchristos prehash(union node *n)
153661f28255Scgd {
153761f28255Scgd 	struct cmdentry entry;
153861f28255Scgd 
1539da09d1a8Schristos 	if (n && n->type == NCMD && n->ncmd.args)
154008a67300Smycroft 		if (goodname(n->ncmd.args->narg.text))
1541a45947b2Schristos 			find_command(n->ncmd.args->narg.text, &entry, 0,
1542a45947b2Schristos 				     pathval());
154361f28255Scgd }
154461f28255Scgd 
1545404b1d02Skre int
154601f35fccSchristos in_function(void)
154701f35fccSchristos {
154801f35fccSchristos 	return funcnest;
154901f35fccSchristos }
155061f28255Scgd 
1551404b1d02Skre enum skipstate
155201f35fccSchristos current_skipstate(void)
155301f35fccSchristos {
155401f35fccSchristos 	return evalskip;
155501f35fccSchristos }
155601f35fccSchristos 
1557404b1d02Skre void
155813fc5c1bSkre save_skipstate(struct skipsave *p)
155913fc5c1bSkre {
156013fc5c1bSkre 	*p = s_k_i_p;
156113fc5c1bSkre }
156213fc5c1bSkre 
156313fc5c1bSkre void
156413fc5c1bSkre restore_skipstate(const struct skipsave *p)
156513fc5c1bSkre {
156613fc5c1bSkre 	s_k_i_p = *p;
156713fc5c1bSkre }
156813fc5c1bSkre 
156913fc5c1bSkre void
157001f35fccSchristos stop_skipping(void)
157101f35fccSchristos {
157201f35fccSchristos 	evalskip = SKIPNONE;
157301f35fccSchristos 	skipcount = 0;
157401f35fccSchristos }
157561f28255Scgd 
157661f28255Scgd /*
157761f28255Scgd  * Builtin commands.  Builtin commands whose functions are closely
157861f28255Scgd  * tied to evaluation are implemented here.
157961f28255Scgd  */
158061f28255Scgd 
158161f28255Scgd /*
1582e314f958Sdsl  * No command given.
158361f28255Scgd  */
158461f28255Scgd 
15855dad1439Scgd int
1586c02b3bbdSchristos bltincmd(int argc, char **argv)
15875dad1439Scgd {
15882e197048Schristos 	/*
15892e197048Schristos 	 * Preserve exitstatus of a previous possible redirection
15902e197048Schristos 	 * as POSIX mandates
15912e197048Schristos 	 */
1592c02b3bbdSchristos 	return back_exitstatus;
159361f28255Scgd }
159461f28255Scgd 
159561f28255Scgd 
159661f28255Scgd /*
159761f28255Scgd  * Handle break and continue commands.  Break, continue, and return are
159861f28255Scgd  * all handled by setting the evalskip flag.  The evaluation routines
159961f28255Scgd  * above all check this flag, and if it is set they start skipping
160061f28255Scgd  * commands rather than executing them.  The variable skipcount is
160161f28255Scgd  * the number of loops to break/continue, or the number of function
160261f28255Scgd  * levels to return.  (The latter is always 1.)  It should probably
160361f28255Scgd  * be an error to break out of more loops than exist, but it isn't
160461f28255Scgd  * in the standard shell so we don't make it one here.
160561f28255Scgd  */
160661f28255Scgd 
16075dad1439Scgd int
1608c02b3bbdSchristos breakcmd(int argc, char **argv)
16095dad1439Scgd {
161066824391Schristos 	int n = argc > 1 ? number(argv[1]) : 1;
161161f28255Scgd 
1612e0f1e164Skre 	if (n <= 0)
1613e0f1e164Skre 		error("invalid count: %d", n);
161461f28255Scgd 	if (n > loopnest)
161561f28255Scgd 		n = loopnest;
161661f28255Scgd 	if (n > 0) {
161761f28255Scgd 		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
161861f28255Scgd 		skipcount = n;
161961f28255Scgd 	}
162061f28255Scgd 	return 0;
162161f28255Scgd }
162261f28255Scgd 
162301f35fccSchristos int
162401f35fccSchristos dotcmd(int argc, char **argv)
162501f35fccSchristos {
162601f35fccSchristos 	exitstatus = 0;
162701f35fccSchristos 
1628ebc4cf1cSkre 	(void) nextopt(NULL);		/* ignore a leading "--" */
1629ebc4cf1cSkre 
1630ebc4cf1cSkre 	if (*argptr != NULL) {		/* That's what SVR2 does */
163101f35fccSchristos 		char *fullname;
163201f35fccSchristos 		/*
163301f35fccSchristos 		 * dot_funcnest needs to be 0 when not in a dotcmd, so it
163401f35fccSchristos 		 * cannot be restored with (funcnest + 1).
163501f35fccSchristos 		 */
163601f35fccSchristos 		int dot_funcnest_old;
163701f35fccSchristos 		struct stackmark smark;
163801f35fccSchristos 
163901f35fccSchristos 		setstackmark(&smark);
1640ebc4cf1cSkre 		fullname = find_dot_file(*argptr);
164101f35fccSchristos 		setinputfile(fullname, 1);
164201f35fccSchristos 		commandname = fullname;
164301f35fccSchristos 		dot_funcnest_old = dot_funcnest;
164401f35fccSchristos 		dot_funcnest = funcnest + 1;
164501f35fccSchristos 		cmdloop(0);
164601f35fccSchristos 		dot_funcnest = dot_funcnest_old;
164701f35fccSchristos 		popfile();
164801f35fccSchristos 		popstackmark(&smark);
164901f35fccSchristos 	}
165001f35fccSchristos 	return exitstatus;
165101f35fccSchristos }
165201f35fccSchristos 
165301f35fccSchristos /*
16544084f829Skre  * allow dotfile function nesting to be manipulated
16554084f829Skre  * (for read_profile).  This allows profile files to
16564084f829Skre  * be treated as if they were used as '.' commands,
16574084f829Skre  * (approximately) and in particular, for "return" to work.
16584084f829Skre  */
16594084f829Skre int
16604084f829Skre set_dot_funcnest(int new)
16614084f829Skre {
16624084f829Skre 	int rv = dot_funcnest;
16634084f829Skre 
16644084f829Skre 	if (new >= 0)
16654084f829Skre 		dot_funcnest = new;
16664084f829Skre 
16674084f829Skre 	return rv;
16684084f829Skre }
16694084f829Skre 
16704084f829Skre /*
167101f35fccSchristos  * Take commands from a file.  To be compatible we should do a path
167201f35fccSchristos  * search for the file, which is necessary to find sub-commands.
167301f35fccSchristos  */
167401f35fccSchristos 
167501f35fccSchristos STATIC char *
167601f35fccSchristos find_dot_file(char *basename)
167701f35fccSchristos {
167801f35fccSchristos 	char *fullname;
167901f35fccSchristos 	const char *path = pathval();
168001f35fccSchristos 	struct stat statb;
168101f35fccSchristos 
168201f35fccSchristos 	/* don't try this for absolute or relative paths */
16831d680406Skre 	if (strchr(basename, '/')) {
16841d680406Skre 		if (stat(basename, &statb) == 0) {
1685cdb38c6bSkre 			if (S_ISDIR(statb.st_mode))
1686cdb38c6bSkre 				error("%s: is a directory", basename);
1687cdb38c6bSkre 			if (S_ISBLK(statb.st_mode))
1688cdb38c6bSkre 				error("%s: is a block device", basename);
168901f35fccSchristos 			return basename;
16901d680406Skre 		}
16911676135eSkre 	} else while ((fullname = padvance(&path, basename, 1)) != NULL) {
1692cdb38c6bSkre 		if ((stat(fullname, &statb) == 0)) {
1693cdb38c6bSkre 			/* weird format is to ease future code... */
1694cdb38c6bSkre 			if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode))
1695cdb38c6bSkre 				;
1696cdb38c6bSkre #if notyet
1697cdb38c6bSkre 			else if (unreadable()) {
169801f35fccSchristos 				/*
1699cdb38c6bSkre 				 * testing this via st_mode is ugly to get
1700cdb38c6bSkre 				 * correct (and would ignore ACLs).
1701cdb38c6bSkre 				 * better way is just to open the file.
1702cdb38c6bSkre 				 * But doing that here would (currently)
1703cdb38c6bSkre 				 * mean opening the file twice, which
1704cdb38c6bSkre 				 * might not be safe.  So, defer this
1705cdb38c6bSkre 				 * test until code is restructures so
1706cdb38c6bSkre 				 * we can return a fd.   Then we also
1707cdb38c6bSkre 				 * get to fix the mem leak just below...
1708cdb38c6bSkre 				 */
1709cdb38c6bSkre 			}
1710cdb38c6bSkre #endif
1711cdb38c6bSkre 			else {
1712cdb38c6bSkre 				/*
1713cdb38c6bSkre 				 * Don't bother freeing here, since
1714cdb38c6bSkre 				 * it will be freed by the caller.
1715cdb38c6bSkre 				 * XXX no it won't - a bug for later.
171601f35fccSchristos 				 */
171701f35fccSchristos 				return fullname;
171801f35fccSchristos 			}
1719cdb38c6bSkre 		}
172001f35fccSchristos 		stunalloc(fullname);
172101f35fccSchristos 	}
172201f35fccSchristos 
172301f35fccSchristos 	/* not found in the PATH */
172401f35fccSchristos 	error("%s: not found", basename);
172501f35fccSchristos 	/* NOTREACHED */
172601f35fccSchristos }
172701f35fccSchristos 
172801f35fccSchristos 
172961f28255Scgd 
173061f28255Scgd /*
173161f28255Scgd  * The return command.
173201f35fccSchristos  *
173301f35fccSchristos  * Quoth the POSIX standard:
173401f35fccSchristos  *   The return utility shall cause the shell to stop executing the current
173501f35fccSchristos  *   function or dot script. If the shell is not currently executing
173601f35fccSchristos  *   a function or dot script, the results are unspecified.
173701f35fccSchristos  *
173801f35fccSchristos  * As for the unspecified part, there seems to be no de-facto standard: bash
173901f35fccSchristos  * ignores the return with a warning, zsh ignores the return in interactive
174001f35fccSchristos  * mode but seems to liken it to exit in a script.  (checked May 2014)
174101f35fccSchristos  *
174201f35fccSchristos  * We choose to silently ignore the return.  Older versions of this shell
174301f35fccSchristos  * set evalskip to SKIPFILE causing the shell to (indirectly) exit.  This
174401f35fccSchristos  * had at least the problem of circumventing the check for stopped jobs,
174501f35fccSchristos  * which would occur for exit or ^D.
174661f28255Scgd  */
174761f28255Scgd 
17485dad1439Scgd int
1749c02b3bbdSchristos returncmd(int argc, char **argv)
17505dad1439Scgd {
1751c02b3bbdSchristos 	int ret = argc > 1 ? number(argv[1]) : exitstatus;
175261f28255Scgd 
175301f35fccSchristos 	if ((dot_funcnest == 0 && funcnest)
175401f35fccSchristos 	    || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) {
175561f28255Scgd 		evalskip = SKIPFUNC;
175661f28255Scgd 		skipcount = 1;
175701f35fccSchristos 	} else if (dot_funcnest > 0) {
1758f9382bcaSchristos 		evalskip = SKIPFILE;
1759f9382bcaSchristos 		skipcount = 1;
176001f35fccSchristos 	} else {
176101f35fccSchristos 		/* XXX: should a warning be issued? */
176201f35fccSchristos 		ret = 0;
1763f9382bcaSchristos 	}
176401f35fccSchristos 
176501f35fccSchristos 	return ret;
1766f9382bcaSchristos }
176761f28255Scgd 
176861f28255Scgd 
17695dad1439Scgd int
1770c02b3bbdSchristos falsecmd(int argc, char **argv)
17715dad1439Scgd {
1772542ace3bSjtc 	return 1;
1773542ace3bSjtc }
1774542ace3bSjtc 
1775542ace3bSjtc 
17765dad1439Scgd int
1777c02b3bbdSchristos truecmd(int argc, char **argv)
17785dad1439Scgd {
177961f28255Scgd 	return 0;
178061f28255Scgd }
178161f28255Scgd 
178261f28255Scgd 
17835dad1439Scgd int
1784c02b3bbdSchristos execcmd(int argc, char **argv)
17855dad1439Scgd {
1786*1c3b0983Skre 	/*
1787*1c3b0983Skre 	 * BEWARE: if any options are added here, they must
1788*1c3b0983Skre 	 * also be added in evalcommand(), look for "DUMMY_EXAMPLE_CODE"
1789*1c3b0983Skre 	 * for example code for there.   Here the options would be
1790*1c3b0983Skre 	 * processed completely normally.
1791*1c3b0983Skre 	 */
1792*1c3b0983Skre 	(void) nextopt("");		/* ignore a leading "--" */
1793ebc4cf1cSkre 
1794ebc4cf1cSkre 	if (*argptr) {
1795df504509Schristos 		struct strlist *sp;
1796df504509Schristos 
179761f28255Scgd 		iflag = 0;		/* exit on error */
179837ed7877Sjtc 		mflag = 0;
179937ed7877Sjtc 		optschanged();
1800df504509Schristos 		for (sp = cmdenviron; sp; sp = sp->next)
1801522d1631Skre 			setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK);
1802ebc4cf1cSkre 		shellexec(argptr, environment(), pathval(), 0, 0);
1803*1c3b0983Skre 		/* NOTREACHED */
180461f28255Scgd 	}
1805*1c3b0983Skre 	popredir(POPREDIR_PERMANENT);	/* make any redirections permanent */
180661f28255Scgd 	return 0;
180761f28255Scgd }
1808c02b3bbdSchristos 
1809c02b3bbdSchristos static int
1810a69cd45bSitojun conv_time(clock_t ticks, char *seconds, size_t l)
1811c02b3bbdSchristos {
1812c02b3bbdSchristos 	static clock_t tpm = 0;
1813c02b3bbdSchristos 	clock_t mins;
1814c02b3bbdSchristos 	int i;
1815c02b3bbdSchristos 
1816c02b3bbdSchristos 	if (!tpm)
1817c02b3bbdSchristos 		tpm = sysconf(_SC_CLK_TCK) * 60;
1818c02b3bbdSchristos 
1819c02b3bbdSchristos 	mins = ticks / tpm;
1820a69cd45bSitojun 	snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1821c02b3bbdSchristos 
1822c02b3bbdSchristos 	if (seconds[0] == '6' && seconds[1] == '0') {
1823c02b3bbdSchristos 		/* 59.99995 got rounded up... */
1824c02b3bbdSchristos 		mins++;
1825a69cd45bSitojun 		strlcpy(seconds, "0.0", l);
1826c02b3bbdSchristos 		return mins;
1827c02b3bbdSchristos 	}
1828c02b3bbdSchristos 
1829c02b3bbdSchristos 	/* suppress trailing zeros */
1830c02b3bbdSchristos 	i = strlen(seconds) - 1;
1831c02b3bbdSchristos 	for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1832c02b3bbdSchristos 		seconds[i] = 0;
1833c02b3bbdSchristos 	return mins;
1834c02b3bbdSchristos }
1835c02b3bbdSchristos 
1836c02b3bbdSchristos int
1837c02b3bbdSchristos timescmd(int argc, char **argv)
1838c02b3bbdSchristos {
1839c02b3bbdSchristos 	struct tms tms;
1840c02b3bbdSchristos 	int u, s, cu, cs;
1841c02b3bbdSchristos 	char us[8], ss[8], cus[8], css[8];
1842c02b3bbdSchristos 
1843c02b3bbdSchristos 	nextopt("");
1844c02b3bbdSchristos 
1845c02b3bbdSchristos 	times(&tms);
1846c02b3bbdSchristos 
1847a69cd45bSitojun 	u = conv_time(tms.tms_utime, us, sizeof(us));
1848a69cd45bSitojun 	s = conv_time(tms.tms_stime, ss, sizeof(ss));
1849a69cd45bSitojun 	cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1850a69cd45bSitojun 	cs = conv_time(tms.tms_cstime, css, sizeof(css));
1851c02b3bbdSchristos 
1852c02b3bbdSchristos 	outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1853c02b3bbdSchristos 		u, us, s, ss, cu, cus, cs, css);
1854c02b3bbdSchristos 
1855c02b3bbdSchristos 	return 0;
1856c02b3bbdSchristos }
1857