xref: /netbsd-src/bin/sh/exec.c (revision d73cf48c32d7312664aed04e32d6cf018c8b64f2)
1*d73cf48cSkre /*	$NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 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[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
3949f0ad86Scgd #else
40*d73cf48cSkre __RCSID("$NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4407bae7edSchristos #include <sys/types.h>
4507bae7edSchristos #include <sys/stat.h>
46edcb4544Schristos #include <sys/wait.h>
4707bae7edSchristos #include <unistd.h>
4807bae7edSchristos #include <fcntl.h>
4907bae7edSchristos #include <errno.h>
50edcb4544Schristos #include <stdio.h>
5107bae7edSchristos #include <stdlib.h>
5207bae7edSchristos 
5361f28255Scgd /*
5461f28255Scgd  * When commands are first encountered, they are entered in a hash table.
5561f28255Scgd  * This ensures that a full path search will not have to be done for them
5661f28255Scgd  * on each invocation.
5761f28255Scgd  *
5861f28255Scgd  * We should investigate converting to a linear search, even though that
5961f28255Scgd  * would make the command name "hash" a misnomer.
6061f28255Scgd  */
6161f28255Scgd 
6261f28255Scgd #include "shell.h"
6361f28255Scgd #include "main.h"
6461f28255Scgd #include "nodes.h"
6561f28255Scgd #include "parser.h"
6661f28255Scgd #include "redir.h"
6761f28255Scgd #include "eval.h"
6861f28255Scgd #include "exec.h"
6961f28255Scgd #include "builtins.h"
7061f28255Scgd #include "var.h"
7161f28255Scgd #include "options.h"
7261f28255Scgd #include "input.h"
7361f28255Scgd #include "output.h"
7461f28255Scgd #include "syntax.h"
7561f28255Scgd #include "memalloc.h"
7661f28255Scgd #include "error.h"
7761f28255Scgd #include "init.h"
7861f28255Scgd #include "mystring.h"
7907bae7edSchristos #include "show.h"
8037ed7877Sjtc #include "jobs.h"
81680690d3Schristos #include "alias.h"
8261f28255Scgd 
8361f28255Scgd 
8461f28255Scgd #define CMDTABLESIZE 31		/* should be prime */
8561f28255Scgd #define ARB 1			/* actual size determined at run time */
8661f28255Scgd 
8761f28255Scgd 
8861f28255Scgd 
8961f28255Scgd struct tblentry {
9061f28255Scgd 	struct tblentry *next;	/* next entry in hash chain */
9161f28255Scgd 	union param param;	/* definition of builtin function */
9261f28255Scgd 	short cmdtype;		/* index identifying command */
9361f28255Scgd 	char rehash;		/* if set, cd done since entry created */
94727a69dcSkre 	char fn_ln1;		/* for functions, LINENO from 1 */
95727a69dcSkre 	int lineno;		/* for functions abs LINENO of definition */
9661f28255Scgd 	char cmdname[ARB];	/* name of command */
9761f28255Scgd };
9861f28255Scgd 
9961f28255Scgd 
10061f28255Scgd STATIC struct tblentry *cmdtable[CMDTABLESIZE];
10161f28255Scgd STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
1022a9c11ddSchristos int exerrno = 0;			/* Last exec error */
10361f28255Scgd 
10461f28255Scgd 
105c02b3bbdSchristos STATIC void tryexec(char *, char **, char **, int);
106c02b3bbdSchristos STATIC void printentry(struct tblentry *, int);
107ddfe7420Schristos STATIC void addcmdentry(char *, struct cmdentry *);
108c02b3bbdSchristos STATIC void clearcmdentry(int);
109c02b3bbdSchristos STATIC struct tblentry *cmdlookup(const char *, int);
110c02b3bbdSchristos STATIC void delete_cmd_entry(void);
11161f28255Scgd 
112744c8edcSdholland #ifndef BSD
113744c8edcSdholland STATIC void execinterp(char **, char **);
114744c8edcSdholland #endif
115744c8edcSdholland 
11661f28255Scgd 
1174498b1feSmatt extern const char *const parsekwd[];
11861f28255Scgd 
11961f28255Scgd /*
12061f28255Scgd  * Exec a program.  Never returns.  If you change this routine, you may
12161f28255Scgd  * have to change the find_command routine as well.
12261f28255Scgd  */
12361f28255Scgd 
12461f28255Scgd void
shellexec(char ** argv,char ** envp,const char * path,int idx,int vforked)125c02b3bbdSchristos shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
12661f28255Scgd {
12761f28255Scgd 	char *cmdname;
12898b2eb36Skre 	int e, action;
12998b2eb36Skre 	struct stat statb;
13098b2eb36Skre 
13198b2eb36Skre 	action = E_EXEC;
13261f28255Scgd 
13361f28255Scgd 	if (strchr(argv[0], '/') != NULL) {
134edcb4544Schristos 		tryexec(argv[0], argv, envp, vforked);
13561f28255Scgd 		e = errno;
13698b2eb36Skre 		if (e == EACCES && stat(argv[0], &statb) == -1)
13798b2eb36Skre 			action = E_OPEN;
13861f28255Scgd 	} else {
13961f28255Scgd 		e = ENOENT;
1401676135eSkre 		while ((cmdname = padvance(&path, argv[0], 1)) != NULL) {
1413d424690Schristos 			if (--idx < 0 && pathopt == NULL) {
14298b2eb36Skre 				/*
14398b2eb36Skre 				 * tryexec() does not return if it works.
14498b2eb36Skre 				 */
145edcb4544Schristos 				tryexec(cmdname, argv, envp, vforked);
14698b2eb36Skre 				/*
14798b2eb36Skre 				 * If do not already have a meaningful error
14898b2eb36Skre 				 * from earlier in the PATH, examine this one
14998b2eb36Skre 				 * if it is a simple "not found", just keep
15098b2eb36Skre 				 * searching.
15198b2eb36Skre 				 */
15298b2eb36Skre 				if (e == ENOENT &&
15398b2eb36Skre 				    errno != ENOENT && errno != ENOTDIR) {
15498b2eb36Skre 					/*
15598b2eb36Skre 					 * If the error is from permission
15698b2eb36Skre 					 * denied on the path search (a call
15798b2eb36Skre 					 * to stat() also fails) ignore it
15898b2eb36Skre 					 * (just continue with the search)
15998b2eb36Skre 					 * If it is EACCESS and the file exists
16098b2eb36Skre 					 * (the stat succeeds) that means no
16198b2eb36Skre 					 * 'x' perm on the file itself, which
16298b2eb36Skre 					 * is a meaningful error, this will be
16398b2eb36Skre 					 * the one reported if no later PATH
16498b2eb36Skre 					 * element actually succeeds.
16598b2eb36Skre 					 */
16698b2eb36Skre 					if (errno == EACCES) {
16798b2eb36Skre 						if (stat(cmdname, &statb) != -1)
16898b2eb36Skre 							e = EACCES;
16998b2eb36Skre 					} else {
17098b2eb36Skre 						/*
17198b2eb36Skre 						 * any other error we will
17298b2eb36Skre 						 * remember as the significant
17398b2eb36Skre 						 * error
17498b2eb36Skre 						 */
17561f28255Scgd 						e = errno;
17661f28255Scgd 					}
17798b2eb36Skre 				}
17898b2eb36Skre 			}
17961f28255Scgd 			stunalloc(cmdname);
18061f28255Scgd 		}
18161f28255Scgd 	}
1822a9c11ddSchristos 
1832a9c11ddSchristos 	/* Map to POSIX errors */
1842a9c11ddSchristos 	switch (e) {
1859006b741Skre 	case EACCES:	/* particularly this (unless no search perm) */
18698b2eb36Skre 		if (action == E_OPEN) {
1879006b741Skre 			/*
18898b2eb36Skre 			 * this is an EACCES from namei
18998b2eb36Skre 			 * ie: no permission to search the path given
19098b2eb36Skre 			 * rather than an EACCESS from exec
19198b2eb36Skre 			 * ie: no 'x' bit on the file to be executed
1929006b741Skre 			 */
19398b2eb36Skre 			exerrno = 127;
19498b2eb36Skre 			break;
19598b2eb36Skre 		}
19698b2eb36Skre 		/* FALLTHROUGH */
1979006b741Skre 	case EINVAL:	/* also explicitly these */
1989006b741Skre 	case ENOEXEC:
1999006b741Skre 	default:	/* and anything else */
2002a9c11ddSchristos 		exerrno = 126;
2012a9c11ddSchristos 		break;
2029006b741Skre 
2039006b741Skre 	case ENOENT:	/* these are the "pathname lookup failed" errors */
2049006b741Skre 	case ELOOP:
2059006b741Skre 	case ENOTDIR:
2069006b741Skre 	case ENAMETOOLONG:
2072a9c11ddSchristos 		exerrno = 127;
2082a9c11ddSchristos 		break;
2092a9c11ddSchristos 	}
2109006b741Skre 	CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL,
2119006b741Skre 	    ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
212c02b3bbdSchristos 		argv[0], e, vforked, suppressint));
21398b2eb36Skre 	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, action));
214ee9e50eaSmycroft 	/* NOTREACHED */
21561f28255Scgd }
21661f28255Scgd 
21761f28255Scgd 
21861f28255Scgd STATIC void
tryexec(char * cmd,char ** argv,char ** envp,int vforked)219c02b3bbdSchristos tryexec(char *cmd, char **argv, char **envp, int vforked)
22061f28255Scgd {
22161f28255Scgd 	int e;
22207bae7edSchristos #ifndef BSD
22361f28255Scgd 	char *p;
22407bae7edSchristos #endif
22561f28255Scgd 
22661f28255Scgd #ifdef SYSV
22761f28255Scgd 	do {
22861f28255Scgd 		execve(cmd, argv, envp);
22961f28255Scgd 	} while (errno == EINTR);
23061f28255Scgd #else
23161f28255Scgd 	execve(cmd, argv, envp);
23261f28255Scgd #endif
23361f28255Scgd 	e = errno;
23461f28255Scgd 	if (e == ENOEXEC) {
235edcb4544Schristos 		if (vforked) {
236edcb4544Schristos 			/* We are currently vfork(2)ed, so raise an
237edcb4544Schristos 			 * exception, and evalcommand will try again
238edcb4544Schristos 			 * with a normal fork(2).
239edcb4544Schristos 			 */
240edcb4544Schristos 			exraise(EXSHELLPROC);
241edcb4544Schristos 		}
2422554aff2Schristos #ifdef DEBUG
2439006b741Skre 		VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd));
2442554aff2Schristos #endif
24561f28255Scgd 		initshellproc();
24661f28255Scgd 		setinputfile(cmd, 0);
24761f28255Scgd 		commandname = arg0 = savestr(argv[0]);
24861f28255Scgd #ifndef BSD
24961f28255Scgd 		pgetc(); pungetc();		/* fill up input buffer */
25061f28255Scgd 		p = parsenextc;
25161f28255Scgd 		if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
25261f28255Scgd 			argv[0] = cmd;
25361f28255Scgd 			execinterp(argv, envp);
25461f28255Scgd 		}
25561f28255Scgd #endif
25661f28255Scgd 		setparam(argv + 1);
25761f28255Scgd 		exraise(EXSHELLPROC);
25861f28255Scgd 	}
25961f28255Scgd 	errno = e;
26061f28255Scgd }
26161f28255Scgd 
26261f28255Scgd 
26361f28255Scgd #ifndef BSD
26461f28255Scgd /*
26561f28255Scgd  * Execute an interpreter introduced by "#!", for systems where this
26661f28255Scgd  * feature has not been built into the kernel.  If the interpreter is
26761f28255Scgd  * the shell, return (effectively ignoring the "#!").  If the execution
26861f28255Scgd  * of the interpreter fails, exit.
26961f28255Scgd  *
27061f28255Scgd  * This code peeks inside the input buffer in order to avoid actually
27161f28255Scgd  * reading any input.  It would benefit from a rewrite.
27261f28255Scgd  */
27361f28255Scgd 
27461f28255Scgd #define NEWARGS 5
27561f28255Scgd 
27661f28255Scgd STATIC void
execinterp(char ** argv,char ** envp)277c02b3bbdSchristos execinterp(char **argv, char **envp)
27861f28255Scgd {
27961f28255Scgd 	int n;
28061f28255Scgd 	char *inp;
28161f28255Scgd 	char *outp;
28261f28255Scgd 	char c;
28361f28255Scgd 	char *p;
28461f28255Scgd 	char **ap;
28561f28255Scgd 	char *newargs[NEWARGS];
28661f28255Scgd 	int i;
28761f28255Scgd 	char **ap2;
28861f28255Scgd 	char **new;
28961f28255Scgd 
29061f28255Scgd 	n = parsenleft - 2;
29161f28255Scgd 	inp = parsenextc + 2;
29261f28255Scgd 	ap = newargs;
29361f28255Scgd 	for (;;) {
29461f28255Scgd 		while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
29561f28255Scgd 			inp++;
29661f28255Scgd 		if (n < 0)
29761f28255Scgd 			goto bad;
29861f28255Scgd 		if ((c = *inp++) == '\n')
29961f28255Scgd 			break;
30061f28255Scgd 		if (ap == &newargs[NEWARGS])
30161f28255Scgd bad:		  error("Bad #! line");
30261f28255Scgd 		STARTSTACKSTR(outp);
30361f28255Scgd 		do {
30461f28255Scgd 			STPUTC(c, outp);
30561f28255Scgd 		} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
30661f28255Scgd 		STPUTC('\0', outp);
30761f28255Scgd 		n++, inp--;
30861f28255Scgd 		*ap++ = grabstackstr(outp);
30961f28255Scgd 	}
31061f28255Scgd 	if (ap == newargs + 1) {	/* if no args, maybe no exec is needed */
31161f28255Scgd 		p = newargs[0];
31261f28255Scgd 		for (;;) {
31361f28255Scgd 			if (equal(p, "sh") || equal(p, "ash")) {
31461f28255Scgd 				return;
31561f28255Scgd 			}
31661f28255Scgd 			while (*p != '/') {
31761f28255Scgd 				if (*p == '\0')
31861f28255Scgd 					goto break2;
31961f28255Scgd 				p++;
32061f28255Scgd 			}
32161f28255Scgd 			p++;
32261f28255Scgd 		}
32361f28255Scgd break2:;
32461f28255Scgd 	}
32561f28255Scgd 	i = (char *)ap - (char *)newargs;		/* size in bytes */
32661f28255Scgd 	if (i == 0)
32761f28255Scgd 		error("Bad #! line");
32861f28255Scgd 	for (ap2 = argv ; *ap2++ != NULL ; );
32961f28255Scgd 	new = ckmalloc(i + ((char *)ap2 - (char *)argv));
33061f28255Scgd 	ap = newargs, ap2 = new;
33161f28255Scgd 	while ((i -= sizeof (char **)) >= 0)
33261f28255Scgd 		*ap2++ = *ap++;
33361f28255Scgd 	ap = argv;
33461f28255Scgd 	while (*ap2++ = *ap++);
33561f28255Scgd 	shellexec(new, envp, pathval(), 0);
336ee9e50eaSmycroft 	/* NOTREACHED */
33761f28255Scgd }
33861f28255Scgd #endif
33961f28255Scgd 
34061f28255Scgd 
34161f28255Scgd 
34261f28255Scgd /*
34361f28255Scgd  * Do a path search.  The variable path (passed by reference) should be
34461f28255Scgd  * set to the start of the path before the first call; padvance will update
34561f28255Scgd  * this value as it proceeds.  Successive calls to padvance will return
34661f28255Scgd  * the possible path expansions in sequence.  If an option (indicated by
34761f28255Scgd  * a percent sign) appears in the path entry then the global variable
34861f28255Scgd  * pathopt will be set to point to it; otherwise pathopt will be set to
34961f28255Scgd  * NULL.
35061f28255Scgd  */
35161f28255Scgd 
3523d424690Schristos const char *pathopt;
35361f28255Scgd 
35461f28255Scgd char *
padvance(const char ** path,const char * name,int magic_percent)3551676135eSkre padvance(const char **path, const char *name, int magic_percent)
35661f28255Scgd {
3573d424690Schristos 	const char *p;
3583d424690Schristos 	char *q;
3593d424690Schristos 	const char *start;
36061f28255Scgd 	int len;
36161f28255Scgd 
36261f28255Scgd 	if (*path == NULL)
36361f28255Scgd 		return NULL;
3641676135eSkre 	if (magic_percent)
3651676135eSkre 		magic_percent = '%';
3661676135eSkre 
36761f28255Scgd 	start = *path;
3681676135eSkre 	for (p = start ; *p && *p != ':' && *p != magic_percent ; p++)
3691676135eSkre 		;
37061f28255Scgd 	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
37161f28255Scgd 	while (stackblocksize() < len)
37261f28255Scgd 		growstackblock();
37361f28255Scgd 	q = stackblock();
37461f28255Scgd 	if (p != start) {
37506f53b68Smycroft 		memcpy(q, start, p - start);
37661f28255Scgd 		q += p - start;
3771676135eSkre 		if (q[-1] != '/')
37861f28255Scgd 			*q++ = '/';
37961f28255Scgd 	}
38061f28255Scgd 	strcpy(q, name);
38161f28255Scgd 	pathopt = NULL;
3821676135eSkre 	if (*p == magic_percent) {
38361f28255Scgd 		pathopt = ++p;
3841676135eSkre 		while (*p && *p != ':')
3851676135eSkre 			p++;
38661f28255Scgd 	}
38761f28255Scgd 	if (*p == ':')
38861f28255Scgd 		*path = p + 1;
38961f28255Scgd 	else
39061f28255Scgd 		*path = NULL;
391ee3b307fSkre 	return grabstackstr(q + strlen(name) + 1);
39261f28255Scgd }
39361f28255Scgd 
39461f28255Scgd 
39561f28255Scgd /*** Command hashing code ***/
39661f28255Scgd 
39761f28255Scgd 
3984ce0d34aScgd int
hashcmd(int argc,char ** argv)399c02b3bbdSchristos hashcmd(int argc, char **argv)
4004ce0d34aScgd {
40161f28255Scgd 	struct tblentry **pp;
40261f28255Scgd 	struct tblentry *cmdp;
40361f28255Scgd 	int c;
40461f28255Scgd 	struct cmdentry entry;
40561f28255Scgd 	char *name;
40645597480Skre 	int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0;
4078ea2ac47Skre 	int errs=1, emsg=DO_ERR;
4088ea2ac47Skre 	int status = 0;
40961f28255Scgd 
4108ea2ac47Skre 	while ((c = nextopt("bcefqrsuv")) != '\0')
41145597480Skre 		switch (c) {
41245597480Skre 		case 'b':	bopt = 1;	break;
41345597480Skre 		case 'c':	uopt = 1;	break;	/* c == u */
4148ea2ac47Skre 		case 'e':	errs = 0;	break;
41545597480Skre 		case 'f':	fopt = 1;	break;
4168ea2ac47Skre 		case 'q':	emsg = 0;	break;
41745597480Skre 		case 'r':	ropt = 1;	break;
41845597480Skre 		case 's':	sopt = 1;	break;
41945597480Skre 		case 'u':	uopt = 1;	break;
42045597480Skre 		case 'v':	verbose = 1;	break;
42145597480Skre 		}
42245597480Skre 
4238ea2ac47Skre 	if (!errs)
4248ea2ac47Skre 		emsg ^= DO_ERR;
4258ea2ac47Skre 
42645597480Skre 	if (ropt)
42761f28255Scgd 		clearcmdentry(0);
42845597480Skre 
42945597480Skre 	if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0)
43045597480Skre 		allopt = bopt = fopt = sopt = uopt = 1;
43145597480Skre 
43237ed7877Sjtc 	if (*argptr == NULL) {
43337ed7877Sjtc 		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
43437ed7877Sjtc 			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
43545597480Skre 				switch (cmdp->cmdtype) {
43645597480Skre 				case CMDNORMAL:
43745597480Skre 					if (!uopt)
43845597480Skre 						continue;
43945597480Skre 					break;
44045597480Skre 				case CMDBUILTIN:
44145597480Skre 					if (!bopt)
44245597480Skre 						continue;
44345597480Skre 					break;
44445597480Skre 				case CMDSPLBLTIN:
44545597480Skre 					if (!sopt)
44645597480Skre 						continue;
44745597480Skre 					break;
44845597480Skre 				case CMDFUNCTION:
44945597480Skre 					if (!fopt)
45045597480Skre 						continue;
45145597480Skre 					break;
45245597480Skre 				default:	/* never happens */
45345597480Skre 					continue;
45445597480Skre 				}
45545597480Skre 				if (!allopt || verbose ||
45645597480Skre 				    cmdp->cmdtype == CMDNORMAL)
45737ed7877Sjtc 					printentry(cmdp, verbose);
45837ed7877Sjtc 			}
45937ed7877Sjtc 		}
4608ea2ac47Skre 		flushout(out1);
4618ea2ac47Skre 		if (io_err(out1)) {
4628ea2ac47Skre 			out2str("hash: I/O error writing to standard output\n");
4638ea2ac47Skre 			return 1;
4648ea2ac47Skre 		}
46537ed7877Sjtc 		return 0;
46637ed7877Sjtc 	}
46745597480Skre 
46845597480Skre 	while ((name = *argptr++) != NULL) {
46945597480Skre 		if ((cmdp = cmdlookup(name, 0)) != NULL) {
47045597480Skre 			switch (cmdp->cmdtype) {
47145597480Skre 			case CMDNORMAL:
47245597480Skre 				if (!uopt)
47345597480Skre 					continue;
47461f28255Scgd 				delete_cmd_entry();
47545597480Skre 				break;
47645597480Skre 			case CMDBUILTIN:
47745597480Skre 				if (!bopt)
47845597480Skre 					continue;
47945597480Skre 				if (builtinloc >= 0)
48045597480Skre 					delete_cmd_entry();
48145597480Skre 				break;
48245597480Skre 			case CMDSPLBLTIN:
48345597480Skre 				if (!sopt)
48445597480Skre 					continue;
48545597480Skre 				break;
48645597480Skre 			case CMDFUNCTION:
48745597480Skre 				if (!fopt)
48845597480Skre 					continue;
48945597480Skre 				break;
49045597480Skre 			}
49145597480Skre 		}
4928ea2ac47Skre 		find_command(name, &entry, emsg, pathval());
4938ea2ac47Skre 		if (errs && entry.cmdtype == CMDUNKNOWN)
4948ea2ac47Skre 			status = 1;
49561f28255Scgd 		if (verbose) {
49661f28255Scgd 			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
49761f28255Scgd 				cmdp = cmdlookup(name, 0);
498169a2694Schristos 				if (cmdp != NULL)
49937ed7877Sjtc 					printentry(cmdp, verbose);
50061f28255Scgd 			}
50161f28255Scgd 			flushall();
50261f28255Scgd 		}
50361f28255Scgd 	}
5048ea2ac47Skre 	return status;
50561f28255Scgd }
50661f28255Scgd 
50761f28255Scgd STATIC void
printentry(struct tblentry * cmdp,int verbose)508c02b3bbdSchristos printentry(struct tblentry *cmdp, int verbose)
50961f28255Scgd {
5103d424690Schristos 	int idx;
5113d424690Schristos 	const char *path;
51261f28255Scgd 	char *name;
51361f28255Scgd 
514c02b3bbdSchristos 	switch (cmdp->cmdtype) {
515c02b3bbdSchristos 	case CMDNORMAL:
5163d424690Schristos 		idx = cmdp->param.index;
51761f28255Scgd 		path = pathval();
51861f28255Scgd 		do {
5191676135eSkre 			name = padvance(&path, cmdp->cmdname, 1);
52061f28255Scgd 			stunalloc(name);
5213d424690Schristos 		} while (--idx >= 0);
52245597480Skre 		if (verbose)
52345597480Skre 			out1fmt("Command from PATH[%d]: ",
52445597480Skre 			    cmdp->param.index);
52561f28255Scgd 		out1str(name);
526c02b3bbdSchristos 		break;
527c02b3bbdSchristos 	case CMDSPLBLTIN:
52845597480Skre 		if (verbose)
52945597480Skre 			out1str("special ");
53045597480Skre 		/* FALLTHROUGH */
531c02b3bbdSchristos 	case CMDBUILTIN:
53245597480Skre 		if (verbose)
53345597480Skre 			out1str("builtin ");
53445597480Skre 		out1fmt("%s", cmdp->cmdname);
535c02b3bbdSchristos 		break;
536c02b3bbdSchristos 	case CMDFUNCTION:
53745597480Skre 		if (verbose)
53845597480Skre 			out1str("function ");
53945597480Skre 		out1fmt("%s", cmdp->cmdname);
54037ed7877Sjtc 		if (verbose) {
541c02b3bbdSchristos 			struct procstat ps;
542c7c0722aSkre 
54337ed7877Sjtc 			INTOFF;
544c7c0722aSkre 			commandtext(&ps, getfuncnode(cmdp->param.func));
54537ed7877Sjtc 			INTON;
54651d94f21Sdsl 			out1str("() { ");
54751d94f21Sdsl 			out1str(ps.cmd);
54851d94f21Sdsl 			out1str("; }");
54937ed7877Sjtc 		}
550c02b3bbdSchristos 		break;
551c02b3bbdSchristos 	default:
55245597480Skre 		error("internal error: %s cmdtype %d",
55345597480Skre 		    cmdp->cmdname, cmdp->cmdtype);
55461f28255Scgd 	}
55561f28255Scgd 	if (cmdp->rehash)
55661f28255Scgd 		out1c('*');
55761f28255Scgd 	out1c('\n');
55861f28255Scgd }
55961f28255Scgd 
56061f28255Scgd 
56161f28255Scgd 
56261f28255Scgd /*
56361f28255Scgd  * Resolve a command name.  If you change this routine, you may have to
56461f28255Scgd  * change the shellexec routine as well.
56561f28255Scgd  */
56661f28255Scgd 
56761f28255Scgd void
find_command(char * name,struct cmdentry * entry,int act,const char * path)568c02b3bbdSchristos find_command(char *name, struct cmdentry *entry, int act, const char *path)
56961f28255Scgd {
570e314f958Sdsl 	struct tblentry *cmdp, loc_cmd;
5713d424690Schristos 	int idx;
57261f28255Scgd 	int prev;
57361f28255Scgd 	char *fullname;
57461f28255Scgd 	struct stat statb;
57561f28255Scgd 	int e;
576c02b3bbdSchristos 	int (*bltin)(int,char **);
57761f28255Scgd 
578e314f958Sdsl 	/* If name contains a slash, don't use PATH or hash table */
57961f28255Scgd 	if (strchr(name, '/') != NULL) {
580c996803cSchristos 		if (act & DO_ABS) {
581c996803cSchristos 			while (stat(name, &statb) < 0) {
582c996803cSchristos #ifdef SYSV
583c996803cSchristos 				if (errno == EINTR)
584c996803cSchristos 					continue;
585c996803cSchristos #endif
586c996803cSchristos 				if (errno != ENOENT && errno != ENOTDIR)
587c996803cSchristos 					e = errno;
588c996803cSchristos 				entry->cmdtype = CMDUNKNOWN;
589c996803cSchristos 				entry->u.index = -1;
590c996803cSchristos 				return;
591c996803cSchristos 			}
592c996803cSchristos 			entry->cmdtype = CMDNORMAL;
593c996803cSchristos 			entry->u.index = -1;
594c996803cSchristos 			return;
595c996803cSchristos 		}
59661f28255Scgd 		entry->cmdtype = CMDNORMAL;
59761f28255Scgd 		entry->u.index = 0;
59861f28255Scgd 		return;
59961f28255Scgd 	}
60061f28255Scgd 
601e314f958Sdsl 	if (path != pathval())
602e314f958Sdsl 		act |= DO_ALTPATH;
603e314f958Sdsl 
604e314f958Sdsl 	if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
605e314f958Sdsl 		act |= DO_ALTBLTIN;
606e314f958Sdsl 
607e314f958Sdsl 	/* If name is in the table, check answer will be ok */
608e314f958Sdsl 	if ((cmdp = cmdlookup(name, 0)) != NULL) {
609e314f958Sdsl 		switch (cmdp->cmdtype) {
610e314f958Sdsl 		case CMDNORMAL:
61116296115Srillig 			if (act & DO_ALTPATH)
612e314f958Sdsl 				cmdp = NULL;
613e314f958Sdsl 			break;
614e314f958Sdsl 		case CMDFUNCTION:
61516296115Srillig 			if (act & DO_NOFUNC)
616e314f958Sdsl 				cmdp = NULL;
617e314f958Sdsl 			break;
618e314f958Sdsl 		case CMDBUILTIN:
61916296115Srillig 			if ((act & DO_ALTBLTIN) || builtinloc >= 0)
620e314f958Sdsl 				cmdp = NULL;
621e314f958Sdsl 			break;
622e314f958Sdsl 		}
623e314f958Sdsl 		/* if not invalidated by cd, we're done */
62416296115Srillig 		if (cmdp != NULL && cmdp->rehash == 0)
62561f28255Scgd 			goto success;
626e314f958Sdsl 	}
62761f28255Scgd 
62861f28255Scgd 	/* If %builtin not in path, check for builtin next */
629e314f958Sdsl 	if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
630e314f958Sdsl 	    (bltin = find_builtin(name)) != 0)
631e314f958Sdsl 		goto builtin_success;
63261f28255Scgd 
63361f28255Scgd 	/* We have to search path. */
63461f28255Scgd 	prev = -1;		/* where to start */
63561f28255Scgd 	if (cmdp) {		/* doing a rehash */
63661f28255Scgd 		if (cmdp->cmdtype == CMDBUILTIN)
63761f28255Scgd 			prev = builtinloc;
63861f28255Scgd 		else
63961f28255Scgd 			prev = cmdp->param.index;
64061f28255Scgd 	}
64161f28255Scgd 
64261f28255Scgd 	e = ENOENT;
6433d424690Schristos 	idx = -1;
64461f28255Scgd loop:
6451676135eSkre 	while ((fullname = padvance(&path, name, 1)) != NULL) {
64661f28255Scgd 		stunalloc(fullname);
6473d424690Schristos 		idx++;
64861f28255Scgd 		if (pathopt) {
64961f28255Scgd 			if (prefix("builtin", pathopt)) {
650c02b3bbdSchristos 				if ((bltin = find_builtin(name)) == 0)
65161f28255Scgd 					goto loop;
652e314f958Sdsl 				goto builtin_success;
65361f28255Scgd 			} else if (prefix("func", pathopt)) {
65461f28255Scgd 				/* handled below */
65561f28255Scgd 			} else {
656e314f958Sdsl 				/* ignore unimplemented options */
657e314f958Sdsl 				goto loop;
65861f28255Scgd 			}
65961f28255Scgd 		}
66061f28255Scgd 		/* if rehash, don't redo absolute path names */
6613d424690Schristos 		if (fullname[0] == '/' && idx <= prev) {
6623d424690Schristos 			if (idx < prev)
66361f28255Scgd 				goto loop;
6649006b741Skre 			VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n",
6659006b741Skre 			    name));
66661f28255Scgd 			goto success;
66761f28255Scgd 		}
66861f28255Scgd 		while (stat(fullname, &statb) < 0) {
66961f28255Scgd #ifdef SYSV
67061f28255Scgd 			if (errno == EINTR)
67161f28255Scgd 				continue;
67261f28255Scgd #endif
67361f28255Scgd 			if (errno != ENOENT && errno != ENOTDIR)
67461f28255Scgd 				e = errno;
67561f28255Scgd 			goto loop;
67661f28255Scgd 		}
67761f28255Scgd 		e = EACCES;	/* if we fail, this will be the error */
678f5ad44b6Smycroft 		if (!S_ISREG(statb.st_mode))
67961f28255Scgd 			goto loop;
68061f28255Scgd 		if (pathopt) {		/* this is a %func directory */
681ee3b307fSkre 			char *endname;
682ee3b307fSkre 
683e314f958Sdsl 			if (act & DO_NOFUNC)
684e314f958Sdsl 				goto loop;
685ee3b307fSkre 			endname = fullname + strlen(fullname) + 1;
686ee3b307fSkre 			grabstackstr(endname);
68761f28255Scgd 			readcmdfile(fullname);
688e314f958Sdsl 			if ((cmdp = cmdlookup(name, 0)) == NULL ||
689e314f958Sdsl 			    cmdp->cmdtype != CMDFUNCTION)
69061f28255Scgd 				error("%s not defined in %s", name, fullname);
691ee3b307fSkre 			ungrabstackstr(fullname, endname);
69261f28255Scgd 			goto success;
69361f28255Scgd 		}
69437ed7877Sjtc #ifdef notdef
695e314f958Sdsl 		/* XXX this code stops root executing stuff, and is buggy
696e314f958Sdsl 		   if you need a group from the group list. */
69737ed7877Sjtc 		if (statb.st_uid == geteuid()) {
69861f28255Scgd 			if ((statb.st_mode & 0100) == 0)
69961f28255Scgd 				goto loop;
70061f28255Scgd 		} else if (statb.st_gid == getegid()) {
70161f28255Scgd 			if ((statb.st_mode & 010) == 0)
70261f28255Scgd 				goto loop;
70361f28255Scgd 		} else {
70437ed7877Sjtc 			if ((statb.st_mode & 01) == 0)
70561f28255Scgd 				goto loop;
70661f28255Scgd 		}
707a3a1e404Scgd #endif
7089006b741Skre 		VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name,
7099006b741Skre 		    fullname));
71061f28255Scgd 		INTOFF;
711e314f958Sdsl 		if (act & DO_ALTPATH) {
712ee3b307fSkre 		/*
713ee3b307fSkre 		 * this should be a grabstackstr() but is not needed:
714ee3b307fSkre 		 * fullname is no longer needed for anything
715e314f958Sdsl 			stalloc(strlen(fullname) + 1);
716ee3b307fSkre 		 */
717e314f958Sdsl 			cmdp = &loc_cmd;
718e314f958Sdsl 		} else
71961f28255Scgd 			cmdp = cmdlookup(name, 1);
720b7625640Skre 
721b7625640Skre 		if (cmdp->cmdtype == CMDFUNCTION)
722b7625640Skre 			cmdp = &loc_cmd;
723b7625640Skre 
72461f28255Scgd 		cmdp->cmdtype = CMDNORMAL;
7253d424690Schristos 		cmdp->param.index = idx;
72661f28255Scgd 		INTON;
72761f28255Scgd 		goto success;
72861f28255Scgd 	}
72961f28255Scgd 
73061f28255Scgd 	/* We failed.  If there was an entry for this command, delete it */
73161f28255Scgd 	if (cmdp)
73261f28255Scgd 		delete_cmd_entry();
733c996803cSchristos 	if (act & DO_ERR)
734e6132242Sabrown 		outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
73561f28255Scgd 	entry->cmdtype = CMDUNKNOWN;
736f49e0d69Skre 	entry->u.index = idx + 1;
73761f28255Scgd 	return;
73861f28255Scgd 
739e314f958Sdsl builtin_success:
740e314f958Sdsl 	INTOFF;
741e314f958Sdsl 	if (act & DO_ALTPATH)
742e314f958Sdsl 		cmdp = &loc_cmd;
743e314f958Sdsl 	else
744e314f958Sdsl 		cmdp = cmdlookup(name, 1);
74551d94f21Sdsl 	if (cmdp->cmdtype == CMDFUNCTION)
74651d94f21Sdsl 		/* DO_NOFUNC must have been set */
74751d94f21Sdsl 		cmdp = &loc_cmd;
748e314f958Sdsl 	cmdp->cmdtype = CMDBUILTIN;
749e314f958Sdsl 	cmdp->param.bltin = bltin;
750e314f958Sdsl 	INTON;
75161f28255Scgd success:
752f6828859Schristos 	if (cmdp) {
75361f28255Scgd 		cmdp->rehash = 0;
75461f28255Scgd 		entry->cmdtype = cmdp->cmdtype;
755727a69dcSkre 		entry->lineno = cmdp->lineno;
756727a69dcSkre 		entry->lno_frel = cmdp->fn_ln1;
75761f28255Scgd 		entry->u = cmdp->param;
758f49e0d69Skre 	} else {
759f6828859Schristos 		entry->cmdtype = CMDUNKNOWN;
760f49e0d69Skre 		entry->u.index = -1;
761f49e0d69Skre 	}
76261f28255Scgd }
76361f28255Scgd 
76461f28255Scgd 
76561f28255Scgd 
76661f28255Scgd /*
76761f28255Scgd  * Search the table of builtin commands.
76861f28255Scgd  */
76961f28255Scgd 
77061f28255Scgd int
find_builtin(char * name)771da4f7877Smatt (*find_builtin(char *name))(int, char **)
77261f28255Scgd {
77348250187Stls 	const struct builtincmd *bp;
77461f28255Scgd 
77561f28255Scgd 	for (bp = builtincmd ; bp->name ; bp++) {
776658a58d0Sdsl 		if (*bp->name == *name
777658a58d0Sdsl 		    && (*name == '%' || equal(bp->name, name)))
778c02b3bbdSchristos 			return bp->builtin;
77961f28255Scgd 	}
780c02b3bbdSchristos 	return 0;
781c02b3bbdSchristos }
782c02b3bbdSchristos 
783c02b3bbdSchristos int
find_splbltin(char * name)784da4f7877Smatt (*find_splbltin(char *name))(int, char **)
785c02b3bbdSchristos {
786c02b3bbdSchristos 	const struct builtincmd *bp;
787c02b3bbdSchristos 
788c02b3bbdSchristos 	for (bp = splbltincmd ; bp->name ; bp++) {
789c02b3bbdSchristos 		if (*bp->name == *name && equal(bp->name, name))
790c02b3bbdSchristos 			return bp->builtin;
791c02b3bbdSchristos 	}
792c02b3bbdSchristos 	return 0;
793c02b3bbdSchristos }
794c02b3bbdSchristos 
795c02b3bbdSchristos /*
796c02b3bbdSchristos  * At shell startup put special builtins into hash table.
797c02b3bbdSchristos  * ensures they are executed first (see posix).
798c02b3bbdSchristos  * We stop functions being added with the same name
799c02b3bbdSchristos  * (as they are impossible to call)
800c02b3bbdSchristos  */
801c02b3bbdSchristos 
802c02b3bbdSchristos void
hash_special_builtins(void)803c02b3bbdSchristos hash_special_builtins(void)
804c02b3bbdSchristos {
805c02b3bbdSchristos 	const struct builtincmd *bp;
806c02b3bbdSchristos 	struct tblentry *cmdp;
807c02b3bbdSchristos 
808c02b3bbdSchristos 	for (bp = splbltincmd ; bp->name ; bp++) {
809c02b3bbdSchristos 		cmdp = cmdlookup(bp->name, 1);
810c02b3bbdSchristos 		cmdp->cmdtype = CMDSPLBLTIN;
811c02b3bbdSchristos 		cmdp->param.bltin = bp->builtin;
812c02b3bbdSchristos 	}
81361f28255Scgd }
81461f28255Scgd 
81561f28255Scgd 
81661f28255Scgd 
81761f28255Scgd /*
81861f28255Scgd  * Called when a cd is done.  Marks all commands so the next time they
81961f28255Scgd  * are executed they will be rehashed.
82061f28255Scgd  */
82161f28255Scgd 
82261f28255Scgd void
hashcd(void)823c02b3bbdSchristos hashcd(void)
824c02b3bbdSchristos {
82561f28255Scgd 	struct tblentry **pp;
82661f28255Scgd 	struct tblentry *cmdp;
82761f28255Scgd 
82861f28255Scgd 	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
82961f28255Scgd 		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
83061f28255Scgd 			if (cmdp->cmdtype == CMDNORMAL
83107bae7edSchristos 			 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
83261f28255Scgd 				cmdp->rehash = 1;
83361f28255Scgd 		}
83461f28255Scgd 	}
83561f28255Scgd }
83661f28255Scgd 
83761f28255Scgd 
83861f28255Scgd 
83961f28255Scgd /*
840e314f958Sdsl  * Fix command hash table when PATH changed.
84161f28255Scgd  * Called before PATH is changed.  The argument is the new value of PATH;
842e314f958Sdsl  * pathval() still returns the old value at this point.
843e314f958Sdsl  * Called with interrupts off.
84461f28255Scgd  */
84561f28255Scgd 
84661f28255Scgd void
changepath(char * newval,int flags __unused)847*d73cf48cSkre changepath(char *newval, int flags __unused)
84861f28255Scgd {
8499a738ef2Schristos 	const char *old, *new;
8503d424690Schristos 	int idx;
85161f28255Scgd 	int firstchange;
85261f28255Scgd 	int bltin;
85361f28255Scgd 
85461f28255Scgd 	old = pathval();
85561f28255Scgd 	new = newval;
85661f28255Scgd 	firstchange = 9999;	/* assume no change */
8573d424690Schristos 	idx = 0;
85861f28255Scgd 	bltin = -1;
85961f28255Scgd 	for (;;) {
86061f28255Scgd 		if (*old != *new) {
8613d424690Schristos 			firstchange = idx;
86207bae7edSchristos 			if ((*old == '\0' && *new == ':')
86307bae7edSchristos 			 || (*old == ':' && *new == '\0'))
86461f28255Scgd 				firstchange++;
86561f28255Scgd 			old = new;	/* ignore subsequent differences */
86661f28255Scgd 		}
86761f28255Scgd 		if (*new == '\0')
86861f28255Scgd 			break;
86961f28255Scgd 		if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
8703d424690Schristos 			bltin = idx;
87161f28255Scgd 		if (*new == ':') {
8723d424690Schristos 			idx++;
87361f28255Scgd 		}
87461f28255Scgd 		new++, old++;
87561f28255Scgd 	}
87661f28255Scgd 	if (builtinloc < 0 && bltin >= 0)
87761f28255Scgd 		builtinloc = bltin;		/* zap builtins */
87861f28255Scgd 	if (builtinloc >= 0 && bltin < 0)
87961f28255Scgd 		firstchange = 0;
88061f28255Scgd 	clearcmdentry(firstchange);
88161f28255Scgd 	builtinloc = bltin;
88261f28255Scgd }
88361f28255Scgd 
88461f28255Scgd 
88561f28255Scgd /*
88661f28255Scgd  * Clear out command entries.  The argument specifies the first entry in
88761f28255Scgd  * PATH which has changed.
88861f28255Scgd  */
88961f28255Scgd 
89061f28255Scgd STATIC void
clearcmdentry(int firstchange)891c02b3bbdSchristos clearcmdentry(int firstchange)
8924ce0d34aScgd {
89361f28255Scgd 	struct tblentry **tblp;
89461f28255Scgd 	struct tblentry **pp;
89561f28255Scgd 	struct tblentry *cmdp;
89661f28255Scgd 
89761f28255Scgd 	INTOFF;
89861f28255Scgd 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
89961f28255Scgd 		pp = tblp;
90061f28255Scgd 		while ((cmdp = *pp) != NULL) {
90107bae7edSchristos 			if ((cmdp->cmdtype == CMDNORMAL &&
90207bae7edSchristos 			     cmdp->param.index >= firstchange)
90307bae7edSchristos 			 || (cmdp->cmdtype == CMDBUILTIN &&
90407bae7edSchristos 			     builtinloc >= firstchange)) {
90561f28255Scgd 				*pp = cmdp->next;
90661f28255Scgd 				ckfree(cmdp);
90761f28255Scgd 			} else {
90861f28255Scgd 				pp = &cmdp->next;
90961f28255Scgd 			}
91061f28255Scgd 		}
91161f28255Scgd 	}
91261f28255Scgd 	INTON;
91361f28255Scgd }
91461f28255Scgd 
91561f28255Scgd 
91661f28255Scgd /*
91761f28255Scgd  * Delete all functions.
91861f28255Scgd  */
91961f28255Scgd 
92061f28255Scgd #ifdef mkinit
921c02b3bbdSchristos MKINIT void deletefuncs(void);
922c02b3bbdSchristos MKINIT void hash_special_builtins(void);
923c02b3bbdSchristos 
924c02b3bbdSchristos INIT {
925c02b3bbdSchristos 	hash_special_builtins();
926c02b3bbdSchristos }
92761f28255Scgd 
92861f28255Scgd SHELLPROC {
92961f28255Scgd 	deletefuncs();
93061f28255Scgd }
93161f28255Scgd #endif
93261f28255Scgd 
93361f28255Scgd void
deletefuncs(void)934c02b3bbdSchristos deletefuncs(void)
935c02b3bbdSchristos {
93661f28255Scgd 	struct tblentry **tblp;
93761f28255Scgd 	struct tblentry **pp;
93861f28255Scgd 	struct tblentry *cmdp;
93961f28255Scgd 
94061f28255Scgd 	INTOFF;
94161f28255Scgd 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
94261f28255Scgd 		pp = tblp;
94361f28255Scgd 		while ((cmdp = *pp) != NULL) {
94461f28255Scgd 			if (cmdp->cmdtype == CMDFUNCTION) {
94561f28255Scgd 				*pp = cmdp->next;
94661f28255Scgd 				freefunc(cmdp->param.func);
94761f28255Scgd 				ckfree(cmdp);
94861f28255Scgd 			} else {
94961f28255Scgd 				pp = &cmdp->next;
95061f28255Scgd 			}
95161f28255Scgd 		}
95261f28255Scgd 	}
95361f28255Scgd 	INTON;
95461f28255Scgd }
95561f28255Scgd 
95661f28255Scgd 
95761f28255Scgd 
95861f28255Scgd /*
95961f28255Scgd  * Locate a command in the command hash table.  If "add" is nonzero,
96061f28255Scgd  * add the command to the table if it is not already present.  The
96161f28255Scgd  * variable "lastcmdentry" is set to point to the address of the link
96261f28255Scgd  * pointing to the entry, so that delete_cmd_entry can delete the
96361f28255Scgd  * entry.
96461f28255Scgd  */
96561f28255Scgd 
96661f28255Scgd struct tblentry **lastcmdentry;
96761f28255Scgd 
96861f28255Scgd 
96961f28255Scgd STATIC struct tblentry *
cmdlookup(const char * name,int add)970c02b3bbdSchristos cmdlookup(const char *name, int add)
97161f28255Scgd {
97261f28255Scgd 	int hashval;
973c02b3bbdSchristos 	const char *p;
97461f28255Scgd 	struct tblentry *cmdp;
97561f28255Scgd 	struct tblentry **pp;
97661f28255Scgd 
97761f28255Scgd 	p = name;
97861f28255Scgd 	hashval = *p << 4;
97961f28255Scgd 	while (*p)
98061f28255Scgd 		hashval += *p++;
98161f28255Scgd 	hashval &= 0x7FFF;
98261f28255Scgd 	pp = &cmdtable[hashval % CMDTABLESIZE];
98361f28255Scgd 	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
98461f28255Scgd 		if (equal(cmdp->cmdname, name))
98561f28255Scgd 			break;
98661f28255Scgd 		pp = &cmdp->next;
98761f28255Scgd 	}
98861f28255Scgd 	if (add && cmdp == NULL) {
98961f28255Scgd 		INTOFF;
99061f28255Scgd 		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
99161f28255Scgd 					+ strlen(name) + 1);
99261f28255Scgd 		cmdp->next = NULL;
99361f28255Scgd 		cmdp->cmdtype = CMDUNKNOWN;
99461f28255Scgd 		cmdp->rehash = 0;
99561f28255Scgd 		strcpy(cmdp->cmdname, name);
99661f28255Scgd 		INTON;
99761f28255Scgd 	}
99861f28255Scgd 	lastcmdentry = pp;
99961f28255Scgd 	return cmdp;
100061f28255Scgd }
100161f28255Scgd 
100261f28255Scgd /*
100361f28255Scgd  * Delete the command entry returned on the last lookup.
100461f28255Scgd  */
100561f28255Scgd 
100661f28255Scgd STATIC void
delete_cmd_entry(void)1007c02b3bbdSchristos delete_cmd_entry(void)
1008c02b3bbdSchristos {
100961f28255Scgd 	struct tblentry *cmdp;
101061f28255Scgd 
101161f28255Scgd 	INTOFF;
101261f28255Scgd 	cmdp = *lastcmdentry;
101361f28255Scgd 	*lastcmdentry = cmdp->next;
101461f28255Scgd 	ckfree(cmdp);
101561f28255Scgd 	INTON;
101661f28255Scgd }
101761f28255Scgd 
101861f28255Scgd 
101961f28255Scgd 
102061f28255Scgd #ifdef notdef
102161f28255Scgd void
getcmdentry(char * name,struct cmdentry * entry)1022c02b3bbdSchristos getcmdentry(char *name, struct cmdentry *entry)
102361f28255Scgd {
102461f28255Scgd 	struct tblentry *cmdp = cmdlookup(name, 0);
102561f28255Scgd 
102661f28255Scgd 	if (cmdp) {
102761f28255Scgd 		entry->u = cmdp->param;
102861f28255Scgd 		entry->cmdtype = cmdp->cmdtype;
102961f28255Scgd 	} else {
103061f28255Scgd 		entry->cmdtype = CMDUNKNOWN;
103161f28255Scgd 		entry->u.index = 0;
103261f28255Scgd 	}
103361f28255Scgd }
103461f28255Scgd #endif
103561f28255Scgd 
103661f28255Scgd 
103761f28255Scgd /*
103861f28255Scgd  * Add a new command entry, replacing any existing command entry for
1039c02b3bbdSchristos  * the same name - except special builtins.
104061f28255Scgd  */
104161f28255Scgd 
1042e314f958Sdsl STATIC void
addcmdentry(char * name,struct cmdentry * entry)1043c02b3bbdSchristos addcmdentry(char *name, struct cmdentry *entry)
104461f28255Scgd {
104561f28255Scgd 	struct tblentry *cmdp;
104661f28255Scgd 
104761f28255Scgd 	INTOFF;
104861f28255Scgd 	cmdp = cmdlookup(name, 1);
1049c02b3bbdSchristos 	if (cmdp->cmdtype != CMDSPLBLTIN) {
1050c7c0722aSkre 		if (cmdp->cmdtype == CMDFUNCTION)
1051c7c0722aSkre 			unreffunc(cmdp->param.func);
105261f28255Scgd 		cmdp->cmdtype = entry->cmdtype;
1053727a69dcSkre 		cmdp->lineno = entry->lineno;
1054727a69dcSkre 		cmdp->fn_ln1 = entry->lno_frel;
105561f28255Scgd 		cmdp->param = entry->u;
1056c02b3bbdSchristos 	}
105761f28255Scgd 	INTON;
105861f28255Scgd }
105961f28255Scgd 
106061f28255Scgd 
106161f28255Scgd /*
106261f28255Scgd  * Define a shell function.
106361f28255Scgd  */
106461f28255Scgd 
106561f28255Scgd void
defun(char * name,union node * func,int lineno)1066727a69dcSkre defun(char *name, union node *func, int lineno)
106761f28255Scgd {
106861f28255Scgd 	struct cmdentry entry;
106961f28255Scgd 
107061f28255Scgd 	INTOFF;
107161f28255Scgd 	entry.cmdtype = CMDFUNCTION;
1072727a69dcSkre 	entry.lineno = lineno;
1073727a69dcSkre 	entry.lno_frel = fnline1;
107461f28255Scgd 	entry.u.func = copyfunc(func);
107561f28255Scgd 	addcmdentry(name, &entry);
107661f28255Scgd 	INTON;
107761f28255Scgd }
107861f28255Scgd 
107961f28255Scgd 
108061f28255Scgd /*
108161f28255Scgd  * Delete a function if it exists.
108261f28255Scgd  */
108361f28255Scgd 
108437ed7877Sjtc int
unsetfunc(char * name)1085c02b3bbdSchristos unsetfunc(char *name)
108661f28255Scgd {
108761f28255Scgd 	struct tblentry *cmdp;
108861f28255Scgd 
1089e314f958Sdsl 	if ((cmdp = cmdlookup(name, 0)) != NULL &&
1090e314f958Sdsl 	    cmdp->cmdtype == CMDFUNCTION) {
1091c7c0722aSkre 		unreffunc(cmdp->param.func);
109261f28255Scgd 		delete_cmd_entry();
109361f28255Scgd 	}
10945c83aa64Schristos 	return 0;
109561f28255Scgd }
1096680690d3Schristos 
1097680690d3Schristos /*
1098680690d3Schristos  * Locate and print what a word is...
1099e314f958Sdsl  * also used for 'command -[v|V]'
1100680690d3Schristos  */
1101680690d3Schristos 
1102680690d3Schristos int
typecmd(int argc,char ** argv)1103c02b3bbdSchristos typecmd(int argc, char **argv)
1104680690d3Schristos {
1105680690d3Schristos 	struct cmdentry entry;
1106680690d3Schristos 	struct tblentry *cmdp;
11074498b1feSmatt 	const char * const *pp;
1108680690d3Schristos 	struct alias *ap;
11093d424690Schristos 	int err = 0;
1110e314f958Sdsl 	char *arg;
1111e314f958Sdsl 	int c;
1112e314f958Sdsl 	int V_flag = 0;
1113e314f958Sdsl 	int v_flag = 0;
1114e314f958Sdsl 	int p_flag = 0;
1115680690d3Schristos 
1116e314f958Sdsl 	while ((c = nextopt("vVp")) != 0) {
1117e314f958Sdsl 		switch (c) {
1118e314f958Sdsl 		case 'v': v_flag = 1; break;
1119e314f958Sdsl 		case 'V': V_flag = 1; break;
1120e314f958Sdsl 		case 'p': p_flag = 1; break;
1121e314f958Sdsl 		}
1122e314f958Sdsl 	}
1123e314f958Sdsl 
1124e2f17f9aSkre 	if (argv[0][0] != 'c' && v_flag | V_flag | p_flag)
1125e2f17f9aSkre 		error("usage: %s name...", argv[0]);
1126e2f17f9aSkre 
1127e2f17f9aSkre 	if (v_flag && V_flag)
1128e2f17f9aSkre 		error("-v and -V cannot both be specified");
1129e2f17f9aSkre 
1130e2f17f9aSkre 	if (*argptr == NULL)
1131e2f17f9aSkre 		error("usage: %s%s name ...", argv[0],
1132e2f17f9aSkre 		    argv[0][0] == 'c' ? " [-p] [-v|-V]" : "");
1133e314f958Sdsl 
1134e314f958Sdsl 	while ((arg = *argptr++)) {
1135e314f958Sdsl 		if (!v_flag)
1136e314f958Sdsl 			out1str(arg);
1137680690d3Schristos 		/* First look at the keywords */
1138e2056eadSmatt 		for (pp = parsekwd; *pp; pp++)
1139e314f958Sdsl 			if (**pp == *arg && equal(*pp, arg))
1140680690d3Schristos 				break;
1141680690d3Schristos 
1142680690d3Schristos 		if (*pp) {
1143e314f958Sdsl 			if (v_flag)
1144e2f17f9aSkre 				out1fmt("%s\n", arg);
1145e314f958Sdsl 			else
1146680690d3Schristos 				out1str(" is a shell keyword\n");
1147680690d3Schristos 			continue;
1148680690d3Schristos 		}
1149680690d3Schristos 
1150680690d3Schristos 		/* Then look at the aliases */
1151e314f958Sdsl 		if ((ap = lookupalias(arg, 1)) != NULL) {
1152e2f17f9aSkre 			int ml = 0;
1153e2f17f9aSkre 
1154e2f17f9aSkre 			if (!v_flag) {
1155e2f17f9aSkre 				out1str(" is an alias ");
1156e2f17f9aSkre 				if (strchr(ap->val, '\n')) {
1157e2f17f9aSkre 					out1str("(multiline)...\n");
1158e2f17f9aSkre 					ml = 1;
1159e2f17f9aSkre 				} else
1160e2f17f9aSkre 					out1str("for: ");
1161e2f17f9aSkre 			}
1162e314f958Sdsl 			out1fmt("%s\n", ap->val);
1163e2f17f9aSkre 			if (ml && *argptr != NULL)
1164e2f17f9aSkre 				out1c('\n');
1165680690d3Schristos 			continue;
1166680690d3Schristos 		}
1167680690d3Schristos 
1168680690d3Schristos 		/* Then check if it is a tracked alias */
1169e2f17f9aSkre 		if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) {
1170680690d3Schristos 			entry.cmdtype = cmdp->cmdtype;
1171680690d3Schristos 			entry.u = cmdp->param;
1172e314f958Sdsl 		} else {
1173e2f17f9aSkre 			cmdp = NULL;
1174680690d3Schristos 			/* Finally use brute force */
1175e2f17f9aSkre 			find_command(arg, &entry, DO_ABS,
1176e2f17f9aSkre 			     p_flag ? syspath() + 5 : pathval());
1177680690d3Schristos 		}
1178680690d3Schristos 
1179680690d3Schristos 		switch (entry.cmdtype) {
1180680690d3Schristos 		case CMDNORMAL: {
1181e314f958Sdsl 			if (strchr(arg, '/') == NULL) {
1182e2f17f9aSkre 				const char *path;
11833d424690Schristos 				char *name;
11849cc4e15fSchristos 				int j = entry.u.index;
1185e2f17f9aSkre 
1186e2f17f9aSkre 				path = p_flag ? syspath() + 5 : pathval();
1187e2f17f9aSkre 
1188680690d3Schristos 				do {
11891676135eSkre 					name = padvance(&path, arg, 1);
1190680690d3Schristos 					stunalloc(name);
1191680690d3Schristos 				} while (--j >= 0);
1192e314f958Sdsl 				if (!v_flag)
1193e314f958Sdsl 					out1fmt(" is%s ",
1194e314f958Sdsl 					    cmdp ? " a tracked alias for" : "");
1195e314f958Sdsl 				out1fmt("%s\n", name);
11969cc4e15fSchristos 			} else {
1197e314f958Sdsl 				if (access(arg, X_OK) == 0) {
1198e314f958Sdsl 					if (!v_flag)
1199e314f958Sdsl 						out1fmt(" is ");
1200e314f958Sdsl 					out1fmt("%s\n", arg);
1201e314f958Sdsl 				} else {
1202e314f958Sdsl 					if (!v_flag)
1203e314f958Sdsl 						out1fmt(": %s\n",
1204e314f958Sdsl 						    strerror(errno));
12059cc4e15fSchristos 					else
1206e314f958Sdsl 						err = 126;
1207e314f958Sdsl 				}
12089cc4e15fSchristos 			}
1209680690d3Schristos  			break;
1210680690d3Schristos 		}
1211680690d3Schristos 		case CMDFUNCTION:
1212e314f958Sdsl 			if (!v_flag)
1213680690d3Schristos 				out1str(" is a shell function\n");
1214e314f958Sdsl 			else
1215e314f958Sdsl 				out1fmt("%s\n", arg);
1216680690d3Schristos 			break;
1217680690d3Schristos 
1218680690d3Schristos 		case CMDBUILTIN:
1219e314f958Sdsl 			if (!v_flag)
1220680690d3Schristos 				out1str(" is a shell builtin\n");
1221e314f958Sdsl 			else
1222e314f958Sdsl 				out1fmt("%s\n", arg);
1223680690d3Schristos 			break;
1224680690d3Schristos 
1225c02b3bbdSchristos 		case CMDSPLBLTIN:
1226e314f958Sdsl 			if (!v_flag)
1227c02b3bbdSchristos 				out1str(" is a special shell builtin\n");
1228e314f958Sdsl 			else
1229e314f958Sdsl 				out1fmt("%s\n", arg);
1230c02b3bbdSchristos 			break;
1231c02b3bbdSchristos 
1232680690d3Schristos 		default:
1233e314f958Sdsl 			if (!v_flag)
12349cc4e15fSchristos 				out1str(": not found\n");
1235e314f958Sdsl 			err = 127;
1236680690d3Schristos 			break;
1237680690d3Schristos 		}
1238680690d3Schristos 	}
12393d424690Schristos 	return err;
1240680690d3Schristos }
1241