exec.c revision 218323
1757Sdg/*-
299703Sjulian * Copyright (c) 1991, 1993
3757Sdg *	The Regents of the University of California.  All rights reserved.
4174395Sjkoshy *
5757Sdg * This code is derived from software contributed to Berkeley by
6757Sdg * Kenneth Almquist.
7174395Sjkoshy *
8174395Sjkoshy * Redistribution and use in source and binary forms, with or without
9174395Sjkoshy * modification, are permitted provided that the following conditions
10757Sdg * are met:
11757Sdg * 1. Redistributions of source code must retain the above copyright
12757Sdg *    notice, this list of conditions and the following disclaimer.
13757Sdg * 2. Redistributions in binary form must reproduce the above copyright
14757Sdg *    notice, this list of conditions and the following disclaimer in the
15757Sdg *    documentation and/or other materials provided with the distribution.
16757Sdg * 4. Neither the name of the University nor the names of its contributors
17757Sdg *    may be used to endorse or promote products derived from this software
18757Sdg *    without specific prior written permission.
19757Sdg *
20757Sdg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21757Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22757Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23757Sdg * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24757Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25757Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26757Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27757Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28757Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29757Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30757Sdg * SUCH DAMAGE.
31757Sdg */
32757Sdg
33757Sdg#ifndef lint
3450477Speter#if 0
35757Sdgstatic char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
36757Sdg#endif
37129742Sbde#endif /* not lint */
38232744Sjhb#include <sys/cdefs.h>
39174395Sjkoshy__FBSDID("$FreeBSD: head/bin/sh/exec.c 218323 2011-02-05 14:01:46Z jilles $");
40179277Sjb
4171257Speter#include <sys/types.h>
4230265Speter#include <sys/stat.h>
4328921Sfsmp#include <unistd.h>
4430786Sbde#include <fcntl.h>
4530786Sbde#include <errno.h>
46757Sdg#include <paths.h>
4730786Sbde#include <stdlib.h>
4830786Sbde
49757Sdg/*
50179304Sattilio * When commands are first encountered, they are entered in a hash table.
51757Sdg * This ensures that a full path search will not have to be done for them
52179277Sjb * on each invocation.
53179277Sjb *
54179277Sjb * We should investigate converting to a linear search, even though that
55179277Sjb * would make the command name "hash" a misnomer.
56179277Sjb */
57251988Skib
58179277Sjb#include "shell.h"
59179277Sjb#include "main.h"
60179277Sjb#include "nodes.h"
61179277Sjb#include "parser.h"
62179277Sjb#include "redir.h"
63251988Skib#include "eval.h"
64179277Sjb#include "exec.h"
65179277Sjb#include "builtins.h"
66179277Sjb#include "var.h"
67757Sdg#include "options.h"
68174395Sjkoshy#include "input.h"
69174395Sjkoshy#include "output.h"
70174395Sjkoshy#include "syntax.h"
71757Sdg#include "memalloc.h"
72757Sdg#include "error.h"
73757Sdg#include "init.h"
74757Sdg#include "mystring.h"
7558717Sdillon#include "show.h"
7658717Sdillon#include "jobs.h"
7758717Sdillon#include "alias.h"
78251988Skib
79251988Skib
8058717Sdillon#define CMDTABLESIZE 31		/* should be prime */
8158717Sdillon#define ARB 1			/* actual size determined at run time */
8258717Sdillon
8358717Sdillon
84251988Skib
85251988Skibstruct tblentry {
86251988Skib	struct tblentry *next;	/* next entry in hash chain */
8758717Sdillon	union param param;	/* definition of builtin function */
88251988Skib	int special;		/* flag for special builtin commands */
89251988Skib	short cmdtype;		/* index identifying command */
90251988Skib	char rehash;		/* if set, cd done since entry created */
9158717Sdillon	char cmdname[ARB];	/* name of command */
9258717Sdillon};
93251988Skib
9458717Sdillon
9558717Sdillonstatic struct tblentry *cmdtable[CMDTABLESIZE];
9658717Sdillonint exerrno = 0;			/* Last exec error */
971321Sdg
981321Sdg
991321Sdgstatic void tryexec(char *, char **, char **);
1001321Sdgstatic void printentry(struct tblentry *, int);
1011321Sdgstatic struct tblentry *cmdlookup(const char *, int);
102153135Sjhbstatic void delete_cmd_entry(void);
103153135Sjhb
104757Sdg
105757Sdg
106757Sdg/*
10781583Sbde * Exec a program.  Never returns.  If you change this routine, you may
108147950Sjkoshy * have to change the find_command routine as well.
109147950Sjkoshy *
110757Sdg * The argv array may be changed and element argv[-1] should be writable.
11181583Sbde */
112211924Srpaulo
113211924Srpaulovoid
114757Sdgshellexec(char **argv, char **envp, const char *path, int idx)
115757Sdg{
116757Sdg	char *cmdname;
117757Sdg	int e;
118179292Sbz
119179292Sbz	if (strchr(argv[0], '/') != NULL) {
120179292Sbz		tryexec(argv[0], argv, envp);
121179292Sbz		e = errno;
122757Sdg	} else {
123757Sdg		e = ENOENT;
124757Sdg		while ((cmdname = padvance(&path, argv[0])) != NULL) {
125757Sdg			if (--idx < 0 && pathopt == NULL) {
126757Sdg				tryexec(cmdname, argv, envp);
127757Sdg				if (errno != ENOENT && errno != ENOTDIR)
128757Sdg					e = errno;
129757Sdg				if (e == ENOEXEC)
130757Sdg					break;
131757Sdg			}
132757Sdg			stunalloc(cmdname);
133757Sdg		}
134757Sdg	}
135757Sdg
13617521Sdg	/* Map to POSIX errors */
13717521Sdg	if (e == ENOENT || e == ENOTDIR) {
138757Sdg		exerrno = 127;
139757Sdg		exerror(EXEXEC, "%s: not found", argv[0]);
140757Sdg	} else {
141757Sdg		exerrno = 126;
1425603Sbde		exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
1435603Sbde	}
14479609Speter}
14579609Speter
146139448Sjhb
14758717Sdillonstatic void
148251988Skibtryexec(char *cmd, char **argv, char **envp)
149251988Skib{
150251988Skib	int e, in;
151251988Skib	ssize_t n;
152251988Skib	char buf[256];
153251988Skib
154157453Sjkoshy	execve(cmd, argv, envp);
15558717Sdillon	e = errno;
156757Sdg	if (e == ENOEXEC) {
15773011Sjake		INTOFF;
15873011Sjake		in = open(cmd, O_RDONLY | O_NONBLOCK);
15973011Sjake		if (in != -1) {
160757Sdg			n = pread(in, buf, sizeof buf, 0);
161290668Sjhb			close(in);
162290668Sjhb			if (n > 0 && memchr(buf, '\0', n) != NULL) {
163290668Sjhb				errno = ENOEXEC;
164290668Sjhb				return;
165290668Sjhb			}
166290668Sjhb		}
1675603Sbde		*argv = cmd;
168153135Sjhb		*--argv = _PATH_BSHELL;
169209483Skib		execve(_PATH_BSHELL, argv, envp);
170129620Sbde	}
171757Sdg	errno = e;
172165302Skmacy}
17373011Sjake
174165302Skmacy/*
175251988Skib * Do a path search.  The variable path (passed by reference) should be
176757Sdg * set to the start of the path before the first call; padvance will update
17773011Sjake * this value as it proceeds.  Successive calls to padvance will return
178757Sdg * the possible path expansions in sequence.  If an option (indicated by
1791321Sdg * a percent sign) appears in the path entry then the global variable
18073011Sjake * pathopt will be set to point to it; otherwise pathopt will be set to
181757Sdg * NULL.
182757Sdg */
183179277Sjb
184179277Sjbconst char *pathopt;
185179292Sbz
186179277Sjbchar *
187179277Sjbpadvance(const char **path, const char *name)
188322755Skib{
189322755Skib	const char *p, *start;
190322755Skib	char *q;
191322755Skib	int len;
192322755Skib
193322755Skib	if (*path == NULL)
194179277Sjb		return NULL;
195179277Sjb	start = *path;
196322755Skib	for (p = start; *p && *p != ':' && *p != '%'; p++)
197322755Skib		; /* nothing */
198322755Skib	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
199322755Skib	STARTSTACKSTR(q);
200322755Skib	CHECKSTRSPACE(len, q);
201179277Sjb	if (p != start) {
202322755Skib		memcpy(q, start, p - start);
203322755Skib		q += p - start;
204251988Skib		*q++ = '/';
205179277Sjb	}
206179277Sjb	strcpy(q, name);
207179277Sjb	pathopt = NULL;
208179277Sjb	if (*p == '%') {
209322755Skib		pathopt = ++p;
210322755Skib		while (*p && *p != ':')  p++;
211179277Sjb	}
212179277Sjb	if (*p == ':')
213179277Sjb		*path = p + 1;
214179277Sjb	else
215179277Sjb		*path = NULL;
216179277Sjb	return stalloc(len);
217179277Sjb}
218179277Sjb
219179277Sjb
220179277Sjb
221179277Sjb/*** Command hashing code ***/
222179277Sjb
223179277Sjb
224179277Sjbint
225179277Sjbhashcmd(int argc __unused, char **argv __unused)
226179277Sjb{
227179292Sbz	struct tblentry **pp;
228179277Sjb	struct tblentry *cmdp;
229179277Sjb	int c;
230251988Skib	int verbose;
231251988Skib	struct cmdentry entry;
23258717Sdillon	char *name;
23310609Sdg
23410609Sdg	verbose = 0;
23510609Sdg	while ((c = nextopt("rv")) != '\0') {
23610609Sdg		if (c == 'r') {
23710609Sdg			clearcmdentry(0);
23810609Sdg		} else if (c == 'v') {
23910609Sdg			verbose++;
240757Sdg		}
241757Sdg	}
24273001Sjake	if (*argptr == NULL) {
24373001Sjake		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
24473001Sjake			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
24573001Sjake				if (cmdp->cmdtype == CMDNORMAL)
246302983Skib					printentry(cmdp, verbose);
247100781Speter			}
248290668Sjhb		}
249290668Sjhb		return 0;
250290668Sjhb	}
251290668Sjhb	while ((name = *argptr) != NULL) {
252290668Sjhb		if ((cmdp = cmdlookup(name, 0)) != NULL
253290668Sjhb		 && cmdp->cmdtype == CMDNORMAL)
254153135Sjhb			delete_cmd_entry();
255209483Skib		find_command(name, &entry, DO_ERR, pathval());
256129620Sbde		if (verbose) {
257165302Skmacy			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
258100781Speter				cmdp = cmdlookup(name, 0);
259165302Skmacy				if (cmdp != NULL)
260100781Speter					printentry(cmdp, verbose);
261100781Speter				else
262757Sdg					outfmt(out2, "%s: not found\n", name);
263757Sdg			}
264251988Skib			flushall();
265251988Skib		}
266251988Skib		argptr++;
26758717Sdillon	}
268251988Skib	return 0;
269251988Skib}
270251988Skib
2716380Ssos
2726380Ssosstatic void
27314331Speterprintentry(struct tblentry *cmdp, int verbose)
27473001Sjake{
275302983Skib	int idx;
2766380Ssos	const char *path;
277290668Sjhb	char *name;
278290668Sjhb
279290668Sjhb	if (cmdp->cmdtype == CMDNORMAL) {
280290668Sjhb		idx = cmdp->param.index;
281290668Sjhb		path = pathval();
282290668Sjhb		do {
283153135Sjhb			name = padvance(&path, cmdp->cmdname);
284209483Skib			stunalloc(name);
285129620Sbde		} while (--idx >= 0);
286165302Skmacy		out1str(name);
28773011Sjake	} else if (cmdp->cmdtype == CMDBUILTIN) {
288165302Skmacy		out1fmt("builtin %s", cmdp->cmdname);
28958717Sdillon	} else if (cmdp->cmdtype == CMDFUNCTION) {
29073011Sjake		out1fmt("function %s", cmdp->cmdname);
2916380Ssos		if (verbose) {
29224691Speter			INTOFF;
29371604Sjhb			name = commandtext(getfuncnode(cmdp->param.func));
29428043Sfsmp			out1c(' ');
29571522Sjhb			out1str(name);
29673011Sjake			ckfree(name);
29771604Sjhb			INTON;
29824691Speter		}
29928641Sfsmp#ifdef DEBUG
30024691Speter	} else {
30173011Sjake		error("internal error: cmdtype %d", cmdp->cmdtype);
30224691Speter#endif
30324691Speter	}
30473011Sjake	if (cmdp->rehash)
30524691Speter		out1c('*');
30628641Sfsmp	out1c('\n');
3076380Ssos}
308129742Sbde
309129742Sbde
310129742Sbde
311129742Sbde/*
312129742Sbde * Resolve a command name.  If you change this routine, you may have to
313129742Sbde * change the shellexec routine as well.
314129742Sbde */
31534840Sjlemon
31634840Sjlemonvoid
31799703Sjulianfind_command(const char *name, struct cmdentry *entry, int act,
318129742Sbde    const char *path)
319129742Sbde{
320129742Sbde	struct tblentry *cmdp, loc_cmd;
321129742Sbde	int idx;
32299703Sjulian	int prev;
323232744Sjhb	char *fullname;
324204309Sattilio	struct stat statb;
325232744Sjhb	int e;
326129742Sbde	int i;
327232744Sjhb	int spec;
328129742Sbde
329129742Sbde	/* If name contains a slash, don't use the hash table */
330129742Sbde	if (strchr(name, '/') != NULL) {
331129742Sbde		entry->cmdtype = CMDNORMAL;
332232744Sjhb		entry->u.index = 0;
333129742Sbde		return;
334232744Sjhb	}
335129742Sbde
336129742Sbde	/* If name is in the table, and not invalidated by cd, we're done */
337129742Sbde	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
338129742Sbde		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
339129742Sbde			cmdp = NULL;
340129742Sbde		else
341129742Sbde			goto success;
342129742Sbde	}
343129742Sbde
344129742Sbde	/* Check for builtin next */
345129742Sbde	if ((i = find_builtin(name, &spec)) >= 0) {
346129742Sbde		INTOFF;
3473156Sbde		cmdp = cmdlookup(name, 1);
34899703Sjulian		if (cmdp->cmdtype == CMDFUNCTION)
34999703Sjulian			cmdp = &loc_cmd;
35099703Sjulian		cmdp->cmdtype = CMDBUILTIN;
3513156Sbde		cmdp->param.index = i;
3523156Sbde		cmdp->special = spec;
3533156Sbde		INTON;
35499703Sjulian		goto success;
355302041Ssephe	}
35699703Sjulian
357129624Sbde	/* We have to search path. */
35899703Sjulian	prev = -1;		/* where to start */
35999703Sjulian	if (cmdp) {		/* doing a rehash */
360174395Sjkoshy		if (cmdp->cmdtype == CMDBUILTIN)
361174395Sjkoshy			prev = -1;
36299703Sjulian		else
363174395Sjkoshy			prev = cmdp->param.index;
364174395Sjkoshy	}
365174395Sjkoshy
366174395Sjkoshy	e = ENOENT;
367174395Sjkoshy	idx = -1;
368174395Sjkoshyloop:
369174395Sjkoshy	while ((fullname = padvance(&path, name)) != NULL) {
370174395Sjkoshy		stunalloc(fullname);
371174395Sjkoshy		idx++;
372251988Skib		if (pathopt) {
373174395Sjkoshy			if (prefix("func", pathopt)) {
374251988Skib				/* handled below */
37599703Sjulian			} else {
376106542Sdavidxu				goto loop;	/* ignore unimplemented options */
377251988Skib			}
378251988Skib		}
379251988Skib		/* if rehash, don't redo absolute path names */
38099703Sjulian		if (fullname[0] == '/' && idx <= prev) {
38199703Sjulian			if (idx < prev)
38299746Sjulian				goto loop;
38399703Sjulian			TRACE(("searchexec \"%s\": no change\n", name));
38499703Sjulian			goto success;
38599703Sjulian		}
38699703Sjulian		if (stat(fullname, &statb) < 0) {
38799703Sjulian			if (errno != ENOENT && errno != ENOTDIR)
388118839Sjhb				e = errno;
38999703Sjulian			goto loop;
39099703Sjulian		}
39199703Sjulian		e = EACCES;	/* if we fail, this will be the error */
39299703Sjulian		if (!S_ISREG(statb.st_mode))
393111032Sjulian			goto loop;
39499703Sjulian		if (pathopt) {		/* this is a %func directory */
39599703Sjulian			stalloc(strlen(fullname) + 1);
39699703Sjulian			readcmdfile(fullname);
39799703Sjulian			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
39899703Sjulian				error("%s not defined in %s", name, fullname);
39999703Sjulian			stunalloc(fullname);
40099703Sjulian			goto success;
40199703Sjulian		}
40299703Sjulian#ifdef notdef
40399703Sjulian		if (statb.st_uid == geteuid()) {
40499703Sjulian			if ((statb.st_mode & 0100) == 0)
40599703Sjulian				goto loop;
40699703Sjulian		} else if (statb.st_gid == getegid()) {
40799703Sjulian			if ((statb.st_mode & 010) == 0)
40899703Sjulian				goto loop;
40999703Sjulian		} else {
41099703Sjulian			if ((statb.st_mode & 01) == 0)
41199703Sjulian				goto loop;
41299703Sjulian		}
41399703Sjulian#endif
41499703Sjulian		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
41599703Sjulian		INTOFF;
41699703Sjulian		cmdp = cmdlookup(name, 1);
41799703Sjulian		if (cmdp->cmdtype == CMDFUNCTION)
41899703Sjulian			cmdp = &loc_cmd;
41999703Sjulian		cmdp->cmdtype = CMDNORMAL;
42099703Sjulian		cmdp->param.index = idx;
42199703Sjulian		INTON;
42299703Sjulian		goto success;
42399703Sjulian	}
42499703Sjulian
42599703Sjulian	/* We failed.  If there was an entry for this command, delete it */
426251988Skib	if (cmdp && cmdp->cmdtype != CMDFUNCTION)
42799703Sjulian		delete_cmd_entry();
42899703Sjulian	if (act & DO_ERR) {
42999703Sjulian		if (e == ENOENT || e == ENOTDIR)
43099703Sjulian			outfmt(out2, "%s: not found\n", name);
43199703Sjulian		else
43299703Sjulian			outfmt(out2, "%s: %s\n", name, strerror(e));
43399703Sjulian	}
43499703Sjulian	entry->cmdtype = CMDUNKNOWN;
43599703Sjulian	entry->u.index = 0;
43699703Sjulian	return;
43799703Sjulian
438290668Sjhbsuccess:
439290668Sjhb	cmdp->rehash = 0;
44099703Sjulian	entry->cmdtype = cmdp->cmdtype;
44199703Sjulian	entry->u = cmdp->param;
442290668Sjhb	entry->special = cmdp->special;
443290668Sjhb}
44499703Sjulian
44599703Sjulian
446290668Sjhb
447290668Sjhb/*
44899703Sjulian * Search the table of builtin commands.
44999703Sjulian */
450251033Skib
45199703Sjulianint
45299703Sjulianfind_builtin(const char *name, int *special)
45399703Sjulian{
454174395Sjkoshy	const struct builtincmd *bp;
455174395Sjkoshy
456174395Sjkoshy	for (bp = builtincmd ; bp->name ; bp++) {
457174395Sjkoshy		if (*bp->name == *name && equal(bp->name, name)) {
458174395Sjkoshy			*special = bp->special;
459174395Sjkoshy			return bp->code;
460174395Sjkoshy		}
461174395Sjkoshy	}
462174395Sjkoshy	return -1;
463174395Sjkoshy}
464174395Sjkoshy
465174395Sjkoshy
466174395Sjkoshy
467174395Sjkoshy/*
468174395Sjkoshy * Called when a cd is done.  Marks all commands so the next time they
469174395Sjkoshy * are executed they will be rehashed.
470174395Sjkoshy */
471174395Sjkoshy
472174395Sjkoshyvoid
473174395Sjkoshyhashcd(void)
474174395Sjkoshy{
475174395Sjkoshy	struct tblentry **pp;
476174395Sjkoshy	struct tblentry *cmdp;
477186037Sjkoshy
478174395Sjkoshy	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
479186037Sjkoshy		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
480186037Sjkoshy			if (cmdp->cmdtype == CMDNORMAL)
481186037Sjkoshy				cmdp->rehash = 1;
482186037Sjkoshy		}
483186037Sjkoshy	}
484186037Sjkoshy}
485186037Sjkoshy
486174395Sjkoshy
487186037Sjkoshy
488186037Sjkoshy/*
489174395Sjkoshy * Called before PATH is changed.  The argument is the new value of PATH;
490174395Sjkoshy * pathval() still returns the old value at this point.  Called with
491174395Sjkoshy * interrupts off.
492 */
493
494void
495changepath(const char *newval)
496{
497	clearcmdentry(0);
498}
499
500
501/*
502 * Clear out command entries.  The argument specifies the first entry in
503 * PATH which has changed.
504 */
505
506void
507clearcmdentry(int firstchange)
508{
509	struct tblentry **tblp;
510	struct tblentry **pp;
511	struct tblentry *cmdp;
512
513	INTOFF;
514	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
515		pp = tblp;
516		while ((cmdp = *pp) != NULL) {
517			if ((cmdp->cmdtype == CMDNORMAL &&
518			     cmdp->param.index >= firstchange)) {
519				*pp = cmdp->next;
520				ckfree(cmdp);
521			} else {
522				pp = &cmdp->next;
523			}
524		}
525	}
526	INTON;
527}
528
529
530/*
531 * Locate a command in the command hash table.  If "add" is nonzero,
532 * add the command to the table if it is not already present.  The
533 * variable "lastcmdentry" is set to point to the address of the link
534 * pointing to the entry, so that delete_cmd_entry can delete the
535 * entry.
536 */
537
538static struct tblentry **lastcmdentry;
539
540
541static struct tblentry *
542cmdlookup(const char *name, int add)
543{
544	int hashval;
545	const char *p;
546	struct tblentry *cmdp;
547	struct tblentry **pp;
548
549	p = name;
550	hashval = *p << 4;
551	while (*p)
552		hashval += *p++;
553	hashval &= 0x7FFF;
554	pp = &cmdtable[hashval % CMDTABLESIZE];
555	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
556		if (equal(cmdp->cmdname, name))
557			break;
558		pp = &cmdp->next;
559	}
560	if (add && cmdp == NULL) {
561		INTOFF;
562		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
563					+ strlen(name) + 1);
564		cmdp->next = NULL;
565		cmdp->cmdtype = CMDUNKNOWN;
566		cmdp->rehash = 0;
567		strcpy(cmdp->cmdname, name);
568		INTON;
569	}
570	lastcmdentry = pp;
571	return cmdp;
572}
573
574/*
575 * Delete the command entry returned on the last lookup.
576 */
577
578static void
579delete_cmd_entry(void)
580{
581	struct tblentry *cmdp;
582
583	INTOFF;
584	cmdp = *lastcmdentry;
585	*lastcmdentry = cmdp->next;
586	ckfree(cmdp);
587	INTON;
588}
589
590
591
592/*
593 * Add a new command entry, replacing any existing command entry for
594 * the same name.
595 */
596
597void
598addcmdentry(const char *name, struct cmdentry *entry)
599{
600	struct tblentry *cmdp;
601
602	INTOFF;
603	cmdp = cmdlookup(name, 1);
604	if (cmdp->cmdtype == CMDFUNCTION) {
605		unreffunc(cmdp->param.func);
606	}
607	cmdp->cmdtype = entry->cmdtype;
608	cmdp->param = entry->u;
609	INTON;
610}
611
612
613/*
614 * Define a shell function.
615 */
616
617void
618defun(const char *name, union node *func)
619{
620	struct cmdentry entry;
621
622	INTOFF;
623	entry.cmdtype = CMDFUNCTION;
624	entry.u.func = copyfunc(func);
625	addcmdentry(name, &entry);
626	INTON;
627}
628
629
630/*
631 * Delete a function if it exists.
632 */
633
634int
635unsetfunc(const char *name)
636{
637	struct tblentry *cmdp;
638
639	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
640		unreffunc(cmdp->param.func);
641		delete_cmd_entry();
642		return (0);
643	}
644	return (0);
645}
646
647/*
648 * Shared code for the following builtin commands:
649 *    type, command -v, command -V
650 */
651
652int
653typecmd_impl(int argc, char **argv, int cmd, const char *path)
654{
655	struct cmdentry entry;
656	struct tblentry *cmdp;
657	const char *const *pp;
658	struct alias *ap;
659	int i;
660	int error1 = 0;
661
662	if (path != pathval())
663		clearcmdentry(0);
664
665	for (i = 1; i < argc; i++) {
666		/* First look at the keywords */
667		for (pp = parsekwd; *pp; pp++)
668			if (**pp == *argv[i] && equal(*pp, argv[i]))
669				break;
670
671		if (*pp) {
672			if (cmd == TYPECMD_SMALLV)
673				out1fmt("%s\n", argv[i]);
674			else
675				out1fmt("%s is a shell keyword\n", argv[i]);
676			continue;
677		}
678
679		/* Then look at the aliases */
680		if ((ap = lookupalias(argv[i], 1)) != NULL) {
681			if (cmd == TYPECMD_SMALLV)
682				out1fmt("alias %s='%s'\n", argv[i], ap->val);
683			else
684				out1fmt("%s is an alias for %s\n", argv[i],
685				    ap->val);
686			continue;
687		}
688
689		/* Then check if it is a tracked alias */
690		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
691			entry.cmdtype = cmdp->cmdtype;
692			entry.u = cmdp->param;
693			entry.special = cmdp->special;
694		}
695		else {
696			/* Finally use brute force */
697			find_command(argv[i], &entry, 0, path);
698		}
699
700		switch (entry.cmdtype) {
701		case CMDNORMAL: {
702			if (strchr(argv[i], '/') == NULL) {
703				const char *path2 = path;
704				char *name;
705				int j = entry.u.index;
706				do {
707					name = padvance(&path2, argv[i]);
708					stunalloc(name);
709				} while (--j >= 0);
710				if (cmd == TYPECMD_SMALLV)
711					out1fmt("%s\n", name);
712				else
713					out1fmt("%s is%s %s\n", argv[i],
714					    (cmdp && cmd == TYPECMD_TYPE) ?
715						" a tracked alias for" : "",
716					    name);
717			} else {
718				if (eaccess(argv[i], X_OK) == 0) {
719					if (cmd == TYPECMD_SMALLV)
720						out1fmt("%s\n", argv[i]);
721					else
722						out1fmt("%s is %s\n", argv[i],
723						    argv[i]);
724				} else {
725					if (cmd != TYPECMD_SMALLV)
726						outfmt(out2, "%s: %s\n",
727						    argv[i], strerror(errno));
728					error1 |= 127;
729				}
730			}
731			break;
732		}
733		case CMDFUNCTION:
734			if (cmd == TYPECMD_SMALLV)
735				out1fmt("%s\n", argv[i]);
736			else
737				out1fmt("%s is a shell function\n", argv[i]);
738			break;
739
740		case CMDBUILTIN:
741			if (cmd == TYPECMD_SMALLV)
742				out1fmt("%s\n", argv[i]);
743			else if (entry.special)
744				out1fmt("%s is a special shell builtin\n",
745				    argv[i]);
746			else
747				out1fmt("%s is a shell builtin\n", argv[i]);
748			break;
749
750		default:
751			if (cmd != TYPECMD_SMALLV)
752				outfmt(out2, "%s: not found\n", argv[i]);
753			error1 |= 127;
754			break;
755		}
756	}
757
758	if (path != pathval())
759		clearcmdentry(0);
760
761	return error1;
762}
763
764/*
765 * Locate and print what a word is...
766 */
767
768int
769typecmd(int argc, char **argv)
770{
771	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
772}
773