xref: /freebsd/bin/sh/exec.c (revision e0f5c1387df23c8c4811f5b24a7ef6ecac51a71a)
14b88c807SRodney W. Grimes /*-
24b88c807SRodney W. Grimes  * Copyright (c) 1991, 1993
34b88c807SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
44b88c807SRodney W. Grimes  *
54b88c807SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
64b88c807SRodney W. Grimes  * Kenneth Almquist.
74b88c807SRodney W. Grimes  *
84b88c807SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
94b88c807SRodney W. Grimes  * modification, are permitted provided that the following conditions
104b88c807SRodney W. Grimes  * are met:
114b88c807SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
124b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
134b88c807SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
144b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
154b88c807SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
16fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
174b88c807SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
184b88c807SRodney W. Grimes  *    without specific prior written permission.
194b88c807SRodney W. Grimes  *
204b88c807SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
214b88c807SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224b88c807SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234b88c807SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
244b88c807SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254b88c807SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264b88c807SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274b88c807SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284b88c807SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294b88c807SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304b88c807SRodney W. Grimes  * SUCH DAMAGE.
314b88c807SRodney W. Grimes  */
324b88c807SRodney W. Grimes 
334b88c807SRodney W. Grimes #ifndef lint
343d7b5b93SPhilippe Charnier #if 0
353d7b5b93SPhilippe Charnier static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
363d7b5b93SPhilippe Charnier #endif
374b88c807SRodney W. Grimes #endif /* not lint */
382749b141SDavid E. O'Brien #include <sys/cdefs.h>
392749b141SDavid E. O'Brien __FBSDID("$FreeBSD$");
404b88c807SRodney W. Grimes 
41aa9caaf6SPeter Wemm #include <sys/types.h>
42aa9caaf6SPeter Wemm #include <sys/stat.h>
43aa9caaf6SPeter Wemm #include <unistd.h>
44aa9caaf6SPeter Wemm #include <fcntl.h>
45aa9caaf6SPeter Wemm #include <errno.h>
463835f47cSJilles Tjoelker #include <paths.h>
47*e0f5c138SJilles Tjoelker #include <stdbool.h>
48aa9caaf6SPeter Wemm #include <stdlib.h>
49aa9caaf6SPeter Wemm 
504b88c807SRodney W. Grimes /*
514b88c807SRodney W. Grimes  * When commands are first encountered, they are entered in a hash table.
524b88c807SRodney W. Grimes  * This ensures that a full path search will not have to be done for them
534b88c807SRodney W. Grimes  * on each invocation.
544b88c807SRodney W. Grimes  *
554b88c807SRodney W. Grimes  * We should investigate converting to a linear search, even though that
564b88c807SRodney W. Grimes  * would make the command name "hash" a misnomer.
574b88c807SRodney W. Grimes  */
584b88c807SRodney W. Grimes 
594b88c807SRodney W. Grimes #include "shell.h"
604b88c807SRodney W. Grimes #include "main.h"
614b88c807SRodney W. Grimes #include "nodes.h"
624b88c807SRodney W. Grimes #include "parser.h"
634b88c807SRodney W. Grimes #include "redir.h"
644b88c807SRodney W. Grimes #include "eval.h"
654b88c807SRodney W. Grimes #include "exec.h"
664b88c807SRodney W. Grimes #include "builtins.h"
674b88c807SRodney W. Grimes #include "var.h"
684b88c807SRodney W. Grimes #include "options.h"
694b88c807SRodney W. Grimes #include "input.h"
704b88c807SRodney W. Grimes #include "output.h"
714b88c807SRodney W. Grimes #include "syntax.h"
724b88c807SRodney W. Grimes #include "memalloc.h"
734b88c807SRodney W. Grimes #include "error.h"
744b88c807SRodney W. Grimes #include "mystring.h"
75aa9caaf6SPeter Wemm #include "show.h"
764b88c807SRodney W. Grimes #include "jobs.h"
7776ad65f7SSteve Price #include "alias.h"
784b88c807SRodney W. Grimes 
794b88c807SRodney W. Grimes 
804b88c807SRodney W. Grimes #define CMDTABLESIZE 31		/* should be prime */
814b88c807SRodney W. Grimes 
824b88c807SRodney W. Grimes 
834b88c807SRodney W. Grimes 
844b88c807SRodney W. Grimes struct tblentry {
854b88c807SRodney W. Grimes 	struct tblentry *next;	/* next entry in hash chain */
864b88c807SRodney W. Grimes 	union param param;	/* definition of builtin function */
8785170a4aSStefan Farfeleder 	int special;		/* flag for special builtin commands */
88d172408cSJilles Tjoelker 	signed char cmdtype;	/* index identifying command */
89422c281cSJilles Tjoelker 	char cmdname[];		/* name of command */
904b88c807SRodney W. Grimes };
914b88c807SRodney W. Grimes 
924b88c807SRodney W. Grimes 
93aa7b6f82SDavid E. O'Brien static struct tblentry *cmdtable[CMDTABLESIZE];
94523646eeSJilles Tjoelker static int cmdtable_cd = 0;	/* cmdtable contains cd-dependent entries */
954b88c807SRodney W. Grimes 
964b88c807SRodney W. Grimes 
9788328642SDavid E. O'Brien static void tryexec(char *, char **, char **);
9888328642SDavid E. O'Brien static void printentry(struct tblentry *, int);
9988328642SDavid E. O'Brien static struct tblentry *cmdlookup(const char *, int);
10088328642SDavid E. O'Brien static void delete_cmd_entry(void);
101260fc3f4SJilles Tjoelker static void addcmdentry(const char *, struct cmdentry *);
1024b88c807SRodney W. Grimes 
1034b88c807SRodney W. Grimes 
1044b88c807SRodney W. Grimes 
1054b88c807SRodney W. Grimes /*
1064b88c807SRodney W. Grimes  * Exec a program.  Never returns.  If you change this routine, you may
1074b88c807SRodney W. Grimes  * have to change the find_command routine as well.
1083835f47cSJilles Tjoelker  *
1093835f47cSJilles Tjoelker  * The argv array may be changed and element argv[-1] should be writable.
1104b88c807SRodney W. Grimes  */
1114b88c807SRodney W. Grimes 
1124b88c807SRodney W. Grimes void
113384aedabSJilles Tjoelker shellexec(char **argv, char **envp, const char *path, int idx)
1144b88c807SRodney W. Grimes {
1154b88c807SRodney W. Grimes 	char *cmdname;
1164600b569SJilles Tjoelker 	const char *opt;
1174b88c807SRodney W. Grimes 	int e;
1184b88c807SRodney W. Grimes 
1194b88c807SRodney W. Grimes 	if (strchr(argv[0], '/') != NULL) {
1204b88c807SRodney W. Grimes 		tryexec(argv[0], argv, envp);
1214b88c807SRodney W. Grimes 		e = errno;
1224b88c807SRodney W. Grimes 	} else {
1234b88c807SRodney W. Grimes 		e = ENOENT;
1244600b569SJilles Tjoelker 		while ((cmdname = padvance(&path, &opt, argv[0])) != NULL) {
1254600b569SJilles Tjoelker 			if (--idx < 0 && opt == NULL) {
1264b88c807SRodney W. Grimes 				tryexec(cmdname, argv, envp);
1274b88c807SRodney W. Grimes 				if (errno != ENOENT && errno != ENOTDIR)
1284b88c807SRodney W. Grimes 					e = errno;
129604e8224SJilles Tjoelker 				if (e == ENOEXEC)
130604e8224SJilles Tjoelker 					break;
1314b88c807SRodney W. Grimes 			}
1324b88c807SRodney W. Grimes 			stunalloc(cmdname);
1334b88c807SRodney W. Grimes 		}
1344b88c807SRodney W. Grimes 	}
135ab0a2172SSteve Price 
136ab0a2172SSteve Price 	/* Map to POSIX errors */
137bb324af6SJilles Tjoelker 	if (e == ENOENT || e == ENOTDIR)
138bb324af6SJilles Tjoelker 		errorwithstatus(127, "%s: not found", argv[0]);
139bb324af6SJilles Tjoelker 	else
140bb324af6SJilles Tjoelker 		errorwithstatus(126, "%s: %s", argv[0], strerror(e));
141834d160bSJilles Tjoelker }
1424b88c807SRodney W. Grimes 
1434b88c807SRodney W. Grimes 
144*e0f5c138SJilles Tjoelker static bool
145*e0f5c138SJilles Tjoelker isbinary(const char *data, size_t len)
146*e0f5c138SJilles Tjoelker {
147*e0f5c138SJilles Tjoelker 	const char *nul, *p;
148*e0f5c138SJilles Tjoelker 	bool hasletter;
149*e0f5c138SJilles Tjoelker 
150*e0f5c138SJilles Tjoelker 	nul = memchr(data, '\0', len);
151*e0f5c138SJilles Tjoelker 	if (nul == NULL)
152*e0f5c138SJilles Tjoelker 		return false;
153*e0f5c138SJilles Tjoelker 	/*
154*e0f5c138SJilles Tjoelker 	 * POSIX says we shall allow execution if the initial part intended
155*e0f5c138SJilles Tjoelker 	 * to be parsed by the shell consists of characters and does not
156*e0f5c138SJilles Tjoelker 	 * contain the NUL character. This allows concatenating a shell
157*e0f5c138SJilles Tjoelker 	 * script (ending with exec or exit) and a binary payload.
158*e0f5c138SJilles Tjoelker 	 *
159*e0f5c138SJilles Tjoelker 	 * In order to reject common binary files such as PNG images, check
160*e0f5c138SJilles Tjoelker 	 * that there is a lowercase letter or expansion before the last
161*e0f5c138SJilles Tjoelker 	 * newline before the NUL character, in addition to the check for
162*e0f5c138SJilles Tjoelker 	 * the newline character suggested by POSIX.
163*e0f5c138SJilles Tjoelker 	 */
164*e0f5c138SJilles Tjoelker 	hasletter = false;
165*e0f5c138SJilles Tjoelker 	for (p = data; *p != '\0'; p++) {
166*e0f5c138SJilles Tjoelker 		if ((*p >= 'a' && *p <= 'z') || *p == '$' || *p == '`')
167*e0f5c138SJilles Tjoelker 			hasletter = true;
168*e0f5c138SJilles Tjoelker 		if (hasletter && *p == '\n')
169*e0f5c138SJilles Tjoelker 			return false;
170*e0f5c138SJilles Tjoelker 	}
171*e0f5c138SJilles Tjoelker 	return true;
172*e0f5c138SJilles Tjoelker }
173*e0f5c138SJilles Tjoelker 
174*e0f5c138SJilles Tjoelker 
17588328642SDavid E. O'Brien static void
1765134c3f7SWarner Losh tryexec(char *cmd, char **argv, char **envp)
1774b88c807SRodney W. Grimes {
178604e8224SJilles Tjoelker 	int e, in;
179604e8224SJilles Tjoelker 	ssize_t n;
180604e8224SJilles Tjoelker 	char buf[256];
1814b88c807SRodney W. Grimes 
1824b88c807SRodney W. Grimes 	execve(cmd, argv, envp);
1834b88c807SRodney W. Grimes 	e = errno;
1844b88c807SRodney W. Grimes 	if (e == ENOEXEC) {
185604e8224SJilles Tjoelker 		INTOFF;
186604e8224SJilles Tjoelker 		in = open(cmd, O_RDONLY | O_NONBLOCK);
187604e8224SJilles Tjoelker 		if (in != -1) {
188604e8224SJilles Tjoelker 			n = pread(in, buf, sizeof buf, 0);
189604e8224SJilles Tjoelker 			close(in);
190*e0f5c138SJilles Tjoelker 			if (n > 0 && isbinary(buf, n)) {
191604e8224SJilles Tjoelker 				errno = ENOEXEC;
192604e8224SJilles Tjoelker 				return;
193604e8224SJilles Tjoelker 			}
194604e8224SJilles Tjoelker 		}
1953835f47cSJilles Tjoelker 		*argv = cmd;
19646c6b52dSJilles Tjoelker 		*--argv = __DECONST(char *, _PATH_BSHELL);
1973835f47cSJilles Tjoelker 		execve(_PATH_BSHELL, argv, envp);
1984b88c807SRodney W. Grimes 	}
1994b88c807SRodney W. Grimes 	errno = e;
2004b88c807SRodney W. Grimes }
2014b88c807SRodney W. Grimes 
2024b88c807SRodney W. Grimes /*
2034b88c807SRodney W. Grimes  * Do a path search.  The variable path (passed by reference) should be
2044b88c807SRodney W. Grimes  * set to the start of the path before the first call; padvance will update
2054b88c807SRodney W. Grimes  * this value as it proceeds.  Successive calls to padvance will return
2064600b569SJilles Tjoelker  * the possible path expansions in sequence.  If popt is not NULL, options
2074600b569SJilles Tjoelker  * are processed: if an option (indicated by a percent sign) appears in
2084600b569SJilles Tjoelker  * the path entry then *popt will be set to point to it; else *popt will be
2094600b569SJilles Tjoelker  * set to NULL.  If popt is NULL, percent signs are not special.
2104b88c807SRodney W. Grimes  */
2114b88c807SRodney W. Grimes 
2124b88c807SRodney W. Grimes char *
2134600b569SJilles Tjoelker padvance(const char **path, const char **popt, const char *name)
2144b88c807SRodney W. Grimes {
2152cac6e36SJilles Tjoelker 	const char *p, *start;
2162cac6e36SJilles Tjoelker 	char *q;
217670dd3f0SJilles Tjoelker 	size_t len, namelen;
2184b88c807SRodney W. Grimes 
2194b88c807SRodney W. Grimes 	if (*path == NULL)
2204b88c807SRodney W. Grimes 		return NULL;
2214b88c807SRodney W. Grimes 	start = *path;
2224600b569SJilles Tjoelker 	if (popt != NULL)
22335f2d3b6SRalf S. Engelschall 		for (p = start; *p && *p != ':' && *p != '%'; p++)
22435f2d3b6SRalf S. Engelschall 			; /* nothing */
2254600b569SJilles Tjoelker 	else
2264600b569SJilles Tjoelker 		for (p = start; *p && *p != ':'; p++)
2274600b569SJilles Tjoelker 			; /* nothing */
228670dd3f0SJilles Tjoelker 	namelen = strlen(name);
229670dd3f0SJilles Tjoelker 	len = p - start + namelen + 2;	/* "2" is for '/' and '\0' */
230d8f32e72SJilles Tjoelker 	STARTSTACKSTR(q);
231d8f32e72SJilles Tjoelker 	CHECKSTRSPACE(len, q);
2324b88c807SRodney W. Grimes 	if (p != start) {
233aa9caaf6SPeter Wemm 		memcpy(q, start, p - start);
2344b88c807SRodney W. Grimes 		q += p - start;
2354b88c807SRodney W. Grimes 		*q++ = '/';
2364b88c807SRodney W. Grimes 	}
237670dd3f0SJilles Tjoelker 	memcpy(q, name, namelen + 1);
2384600b569SJilles Tjoelker 	if (popt != NULL) {
2394b88c807SRodney W. Grimes 		if (*p == '%') {
2404600b569SJilles Tjoelker 			*popt = ++p;
2414b88c807SRodney W. Grimes 			while (*p && *p != ':')  p++;
2424600b569SJilles Tjoelker 		} else
2434600b569SJilles Tjoelker 			*popt = NULL;
2444b88c807SRodney W. Grimes 	}
2454b88c807SRodney W. Grimes 	if (*p == ':')
2464b88c807SRodney W. Grimes 		*path = p + 1;
2474b88c807SRodney W. Grimes 	else
2484b88c807SRodney W. Grimes 		*path = NULL;
2494b88c807SRodney W. Grimes 	return stalloc(len);
2504b88c807SRodney W. Grimes }
2514b88c807SRodney W. Grimes 
2524b88c807SRodney W. Grimes 
2534b88c807SRodney W. Grimes 
2544b88c807SRodney W. Grimes /*** Command hashing code ***/
2554b88c807SRodney W. Grimes 
2564b88c807SRodney W. Grimes 
257aa9caaf6SPeter Wemm int
2585134c3f7SWarner Losh hashcmd(int argc __unused, char **argv __unused)
259aa9caaf6SPeter Wemm {
2604b88c807SRodney W. Grimes 	struct tblentry **pp;
2614b88c807SRodney W. Grimes 	struct tblentry *cmdp;
2624b88c807SRodney W. Grimes 	int c;
2634b88c807SRodney W. Grimes 	int verbose;
2644b88c807SRodney W. Grimes 	struct cmdentry entry;
2654b88c807SRodney W. Grimes 	char *name;
266c0b3cf06SJilles Tjoelker 	int errors;
2674b88c807SRodney W. Grimes 
268c0b3cf06SJilles Tjoelker 	errors = 0;
2694b88c807SRodney W. Grimes 	verbose = 0;
2704b88c807SRodney W. Grimes 	while ((c = nextopt("rv")) != '\0') {
2714b88c807SRodney W. Grimes 		if (c == 'r') {
272c059d822SJilles Tjoelker 			clearcmdentry();
2734b88c807SRodney W. Grimes 		} else if (c == 'v') {
2744b88c807SRodney W. Grimes 			verbose++;
2754b88c807SRodney W. Grimes 		}
2764b88c807SRodney W. Grimes 	}
2774b88c807SRodney W. Grimes 	if (*argptr == NULL) {
2784b88c807SRodney W. Grimes 		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
2794b88c807SRodney W. Grimes 			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
28083952a11STim J. Robbins 				if (cmdp->cmdtype == CMDNORMAL)
2814b88c807SRodney W. Grimes 					printentry(cmdp, verbose);
2824b88c807SRodney W. Grimes 			}
2834b88c807SRodney W. Grimes 		}
2844b88c807SRodney W. Grimes 		return 0;
2854b88c807SRodney W. Grimes 	}
2864b88c807SRodney W. Grimes 	while ((name = *argptr) != NULL) {
2874b88c807SRodney W. Grimes 		if ((cmdp = cmdlookup(name, 0)) != NULL
2884b45b49aSJilles Tjoelker 		 && cmdp->cmdtype == CMDNORMAL)
2894b88c807SRodney W. Grimes 			delete_cmd_entry();
290c848bc18SJilles Tjoelker 		find_command(name, &entry, DO_ERR, pathval());
291c0b3cf06SJilles Tjoelker 		if (entry.cmdtype == CMDUNKNOWN)
292c0b3cf06SJilles Tjoelker 			errors = 1;
293c0b3cf06SJilles Tjoelker 		else if (verbose) {
2944b88c807SRodney W. Grimes 			cmdp = cmdlookup(name, 0);
2954c7e4a54SGeorge C A Reid 			if (cmdp != NULL)
2964b88c807SRodney W. Grimes 				printentry(cmdp, verbose);
297c0b3cf06SJilles Tjoelker 			else {
298f7cc73afSJilles Tjoelker 				outfmt(out2, "%s: not found\n", name);
299c0b3cf06SJilles Tjoelker 				errors = 1;
3004b88c807SRodney W. Grimes 			}
3014b88c807SRodney W. Grimes 			flushall();
3024b88c807SRodney W. Grimes 		}
3034b88c807SRodney W. Grimes 		argptr++;
3044b88c807SRodney W. Grimes 	}
305c0b3cf06SJilles Tjoelker 	return errors;
3064b88c807SRodney W. Grimes }
3074b88c807SRodney W. Grimes 
3084b88c807SRodney W. Grimes 
30988328642SDavid E. O'Brien static void
3105134c3f7SWarner Losh printentry(struct tblentry *cmdp, int verbose)
3114b88c807SRodney W. Grimes {
312384aedabSJilles Tjoelker 	int idx;
3134600b569SJilles Tjoelker 	const char *path, *opt;
3144b88c807SRodney W. Grimes 	char *name;
3154b88c807SRodney W. Grimes 
3164b88c807SRodney W. Grimes 	if (cmdp->cmdtype == CMDNORMAL) {
317384aedabSJilles Tjoelker 		idx = cmdp->param.index;
3184b88c807SRodney W. Grimes 		path = pathval();
3194b88c807SRodney W. Grimes 		do {
3204600b569SJilles Tjoelker 			name = padvance(&path, &opt, cmdp->cmdname);
3214b88c807SRodney W. Grimes 			stunalloc(name);
322384aedabSJilles Tjoelker 		} while (--idx >= 0);
3234b88c807SRodney W. Grimes 		out1str(name);
3244b88c807SRodney W. Grimes 	} else if (cmdp->cmdtype == CMDBUILTIN) {
3254b88c807SRodney W. Grimes 		out1fmt("builtin %s", cmdp->cmdname);
3264b88c807SRodney W. Grimes 	} else if (cmdp->cmdtype == CMDFUNCTION) {
3274b88c807SRodney W. Grimes 		out1fmt("function %s", cmdp->cmdname);
3284b88c807SRodney W. Grimes 		if (verbose) {
3294b88c807SRodney W. Grimes 			INTOFF;
330e16947f8SJilles Tjoelker 			name = commandtext(getfuncnode(cmdp->param.func));
3314b88c807SRodney W. Grimes 			out1c(' ');
3324b88c807SRodney W. Grimes 			out1str(name);
3334b88c807SRodney W. Grimes 			ckfree(name);
3344b88c807SRodney W. Grimes 			INTON;
3354b88c807SRodney W. Grimes 		}
3364b88c807SRodney W. Grimes #ifdef DEBUG
3374b88c807SRodney W. Grimes 	} else {
3384b88c807SRodney W. Grimes 		error("internal error: cmdtype %d", cmdp->cmdtype);
3394b88c807SRodney W. Grimes #endif
3404b88c807SRodney W. Grimes 	}
3414b88c807SRodney W. Grimes 	out1c('\n');
3424b88c807SRodney W. Grimes }
3434b88c807SRodney W. Grimes 
3444b88c807SRodney W. Grimes 
3454b88c807SRodney W. Grimes 
3464b88c807SRodney W. Grimes /*
3474b88c807SRodney W. Grimes  * Resolve a command name.  If you change this routine, you may have to
3484b88c807SRodney W. Grimes  * change the shellexec routine as well.
3494b88c807SRodney W. Grimes  */
3504b88c807SRodney W. Grimes 
3514b88c807SRodney W. Grimes void
352c848bc18SJilles Tjoelker find_command(const char *name, struct cmdentry *entry, int act,
3532cac6e36SJilles Tjoelker     const char *path)
3544b88c807SRodney W. Grimes {
355c848bc18SJilles Tjoelker 	struct tblentry *cmdp, loc_cmd;
356384aedabSJilles Tjoelker 	int idx;
3574600b569SJilles Tjoelker 	const char *opt;
3584b88c807SRodney W. Grimes 	char *fullname;
3594b88c807SRodney W. Grimes 	struct stat statb;
3604b88c807SRodney W. Grimes 	int e;
3614b88c807SRodney W. Grimes 	int i;
36285170a4aSStefan Farfeleder 	int spec;
363523646eeSJilles Tjoelker 	int cd;
3644b88c807SRodney W. Grimes 
3654b88c807SRodney W. Grimes 	/* If name contains a slash, don't use the hash table */
3664b88c807SRodney W. Grimes 	if (strchr(name, '/') != NULL) {
3674b88c807SRodney W. Grimes 		entry->cmdtype = CMDNORMAL;
3684b88c807SRodney W. Grimes 		entry->u.index = 0;
3697d980385SJilles Tjoelker 		entry->special = 0;
3704b88c807SRodney W. Grimes 		return;
3714b88c807SRodney W. Grimes 	}
3724b88c807SRodney W. Grimes 
373523646eeSJilles Tjoelker 	cd = 0;
374523646eeSJilles Tjoelker 
375b9807277SJilles Tjoelker 	/* If name is in the table, we're done */
376523646eeSJilles Tjoelker 	if ((cmdp = cmdlookup(name, 0)) != NULL) {
377c848bc18SJilles Tjoelker 		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
378c848bc18SJilles Tjoelker 			cmdp = NULL;
379c848bc18SJilles Tjoelker 		else
3804b88c807SRodney W. Grimes 			goto success;
381c848bc18SJilles Tjoelker 	}
3824b88c807SRodney W. Grimes 
3834b45b49aSJilles Tjoelker 	/* Check for builtin next */
3844b45b49aSJilles Tjoelker 	if ((i = find_builtin(name, &spec)) >= 0) {
3854b88c807SRodney W. Grimes 		INTOFF;
3864b88c807SRodney W. Grimes 		cmdp = cmdlookup(name, 1);
387c848bc18SJilles Tjoelker 		if (cmdp->cmdtype == CMDFUNCTION)
388c848bc18SJilles Tjoelker 			cmdp = &loc_cmd;
3894b88c807SRodney W. Grimes 		cmdp->cmdtype = CMDBUILTIN;
3904b88c807SRodney W. Grimes 		cmdp->param.index = i;
39185170a4aSStefan Farfeleder 		cmdp->special = spec;
3924b88c807SRodney W. Grimes 		INTON;
3934b88c807SRodney W. Grimes 		goto success;
3944b88c807SRodney W. Grimes 	}
3954b88c807SRodney W. Grimes 
3964b88c807SRodney W. Grimes 	/* We have to search path. */
3974b88c807SRodney W. Grimes 
3984b88c807SRodney W. Grimes 	e = ENOENT;
399384aedabSJilles Tjoelker 	idx = -1;
4004600b569SJilles Tjoelker 	for (;(fullname = padvance(&path, &opt, name)) != NULL;
4014600b569SJilles Tjoelker 	    stunalloc(fullname)) {
402384aedabSJilles Tjoelker 		idx++;
4034600b569SJilles Tjoelker 		if (opt) {
4044600b569SJilles Tjoelker 			if (strncmp(opt, "func", 4) == 0) {
4054b88c807SRodney W. Grimes 				/* handled below */
4064b88c807SRodney W. Grimes 			} else {
4072ceda702SJilles Tjoelker 				continue; /* ignore unimplemented options */
4084b88c807SRodney W. Grimes 			}
4094b88c807SRodney W. Grimes 		}
410523646eeSJilles Tjoelker 		if (fullname[0] != '/')
411523646eeSJilles Tjoelker 			cd = 1;
4122628ebdbSTim J. Robbins 		if (stat(fullname, &statb) < 0) {
4134b88c807SRodney W. Grimes 			if (errno != ENOENT && errno != ENOTDIR)
4144b88c807SRodney W. Grimes 				e = errno;
4152ceda702SJilles Tjoelker 			continue;
4164b88c807SRodney W. Grimes 		}
4174b88c807SRodney W. Grimes 		e = EACCES;	/* if we fail, this will be the error */
418aa9caaf6SPeter Wemm 		if (!S_ISREG(statb.st_mode))
4192ceda702SJilles Tjoelker 			continue;
4204600b569SJilles Tjoelker 		if (opt) {		/* this is a %func directory */
4214b88c807SRodney W. Grimes 			readcmdfile(fullname);
4224b88c807SRodney W. Grimes 			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
4234b88c807SRodney W. Grimes 				error("%s not defined in %s", name, fullname);
4244b88c807SRodney W. Grimes 			stunalloc(fullname);
4254b88c807SRodney W. Grimes 			goto success;
4264b88c807SRodney W. Grimes 		}
4274b88c807SRodney W. Grimes #ifdef notdef
4284b88c807SRodney W. Grimes 		if (statb.st_uid == geteuid()) {
4294b88c807SRodney W. Grimes 			if ((statb.st_mode & 0100) == 0)
4304b88c807SRodney W. Grimes 				goto loop;
4314b88c807SRodney W. Grimes 		} else if (statb.st_gid == getegid()) {
4324b88c807SRodney W. Grimes 			if ((statb.st_mode & 010) == 0)
4334b88c807SRodney W. Grimes 				goto loop;
4344b88c807SRodney W. Grimes 		} else {
4354b88c807SRodney W. Grimes 			if ((statb.st_mode & 01) == 0)
4364b88c807SRodney W. Grimes 				goto loop;
4374b88c807SRodney W. Grimes 		}
4384b88c807SRodney W. Grimes #endif
4394b88c807SRodney W. Grimes 		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
4404b88c807SRodney W. Grimes 		INTOFF;
4412ceda702SJilles Tjoelker 		stunalloc(fullname);
4424b88c807SRodney W. Grimes 		cmdp = cmdlookup(name, 1);
443c848bc18SJilles Tjoelker 		if (cmdp->cmdtype == CMDFUNCTION)
444c848bc18SJilles Tjoelker 			cmdp = &loc_cmd;
4454b88c807SRodney W. Grimes 		cmdp->cmdtype = CMDNORMAL;
446384aedabSJilles Tjoelker 		cmdp->param.index = idx;
4477d980385SJilles Tjoelker 		cmdp->special = 0;
4484b88c807SRodney W. Grimes 		INTON;
4494b88c807SRodney W. Grimes 		goto success;
4504b88c807SRodney W. Grimes 	}
4514b88c807SRodney W. Grimes 
452c848bc18SJilles Tjoelker 	if (act & DO_ERR) {
4538c395729STim J. Robbins 		if (e == ENOENT || e == ENOTDIR)
4548c395729STim J. Robbins 			outfmt(out2, "%s: not found\n", name);
4558c395729STim J. Robbins 		else
4561c59560dSTim J. Robbins 			outfmt(out2, "%s: %s\n", name, strerror(e));
4578c395729STim J. Robbins 	}
4584b88c807SRodney W. Grimes 	entry->cmdtype = CMDUNKNOWN;
459640b70e4SJilles Tjoelker 	entry->u.index = 0;
4607d980385SJilles Tjoelker 	entry->special = 0;
4614b88c807SRodney W. Grimes 	return;
4624b88c807SRodney W. Grimes 
4634b88c807SRodney W. Grimes success:
464523646eeSJilles Tjoelker 	if (cd)
465523646eeSJilles Tjoelker 		cmdtable_cd = 1;
4664b88c807SRodney W. Grimes 	entry->cmdtype = cmdp->cmdtype;
4674b88c807SRodney W. Grimes 	entry->u = cmdp->param;
46885170a4aSStefan Farfeleder 	entry->special = cmdp->special;
4694b88c807SRodney W. Grimes }
4704b88c807SRodney W. Grimes 
4714b88c807SRodney W. Grimes 
4724b88c807SRodney W. Grimes 
4734b88c807SRodney W. Grimes /*
4744b88c807SRodney W. Grimes  * Search the table of builtin commands.
4754b88c807SRodney W. Grimes  */
4764b88c807SRodney W. Grimes 
4774b88c807SRodney W. Grimes int
4782cac6e36SJilles Tjoelker find_builtin(const char *name, int *special)
4794b88c807SRodney W. Grimes {
480d3fa2c78SJilles Tjoelker 	const unsigned char *bp;
481d3fa2c78SJilles Tjoelker 	size_t len;
4824b88c807SRodney W. Grimes 
483d3fa2c78SJilles Tjoelker 	len = strlen(name);
484d3fa2c78SJilles Tjoelker 	for (bp = builtincmd ; *bp ; bp += 2 + bp[0]) {
485d3fa2c78SJilles Tjoelker 		if (bp[0] == len && memcmp(bp + 2, name, len) == 0) {
486d3fa2c78SJilles Tjoelker 			*special = (bp[1] & BUILTIN_SPECIAL) != 0;
487d3fa2c78SJilles Tjoelker 			return bp[1] & ~BUILTIN_SPECIAL;
4884b88c807SRodney W. Grimes 		}
48985170a4aSStefan Farfeleder 	}
4904b88c807SRodney W. Grimes 	return -1;
4914b88c807SRodney W. Grimes }
4924b88c807SRodney W. Grimes 
4934b88c807SRodney W. Grimes 
4944b88c807SRodney W. Grimes 
4954b88c807SRodney W. Grimes /*
496523646eeSJilles Tjoelker  * Called when a cd is done.  If any entry in cmdtable depends on the current
497523646eeSJilles Tjoelker  * directory, simply clear cmdtable completely.
4984b88c807SRodney W. Grimes  */
4994b88c807SRodney W. Grimes 
5004b88c807SRodney W. Grimes void
5015134c3f7SWarner Losh hashcd(void)
5025134c3f7SWarner Losh {
503523646eeSJilles Tjoelker 	if (cmdtable_cd)
504523646eeSJilles Tjoelker 		clearcmdentry();
5054b88c807SRodney W. Grimes }
5064b88c807SRodney W. Grimes 
5074b88c807SRodney W. Grimes 
5084b88c807SRodney W. Grimes 
5094b88c807SRodney W. Grimes /*
5104b88c807SRodney W. Grimes  * Called before PATH is changed.  The argument is the new value of PATH;
5114b88c807SRodney W. Grimes  * pathval() still returns the old value at this point.  Called with
5124b88c807SRodney W. Grimes  * interrupts off.
5134b88c807SRodney W. Grimes  */
5144b88c807SRodney W. Grimes 
5154b88c807SRodney W. Grimes void
5162fae4c3dSPhilippe Charnier changepath(const char *newval __unused)
5174b88c807SRodney W. Grimes {
518c059d822SJilles Tjoelker 	clearcmdentry();
5194b88c807SRodney W. Grimes }
5204b88c807SRodney W. Grimes 
5214b88c807SRodney W. Grimes 
5224b88c807SRodney W. Grimes /*
523b9807277SJilles Tjoelker  * Clear out cached utility locations.
5244b88c807SRodney W. Grimes  */
5254b88c807SRodney W. Grimes 
526a436dc79SMartin Cracauer void
527c059d822SJilles Tjoelker clearcmdentry(void)
528aa9caaf6SPeter Wemm {
5294b88c807SRodney W. Grimes 	struct tblentry **tblp;
5304b88c807SRodney W. Grimes 	struct tblentry **pp;
5314b88c807SRodney W. Grimes 	struct tblentry *cmdp;
5324b88c807SRodney W. Grimes 
5334b88c807SRodney W. Grimes 	INTOFF;
5344b88c807SRodney W. Grimes 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
5354b88c807SRodney W. Grimes 		pp = tblp;
5364b88c807SRodney W. Grimes 		while ((cmdp = *pp) != NULL) {
537c059d822SJilles Tjoelker 			if (cmdp->cmdtype == CMDNORMAL) {
5384b88c807SRodney W. Grimes 				*pp = cmdp->next;
5394b88c807SRodney W. Grimes 				ckfree(cmdp);
5404b88c807SRodney W. Grimes 			} else {
5414b88c807SRodney W. Grimes 				pp = &cmdp->next;
5424b88c807SRodney W. Grimes 			}
5434b88c807SRodney W. Grimes 		}
5444b88c807SRodney W. Grimes 	}
545523646eeSJilles Tjoelker 	cmdtable_cd = 0;
5464b88c807SRodney W. Grimes 	INTON;
5474b88c807SRodney W. Grimes }
5484b88c807SRodney W. Grimes 
5494b88c807SRodney W. Grimes 
5504b88c807SRodney W. Grimes /*
5514b88c807SRodney W. Grimes  * Locate a command in the command hash table.  If "add" is nonzero,
5524b88c807SRodney W. Grimes  * add the command to the table if it is not already present.  The
5534b88c807SRodney W. Grimes  * variable "lastcmdentry" is set to point to the address of the link
5544b88c807SRodney W. Grimes  * pointing to the entry, so that delete_cmd_entry can delete the
5554b88c807SRodney W. Grimes  * entry.
5564b88c807SRodney W. Grimes  */
5574b88c807SRodney W. Grimes 
558aa7b6f82SDavid E. O'Brien static struct tblentry **lastcmdentry;
5594b88c807SRodney W. Grimes 
5604b88c807SRodney W. Grimes 
56188328642SDavid E. O'Brien static struct tblentry *
5622cac6e36SJilles Tjoelker cmdlookup(const char *name, int add)
5634b88c807SRodney W. Grimes {
564c3c85727SJilles Tjoelker 	unsigned int hashval;
5652cac6e36SJilles Tjoelker 	const char *p;
5664b88c807SRodney W. Grimes 	struct tblentry *cmdp;
5674b88c807SRodney W. Grimes 	struct tblentry **pp;
568670dd3f0SJilles Tjoelker 	size_t len;
5694b88c807SRodney W. Grimes 
5704b88c807SRodney W. Grimes 	p = name;
571c3c85727SJilles Tjoelker 	hashval = (unsigned char)*p << 4;
5724b88c807SRodney W. Grimes 	while (*p)
5734b88c807SRodney W. Grimes 		hashval += *p++;
5744b88c807SRodney W. Grimes 	pp = &cmdtable[hashval % CMDTABLESIZE];
5754b88c807SRodney W. Grimes 	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
5764b88c807SRodney W. Grimes 		if (equal(cmdp->cmdname, name))
5774b88c807SRodney W. Grimes 			break;
5784b88c807SRodney W. Grimes 		pp = &cmdp->next;
5794b88c807SRodney W. Grimes 	}
5804b88c807SRodney W. Grimes 	if (add && cmdp == NULL) {
5814b88c807SRodney W. Grimes 		INTOFF;
582670dd3f0SJilles Tjoelker 		len = strlen(name);
583670dd3f0SJilles Tjoelker 		cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
5844b88c807SRodney W. Grimes 		cmdp->next = NULL;
5854b88c807SRodney W. Grimes 		cmdp->cmdtype = CMDUNKNOWN;
586670dd3f0SJilles Tjoelker 		memcpy(cmdp->cmdname, name, len + 1);
5874b88c807SRodney W. Grimes 		INTON;
5884b88c807SRodney W. Grimes 	}
5894b88c807SRodney W. Grimes 	lastcmdentry = pp;
5904b88c807SRodney W. Grimes 	return cmdp;
5914b88c807SRodney W. Grimes }
5924b88c807SRodney W. Grimes 
5934b88c807SRodney W. Grimes /*
5944b88c807SRodney W. Grimes  * Delete the command entry returned on the last lookup.
5954b88c807SRodney W. Grimes  */
5964b88c807SRodney W. Grimes 
59788328642SDavid E. O'Brien static void
5985134c3f7SWarner Losh delete_cmd_entry(void)
5995134c3f7SWarner Losh {
6004b88c807SRodney W. Grimes 	struct tblentry *cmdp;
6014b88c807SRodney W. Grimes 
6024b88c807SRodney W. Grimes 	INTOFF;
6034b88c807SRodney W. Grimes 	cmdp = *lastcmdentry;
6044b88c807SRodney W. Grimes 	*lastcmdentry = cmdp->next;
6054b88c807SRodney W. Grimes 	ckfree(cmdp);
6064b88c807SRodney W. Grimes 	INTON;
6074b88c807SRodney W. Grimes }
6084b88c807SRodney W. Grimes 
6094b88c807SRodney W. Grimes 
6104b88c807SRodney W. Grimes 
6114b88c807SRodney W. Grimes /*
6124b88c807SRodney W. Grimes  * Add a new command entry, replacing any existing command entry for
6134b88c807SRodney W. Grimes  * the same name.
6144b88c807SRodney W. Grimes  */
6154b88c807SRodney W. Grimes 
616260fc3f4SJilles Tjoelker static void
6172cac6e36SJilles Tjoelker addcmdentry(const char *name, struct cmdentry *entry)
6184b88c807SRodney W. Grimes {
6194b88c807SRodney W. Grimes 	struct tblentry *cmdp;
6204b88c807SRodney W. Grimes 
6214b88c807SRodney W. Grimes 	INTOFF;
6224b88c807SRodney W. Grimes 	cmdp = cmdlookup(name, 1);
6234b88c807SRodney W. Grimes 	if (cmdp->cmdtype == CMDFUNCTION) {
624eb33e843SJilles Tjoelker 		unreffunc(cmdp->param.func);
6254b88c807SRodney W. Grimes 	}
6264b88c807SRodney W. Grimes 	cmdp->cmdtype = entry->cmdtype;
6274b88c807SRodney W. Grimes 	cmdp->param = entry->u;
6287d980385SJilles Tjoelker 	cmdp->special = entry->special;
6294b88c807SRodney W. Grimes 	INTON;
6304b88c807SRodney W. Grimes }
6314b88c807SRodney W. Grimes 
6324b88c807SRodney W. Grimes 
6334b88c807SRodney W. Grimes /*
6344b88c807SRodney W. Grimes  * Define a shell function.
6354b88c807SRodney W. Grimes  */
6364b88c807SRodney W. Grimes 
6374b88c807SRodney W. Grimes void
6382cac6e36SJilles Tjoelker defun(const char *name, union node *func)
6394b88c807SRodney W. Grimes {
6404b88c807SRodney W. Grimes 	struct cmdentry entry;
6414b88c807SRodney W. Grimes 
6424b88c807SRodney W. Grimes 	INTOFF;
6434b88c807SRodney W. Grimes 	entry.cmdtype = CMDFUNCTION;
6444b88c807SRodney W. Grimes 	entry.u.func = copyfunc(func);
6457d980385SJilles Tjoelker 	entry.special = 0;
6464b88c807SRodney W. Grimes 	addcmdentry(name, &entry);
6474b88c807SRodney W. Grimes 	INTON;
6484b88c807SRodney W. Grimes }
6494b88c807SRodney W. Grimes 
6504b88c807SRodney W. Grimes 
6514b88c807SRodney W. Grimes /*
6524b88c807SRodney W. Grimes  * Delete a function if it exists.
6531632bf1aSJilles Tjoelker  * Called with interrupts off.
6544b88c807SRodney W. Grimes  */
6554b88c807SRodney W. Grimes 
6564b88c807SRodney W. Grimes int
6572cac6e36SJilles Tjoelker unsetfunc(const char *name)
6584b88c807SRodney W. Grimes {
6594b88c807SRodney W. Grimes 	struct tblentry *cmdp;
6604b88c807SRodney W. Grimes 
6614b88c807SRodney W. Grimes 	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
662eb33e843SJilles Tjoelker 		unreffunc(cmdp->param.func);
6634b88c807SRodney W. Grimes 		delete_cmd_entry();
6644b88c807SRodney W. Grimes 		return (0);
6654b88c807SRodney W. Grimes 	}
666bd766773SDag-Erling Smørgrav 	return (0);
6674b88c807SRodney W. Grimes }
66876ad65f7SSteve Price 
66984fbdd8cSJilles Tjoelker 
67084fbdd8cSJilles Tjoelker /*
67184fbdd8cSJilles Tjoelker  * Check if a function by a certain name exists.
67284fbdd8cSJilles Tjoelker  */
67384fbdd8cSJilles Tjoelker int
67484fbdd8cSJilles Tjoelker isfunc(const char *name)
67584fbdd8cSJilles Tjoelker {
67684fbdd8cSJilles Tjoelker 	struct tblentry *cmdp;
67784fbdd8cSJilles Tjoelker 	cmdp = cmdlookup(name, 0);
67884fbdd8cSJilles Tjoelker 	return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
67984fbdd8cSJilles Tjoelker }
68084fbdd8cSJilles Tjoelker 
68184fbdd8cSJilles Tjoelker 
68276ad65f7SSteve Price /*
683b2f153feSStefan Farfeleder  * Shared code for the following builtin commands:
684b2f153feSStefan Farfeleder  *    type, command -v, command -V
68576ad65f7SSteve Price  */
68676ad65f7SSteve Price 
68776ad65f7SSteve Price int
68806a8a57fSJilles Tjoelker typecmd_impl(int argc, char **argv, int cmd, const char *path)
68976ad65f7SSteve Price {
69076ad65f7SSteve Price 	struct cmdentry entry;
69176ad65f7SSteve Price 	struct tblentry *cmdp;
692384aedabSJilles Tjoelker 	const char *const *pp;
69376ad65f7SSteve Price 	struct alias *ap;
69476ad65f7SSteve Price 	int i;
695384aedabSJilles Tjoelker 	int error1 = 0;
69676ad65f7SSteve Price 
69706a8a57fSJilles Tjoelker 	if (path != pathval())
698c059d822SJilles Tjoelker 		clearcmdentry();
69906a8a57fSJilles Tjoelker 
70076ad65f7SSteve Price 	for (i = 1; i < argc; i++) {
70176ad65f7SSteve Price 		/* First look at the keywords */
702384aedabSJilles Tjoelker 		for (pp = parsekwd; *pp; pp++)
70376ad65f7SSteve Price 			if (**pp == *argv[i] && equal(*pp, argv[i]))
70476ad65f7SSteve Price 				break;
70576ad65f7SSteve Price 
70676ad65f7SSteve Price 		if (*pp) {
707b2f153feSStefan Farfeleder 			if (cmd == TYPECMD_SMALLV)
708b2f153feSStefan Farfeleder 				out1fmt("%s\n", argv[i]);
709b2f153feSStefan Farfeleder 			else
710f7bbf3ffSStefan Farfeleder 				out1fmt("%s is a shell keyword\n", argv[i]);
71176ad65f7SSteve Price 			continue;
71276ad65f7SSteve Price 		}
71376ad65f7SSteve Price 
71476ad65f7SSteve Price 		/* Then look at the aliases */
71576ad65f7SSteve Price 		if ((ap = lookupalias(argv[i], 1)) != NULL) {
7165d4d10e3SJilles Tjoelker 			if (cmd == TYPECMD_SMALLV) {
7175d4d10e3SJilles Tjoelker 				out1fmt("alias %s=", argv[i]);
7185d4d10e3SJilles Tjoelker 				out1qstr(ap->val);
7195d4d10e3SJilles Tjoelker 				outcslow('\n', out1);
7205d4d10e3SJilles Tjoelker 			} else
721f7bbf3ffSStefan Farfeleder 				out1fmt("%s is an alias for %s\n", argv[i],
722f7bbf3ffSStefan Farfeleder 				    ap->val);
72376ad65f7SSteve Price 			continue;
72476ad65f7SSteve Price 		}
72576ad65f7SSteve Price 
72676ad65f7SSteve Price 		/* Then check if it is a tracked alias */
72776ad65f7SSteve Price 		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
72876ad65f7SSteve Price 			entry.cmdtype = cmdp->cmdtype;
72976ad65f7SSteve Price 			entry.u = cmdp->param;
73030268dfaSJilles Tjoelker 			entry.special = cmdp->special;
73176ad65f7SSteve Price 		}
73276ad65f7SSteve Price 		else {
73376ad65f7SSteve Price 			/* Finally use brute force */
73406a8a57fSJilles Tjoelker 			find_command(argv[i], &entry, 0, path);
73576ad65f7SSteve Price 		}
73676ad65f7SSteve Price 
73776ad65f7SSteve Price 		switch (entry.cmdtype) {
73876ad65f7SSteve Price 		case CMDNORMAL: {
739d753a425SMartin Cracauer 			if (strchr(argv[i], '/') == NULL) {
74006a8a57fSJilles Tjoelker 				const char *path2 = path;
7414600b569SJilles Tjoelker 				const char *opt2;
7422cac6e36SJilles Tjoelker 				char *name;
743d753a425SMartin Cracauer 				int j = entry.u.index;
74476ad65f7SSteve Price 				do {
7454600b569SJilles Tjoelker 					name = padvance(&path2, &opt2, argv[i]);
74676ad65f7SSteve Price 					stunalloc(name);
74776ad65f7SSteve Price 				} while (--j >= 0);
748b2f153feSStefan Farfeleder 				if (cmd == TYPECMD_SMALLV)
749b2f153feSStefan Farfeleder 					out1fmt("%s\n", name);
750b2f153feSStefan Farfeleder 				else
751f7bbf3ffSStefan Farfeleder 					out1fmt("%s is%s %s\n", argv[i],
752b2f153feSStefan Farfeleder 					    (cmdp && cmd == TYPECMD_TYPE) ?
753b2f153feSStefan Farfeleder 						" a tracked alias for" : "",
754b2f153feSStefan Farfeleder 					    name);
755d753a425SMartin Cracauer 			} else {
756d92e35fdSStefan Farfeleder 				if (eaccess(argv[i], X_OK) == 0) {
757b2f153feSStefan Farfeleder 					if (cmd == TYPECMD_SMALLV)
758b2f153feSStefan Farfeleder 						out1fmt("%s\n", argv[i]);
759b2f153feSStefan Farfeleder 					else
760f7bbf3ffSStefan Farfeleder 						out1fmt("%s is %s\n", argv[i],
761f7bbf3ffSStefan Farfeleder 						    argv[i]);
762f30842baSStefan Farfeleder 				} else {
763f30842baSStefan Farfeleder 					if (cmd != TYPECMD_SMALLV)
764f7bbf3ffSStefan Farfeleder 						outfmt(out2, "%s: %s\n",
765f7bbf3ffSStefan Farfeleder 						    argv[i], strerror(errno));
766384aedabSJilles Tjoelker 					error1 |= 127;
767b2f153feSStefan Farfeleder 				}
768d753a425SMartin Cracauer 			}
76976ad65f7SSteve Price 			break;
77076ad65f7SSteve Price 		}
77176ad65f7SSteve Price 		case CMDFUNCTION:
772b2f153feSStefan Farfeleder 			if (cmd == TYPECMD_SMALLV)
773b2f153feSStefan Farfeleder 				out1fmt("%s\n", argv[i]);
774b2f153feSStefan Farfeleder 			else
775f7bbf3ffSStefan Farfeleder 				out1fmt("%s is a shell function\n", argv[i]);
77676ad65f7SSteve Price 			break;
77776ad65f7SSteve Price 
77876ad65f7SSteve Price 		case CMDBUILTIN:
779b2f153feSStefan Farfeleder 			if (cmd == TYPECMD_SMALLV)
780b2f153feSStefan Farfeleder 				out1fmt("%s\n", argv[i]);
78130268dfaSJilles Tjoelker 			else if (entry.special)
78230268dfaSJilles Tjoelker 				out1fmt("%s is a special shell builtin\n",
78330268dfaSJilles Tjoelker 				    argv[i]);
784b2f153feSStefan Farfeleder 			else
785f7bbf3ffSStefan Farfeleder 				out1fmt("%s is a shell builtin\n", argv[i]);
78676ad65f7SSteve Price 			break;
78776ad65f7SSteve Price 
78876ad65f7SSteve Price 		default:
789b2f153feSStefan Farfeleder 			if (cmd != TYPECMD_SMALLV)
790f7bbf3ffSStefan Farfeleder 				outfmt(out2, "%s: not found\n", argv[i]);
791384aedabSJilles Tjoelker 			error1 |= 127;
79276ad65f7SSteve Price 			break;
79376ad65f7SSteve Price 		}
79476ad65f7SSteve Price 	}
79506a8a57fSJilles Tjoelker 
79606a8a57fSJilles Tjoelker 	if (path != pathval())
797c059d822SJilles Tjoelker 		clearcmdentry();
79806a8a57fSJilles Tjoelker 
799384aedabSJilles Tjoelker 	return error1;
80076ad65f7SSteve Price }
801b2f153feSStefan Farfeleder 
802b2f153feSStefan Farfeleder /*
803b2f153feSStefan Farfeleder  * Locate and print what a word is...
804b2f153feSStefan Farfeleder  */
805b2f153feSStefan Farfeleder 
806b2f153feSStefan Farfeleder int
807b2f153feSStefan Farfeleder typecmd(int argc, char **argv)
808b2f153feSStefan Farfeleder {
80965519ccbSJilles Tjoelker 	if (argc > 2 && strcmp(argv[1], "--") == 0)
81065519ccbSJilles Tjoelker 		argc--, argv++;
8110fb60646SJilles Tjoelker 	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
812b2f153feSStefan Farfeleder }
813