exec.c revision 266932
1184610Salfred/*-
2184610Salfred * Copyright (c) 1991, 1993
3184610Salfred *	The Regents of the University of California.  All rights reserved.
4184610Salfred *
5184610Salfred * This code is derived from software contributed to Berkeley by
6184610Salfred * Kenneth Almquist.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred * 4. Neither the name of the University nor the names of its contributors
17184610Salfred *    may be used to endorse or promote products derived from this software
18184610Salfred *    without specific prior written permission.
19184610Salfred *
20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25190754Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27188942Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28188942Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29188942Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30184610Salfred * SUCH DAMAGE.
31194228Sthompsa */
32184610Salfred
33188942Sthompsa#ifndef lint
34188942Sthompsa#if 0
35188942Sthompsastatic char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
36188942Sthompsa#endif
37188942Sthompsa#endif /* not lint */
38188942Sthompsa#include <sys/cdefs.h>
39188942Sthompsa__FBSDID("$FreeBSD: head/bin/sh/exec.c 266932 2014-05-31 22:25:45Z jilles $");
40184610Salfred
41188942Sthompsa#include <sys/types.h>
42188942Sthompsa#include <sys/stat.h>
43184610Salfred#include <unistd.h>
44194228Sthompsa#include <fcntl.h>
45184610Salfred#include <errno.h>
46184610Salfred#include <paths.h>
47184610Salfred#include <stdlib.h>
48184610Salfred
49184610Salfred/*
50184610Salfred * When commands are first encountered, they are entered in a hash table.
51184610Salfred * This ensures that a full path search will not have to be done for them
52184610Salfred * on each invocation.
53194228Sthompsa *
54184610Salfred * We should investigate converting to a linear search, even though that
55194228Sthompsa * would make the command name "hash" a misnomer.
56184610Salfred */
57184610Salfred
58184610Salfred#include "shell.h"
59184610Salfred#include "main.h"
60184610Salfred#include "nodes.h"
61184610Salfred#include "parser.h"
62184610Salfred#include "redir.h"
63190734Sthompsa#include "eval.h"
64190734Sthompsa#include "exec.h"
65194228Sthompsa#include "builtins.h"
66192499Sthompsa#include "var.h"
67184610Salfred#include "options.h"
68184610Salfred#include "input.h"
69184610Salfred#include "output.h"
70184610Salfred#include "syntax.h"
71184610Salfred#include "memalloc.h"
72184610Salfred#include "error.h"
73184610Salfred#include "mystring.h"
74184610Salfred#include "show.h"
75192984Sthompsa#include "jobs.h"
76194228Sthompsa#include "alias.h"
77190734Sthompsa
78190734Sthompsa
79190734Sthompsa#define CMDTABLESIZE 31		/* should be prime */
80184610Salfred
81184610Salfred
82184610Salfred
83184610Salfredstruct tblentry {
84184610Salfred	struct tblentry *next;	/* next entry in hash chain */
85194228Sthompsa	union param param;	/* definition of builtin function */
86194228Sthompsa	int special;		/* flag for special builtin commands */
87194228Sthompsa	signed char cmdtype;	/* index identifying command */
88194228Sthompsa	char cmdname[];		/* name of command */
89194228Sthompsa};
90194228Sthompsa
91194228Sthompsa
92194228Sthompsastatic struct tblentry *cmdtable[CMDTABLESIZE];
93194228Sthompsastatic int cmdtable_cd = 0;	/* cmdtable contains cd-dependent entries */
94194228Sthompsaint exerrno = 0;			/* Last exec error */
95194228Sthompsa
96192500Sthompsa
97184610Salfredstatic void tryexec(char *, char **, char **);
98184610Salfredstatic void printentry(struct tblentry *, int);
99194228Sthompsastatic struct tblentry *cmdlookup(const char *, int);
100190734Sthompsastatic void delete_cmd_entry(void);
101190734Sthompsastatic void addcmdentry(const char *, struct cmdentry *);
102194228Sthompsa
103190734Sthompsa
104192499Sthompsa
105194228Sthompsa/*
106190734Sthompsa * Exec a program.  Never returns.  If you change this routine, you may
107194228Sthompsa * have to change the find_command routine as well.
108190734Sthompsa *
109190734Sthompsa * The argv array may be changed and element argv[-1] should be writable.
110190734Sthompsa */
111194228Sthompsa
112184610Salfredvoid
113184610Salfredshellexec(char **argv, char **envp, const char *path, int idx)
114184610Salfred{
115184610Salfred	char *cmdname;
116184610Salfred	int e;
117194228Sthompsa
118184610Salfred	if (strchr(argv[0], '/') != NULL) {
119184610Salfred		tryexec(argv[0], argv, envp);
120184610Salfred		e = errno;
121184610Salfred	} else {
122184610Salfred		e = ENOENT;
123184610Salfred		while ((cmdname = padvance(&path, argv[0])) != NULL) {
124184610Salfred			if (--idx < 0 && pathopt == NULL) {
125184610Salfred				tryexec(cmdname, argv, envp);
126184610Salfred				if (errno != ENOENT && errno != ENOTDIR)
127184610Salfred					e = errno;
128184610Salfred				if (e == ENOEXEC)
129184610Salfred					break;
130184610Salfred			}
131194228Sthompsa			stunalloc(cmdname);
132184610Salfred		}
133184610Salfred	}
134184610Salfred
135184610Salfred	/* Map to POSIX errors */
136184610Salfred	if (e == ENOENT || e == ENOTDIR) {
137184610Salfred		exerrno = 127;
138184610Salfred		exerror(EXEXEC, "%s: not found", argv[0]);
139184610Salfred	} else {
140193045Sthompsa		exerrno = 126;
141194228Sthompsa		exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
142184610Salfred	}
143184610Salfred}
144184610Salfred
145184610Salfred
146184610Salfredstatic void
147184610Salfredtryexec(char *cmd, char **argv, char **envp)
148184610Salfred{
149184610Salfred	int e, in;
150184610Salfred	ssize_t n;
151184610Salfred	char buf[256];
152184610Salfred
153184610Salfred	execve(cmd, argv, envp);
154184610Salfred	e = errno;
155184610Salfred	if (e == ENOEXEC) {
156184610Salfred		INTOFF;
157184610Salfred		in = open(cmd, O_RDONLY | O_NONBLOCK);
158194228Sthompsa		if (in != -1) {
159184610Salfred			n = pread(in, buf, sizeof buf, 0);
160184610Salfred			close(in);
161184610Salfred			if (n > 0 && memchr(buf, '\0', n) != NULL) {
162184610Salfred				errno = ENOEXEC;
163184610Salfred				return;
164184610Salfred			}
165184610Salfred		}
166184610Salfred		*argv = cmd;
167184610Salfred		*--argv = __DECONST(char *, _PATH_BSHELL);
168190180Sthompsa		execve(_PATH_BSHELL, argv, envp);
169184610Salfred	}
170194228Sthompsa	errno = e;
171193074Sthompsa}
172193074Sthompsa
173184610Salfred/*
174192984Sthompsa * Do a path search.  The variable path (passed by reference) should be
175192984Sthompsa * set to the start of the path before the first call; padvance will update
176184610Salfred * this value as it proceeds.  Successive calls to padvance will return
177193074Sthompsa * the possible path expansions in sequence.  If an option (indicated by
178193074Sthompsa * a percent sign) appears in the path entry then the global variable
179193074Sthompsa * pathopt will be set to point to it; otherwise pathopt will be set to
180193074Sthompsa * NULL.
181193074Sthompsa */
182193074Sthompsa
183184610Salfredconst char *pathopt;
184184610Salfred
185184610Salfredchar *
186184610Salfredpadvance(const char **path, const char *name)
187184610Salfred{
188184610Salfred	const char *p, *start;
189184610Salfred	char *q;
190184610Salfred	size_t len, namelen;
191184610Salfred
192184610Salfred	if (*path == NULL)
193184610Salfred		return NULL;
194184610Salfred	start = *path;
195184610Salfred	for (p = start; *p && *p != ':' && *p != '%'; p++)
196184610Salfred		; /* nothing */
197184610Salfred	namelen = strlen(name);
198184610Salfred	len = p - start + namelen + 2;	/* "2" is for '/' and '\0' */
199184610Salfred	STARTSTACKSTR(q);
200184610Salfred	CHECKSTRSPACE(len, q);
201184610Salfred	if (p != start) {
202184610Salfred		memcpy(q, start, p - start);
203184610Salfred		q += p - start;
204184610Salfred		*q++ = '/';
205184610Salfred	}
206184610Salfred	memcpy(q, name, namelen + 1);
207184610Salfred	pathopt = NULL;
208184610Salfred	if (*p == '%') {
209184610Salfred		pathopt = ++p;
210184610Salfred		while (*p && *p != ':')  p++;
211184610Salfred	}
212184610Salfred	if (*p == ':')
213184610Salfred		*path = p + 1;
214184610Salfred	else
215184610Salfred		*path = NULL;
216184610Salfred	return stalloc(len);
217184610Salfred}
218184610Salfred
219184610Salfred
220184610Salfred
221184610Salfred/*** Command hashing code ***/
222184610Salfred
223184610Salfred
224187173Sthompsaint
225184610Salfredhashcmd(int argc __unused, char **argv __unused)
226184610Salfred{
227184610Salfred	struct tblentry **pp;
228184610Salfred	struct tblentry *cmdp;
229187173Sthompsa	int c;
230184610Salfred	int verbose;
231184610Salfred	struct cmdentry entry;
232184610Salfred	char *name;
233184610Salfred	int errors;
234184610Salfred
235184610Salfred	errors = 0;
236184610Salfred	verbose = 0;
237184610Salfred	while ((c = nextopt("rv")) != '\0') {
238184610Salfred		if (c == 'r') {
239184610Salfred			clearcmdentry();
240184610Salfred		} else if (c == 'v') {
241184610Salfred			verbose++;
242184610Salfred		}
243184610Salfred	}
244184610Salfred	if (*argptr == NULL) {
245184610Salfred		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
246184610Salfred			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
247194228Sthompsa				if (cmdp->cmdtype == CMDNORMAL)
248184610Salfred					printentry(cmdp, verbose);
249184610Salfred			}
250184610Salfred		}
251184610Salfred		return 0;
252184610Salfred	}
253184610Salfred	while ((name = *argptr) != NULL) {
254184610Salfred		if ((cmdp = cmdlookup(name, 0)) != NULL
255184610Salfred		 && cmdp->cmdtype == CMDNORMAL)
256184610Salfred			delete_cmd_entry();
257184610Salfred		find_command(name, &entry, DO_ERR, pathval());
258184610Salfred		if (entry.cmdtype == CMDUNKNOWN)
259184610Salfred			errors = 1;
260194228Sthompsa		else if (verbose) {
261184610Salfred			cmdp = cmdlookup(name, 0);
262184610Salfred			if (cmdp != NULL)
263184610Salfred				printentry(cmdp, verbose);
264184610Salfred			else {
265184610Salfred				outfmt(out2, "%s: not found\n", name);
266184610Salfred				errors = 1;
267194228Sthompsa			}
268184610Salfred			flushall();
269184610Salfred		}
270184610Salfred		argptr++;
271184610Salfred	}
272184610Salfred	return errors;
273184610Salfred}
274184610Salfred
275184610Salfred
276184610Salfredstatic void
277184610Salfredprintentry(struct tblentry *cmdp, int verbose)
278184610Salfred{
279190180Sthompsa	int idx;
280184610Salfred	const char *path;
281184610Salfred	char *name;
282194228Sthompsa
283184610Salfred	if (cmdp->cmdtype == CMDNORMAL) {
284184610Salfred		idx = cmdp->param.index;
285184610Salfred		path = pathval();
286184610Salfred		do {
287192984Sthompsa			name = padvance(&path, cmdp->cmdname);
288184610Salfred			stunalloc(name);
289184610Salfred		} while (--idx >= 0);
290184610Salfred		out1str(name);
291194228Sthompsa	} else if (cmdp->cmdtype == CMDBUILTIN) {
292184610Salfred		out1fmt("builtin %s", cmdp->cmdname);
293184610Salfred	} else if (cmdp->cmdtype == CMDFUNCTION) {
294184610Salfred		out1fmt("function %s", cmdp->cmdname);
295184610Salfred		if (verbose) {
296184610Salfred			INTOFF;
297192984Sthompsa			name = commandtext(getfuncnode(cmdp->param.func));
298192984Sthompsa			out1c(' ');
299192984Sthompsa			out1str(name);
300194228Sthompsa			ckfree(name);
301193045Sthompsa			INTON;
302193045Sthompsa		}
303193045Sthompsa#ifdef DEBUG
304184610Salfred	} else {
305184610Salfred		error("internal error: cmdtype %d", cmdp->cmdtype);
306184610Salfred#endif
307184610Salfred	}
308184610Salfred	out1c('\n');
309184610Salfred}
310184610Salfred
311184610Salfred
312184610Salfred
313184610Salfred/*
314184610Salfred * Resolve a command name.  If you change this routine, you may have to
315184610Salfred * change the shellexec routine as well.
316184610Salfred */
317193644Sthompsa
318184610Salfredvoid
319184610Salfredfind_command(const char *name, struct cmdentry *entry, int act,
320184610Salfred    const char *path)
321190734Sthompsa{
322190734Sthompsa	struct tblentry *cmdp, loc_cmd;
323190734Sthompsa	int idx;
324190734Sthompsa	char *fullname;
325190734Sthompsa	struct stat statb;
326193644Sthompsa	int e;
327184610Salfred	int i;
328184610Salfred	int spec;
329184610Salfred	int cd;
330192499Sthompsa
331184610Salfred	/* If name contains a slash, don't use the hash table */
332190734Sthompsa	if (strchr(name, '/') != NULL) {
333184610Salfred		entry->cmdtype = CMDNORMAL;
334184610Salfred		entry->u.index = 0;
335184610Salfred		return;
336184610Salfred	}
337184610Salfred
338184610Salfred	cd = 0;
339184610Salfred
340184610Salfred	/* If name is in the table, and not invalidated by cd, we're done */
341184610Salfred	if ((cmdp = cmdlookup(name, 0)) != NULL) {
342184610Salfred		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
343184610Salfred			cmdp = NULL;
344184610Salfred		else
345184610Salfred			goto success;
346184610Salfred	}
347184610Salfred
348184610Salfred	/* Check for builtin next */
349184610Salfred	if ((i = find_builtin(name, &spec)) >= 0) {
350184610Salfred		INTOFF;
351194228Sthompsa		cmdp = cmdlookup(name, 1);
352184610Salfred		if (cmdp->cmdtype == CMDFUNCTION)
353184610Salfred			cmdp = &loc_cmd;
354184610Salfred		cmdp->cmdtype = CMDBUILTIN;
355184610Salfred		cmdp->param.index = i;
356184610Salfred		cmdp->special = spec;
357184610Salfred		INTON;
358184610Salfred		goto success;
359184610Salfred	}
360184610Salfred
361184610Salfred	/* We have to search path. */
362184610Salfred
363184610Salfred	e = ENOENT;
364184610Salfred	idx = -1;
365184610Salfred	for (;(fullname = padvance(&path, name)) != NULL; stunalloc(fullname)) {
366184610Salfred		idx++;
367184610Salfred		if (pathopt) {
368184610Salfred			if (prefix("func", pathopt)) {
369184610Salfred				/* handled below */
370184610Salfred			} else {
371184610Salfred				continue; /* ignore unimplemented options */
372184610Salfred			}
373184610Salfred		}
374184610Salfred		if (fullname[0] != '/')
375184610Salfred			cd = 1;
376184610Salfred		if (stat(fullname, &statb) < 0) {
377194228Sthompsa			if (errno != ENOENT && errno != ENOTDIR)
378184610Salfred				e = errno;
379184610Salfred			continue;
380184610Salfred		}
381184610Salfred		e = EACCES;	/* if we fail, this will be the error */
382184610Salfred		if (!S_ISREG(statb.st_mode))
383190181Sthompsa			continue;
384184610Salfred		if (pathopt) {		/* this is a %func directory */
385184610Salfred			readcmdfile(fullname);
386184610Salfred			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
387184610Salfred				error("%s not defined in %s", name, fullname);
388184610Salfred			stunalloc(fullname);
389184610Salfred			goto success;
390184610Salfred		}
391184610Salfred#ifdef notdef
392184610Salfred		if (statb.st_uid == geteuid()) {
393184610Salfred			if ((statb.st_mode & 0100) == 0)
394184610Salfred				goto loop;
395187180Sthompsa		} else if (statb.st_gid == getegid()) {
396187180Sthompsa			if ((statb.st_mode & 010) == 0)
397187180Sthompsa				goto loop;
398187180Sthompsa		} else {
399187180Sthompsa			if ((statb.st_mode & 01) == 0)
400187180Sthompsa				goto loop;
401184610Salfred		}
402187180Sthompsa#endif
403184610Salfred		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
404184610Salfred		INTOFF;
405184610Salfred		stunalloc(fullname);
406184610Salfred		cmdp = cmdlookup(name, 1);
407184610Salfred		if (cmdp->cmdtype == CMDFUNCTION)
408184610Salfred			cmdp = &loc_cmd;
409184610Salfred		cmdp->cmdtype = CMDNORMAL;
410184610Salfred		cmdp->param.index = idx;
411184610Salfred		INTON;
412184610Salfred		goto success;
413184610Salfred	}
414184610Salfred
415184610Salfred	if (act & DO_ERR) {
416184610Salfred		if (e == ENOENT || e == ENOTDIR)
417184610Salfred			outfmt(out2, "%s: not found\n", name);
418184610Salfred		else
419184610Salfred			outfmt(out2, "%s: %s\n", name, strerror(e));
420184610Salfred	}
421184610Salfred	entry->cmdtype = CMDUNKNOWN;
422184610Salfred	entry->u.index = 0;
423184610Salfred	return;
424184610Salfred
425184610Salfredsuccess:
426184610Salfred	if (cd)
427184610Salfred		cmdtable_cd = 1;
428184610Salfred	entry->cmdtype = cmdp->cmdtype;
429184610Salfred	entry->u = cmdp->param;
430184610Salfred	entry->special = cmdp->special;
431184610Salfred}
432187180Sthompsa
433187180Sthompsa
434187180Sthompsa
435187180Sthompsa/*
436187180Sthompsa * Search the table of builtin commands.
437187180Sthompsa */
438187180Sthompsa
439187180Sthompsaint
440187180Sthompsafind_builtin(const char *name, int *special)
441187180Sthompsa{
442187180Sthompsa	const struct builtincmd *bp;
443187180Sthompsa
444187180Sthompsa	for (bp = builtincmd ; bp->name ; bp++) {
445187180Sthompsa		if (*bp->name == *name && equal(bp->name, name)) {
446187180Sthompsa			*special = bp->special;
447187180Sthompsa			return bp->code;
448187180Sthompsa		}
449187180Sthompsa	}
450184610Salfred	return -1;
451184610Salfred}
452184610Salfred
453187180Sthompsa
454187180Sthompsa
455184610Salfred/*
456184610Salfred * Called when a cd is done.  If any entry in cmdtable depends on the current
457184610Salfred * directory, simply clear cmdtable completely.
458184610Salfred */
459184610Salfred
460184610Salfredvoid
461184610Salfredhashcd(void)
462184610Salfred{
463184610Salfred	if (cmdtable_cd)
464184610Salfred		clearcmdentry();
465184610Salfred}
466184610Salfred
467184610Salfred
468184610Salfred
469184610Salfred/*
470184610Salfred * Called before PATH is changed.  The argument is the new value of PATH;
471184610Salfred * pathval() still returns the old value at this point.  Called with
472184610Salfred * interrupts off.
473184610Salfred */
474184610Salfred
475184610Salfredvoid
476184610Salfredchangepath(const char *newval __unused)
477184610Salfred{
478184610Salfred	clearcmdentry();
479184610Salfred}
480184610Salfred
481194228Sthompsa
482184610Salfred/*
483184610Salfred * Clear out command entries.  The argument specifies the first entry in
484184610Salfred * PATH which has changed.
485184610Salfred */
486184610Salfred
487184610Salfredvoid
488184610Salfredclearcmdentry(void)
489184610Salfred{
490184610Salfred	struct tblentry **tblp;
491184610Salfred	struct tblentry **pp;
492184610Salfred	struct tblentry *cmdp;
493184610Salfred
494184610Salfred	INTOFF;
495184610Salfred	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
496184610Salfred		pp = tblp;
497184610Salfred		while ((cmdp = *pp) != NULL) {
498184610Salfred			if (cmdp->cmdtype == CMDNORMAL) {
499184610Salfred				*pp = cmdp->next;
500184610Salfred				ckfree(cmdp);
501184610Salfred			} else {
502184610Salfred				pp = &cmdp->next;
503184610Salfred			}
504184610Salfred		}
505184610Salfred	}
506184610Salfred	cmdtable_cd = 0;
507184610Salfred	INTON;
508184610Salfred}
509184610Salfred
510184610Salfred
511184610Salfred/*
512184610Salfred * Locate a command in the command hash table.  If "add" is nonzero,
513184610Salfred * add the command to the table if it is not already present.  The
514184610Salfred * variable "lastcmdentry" is set to point to the address of the link
515184610Salfred * pointing to the entry, so that delete_cmd_entry can delete the
516184610Salfred * entry.
517184610Salfred */
518184610Salfred
519184610Salfredstatic struct tblentry **lastcmdentry;
520184610Salfred
521184610Salfred
522184610Salfredstatic struct tblentry *
523184610Salfredcmdlookup(const char *name, int add)
524184610Salfred{
525184610Salfred	int hashval;
526184610Salfred	const char *p;
527184610Salfred	struct tblentry *cmdp;
528184610Salfred	struct tblentry **pp;
529184610Salfred	size_t len;
530184610Salfred
531184610Salfred	p = name;
532184610Salfred	hashval = *p << 4;
533184610Salfred	while (*p)
534184610Salfred		hashval += *p++;
535184610Salfred	hashval &= 0x7FFF;
536184610Salfred	pp = &cmdtable[hashval % CMDTABLESIZE];
537184610Salfred	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
538184610Salfred		if (equal(cmdp->cmdname, name))
539184610Salfred			break;
540184610Salfred		pp = &cmdp->next;
541184610Salfred	}
542184610Salfred	if (add && cmdp == NULL) {
543184610Salfred		INTOFF;
544184610Salfred		len = strlen(name);
545184610Salfred		cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
546184610Salfred		cmdp->next = NULL;
547184610Salfred		cmdp->cmdtype = CMDUNKNOWN;
548184610Salfred		memcpy(cmdp->cmdname, name, len + 1);
549184610Salfred		INTON;
550184610Salfred	}
551184610Salfred	lastcmdentry = pp;
552184610Salfred	return cmdp;
553184610Salfred}
554184610Salfred
555184610Salfred/*
556184610Salfred * Delete the command entry returned on the last lookup.
557184610Salfred */
558184610Salfred
559184610Salfredstatic void
560184610Salfreddelete_cmd_entry(void)
561184610Salfred{
562184610Salfred	struct tblentry *cmdp;
563184610Salfred
564184610Salfred	INTOFF;
565184610Salfred	cmdp = *lastcmdentry;
566184610Salfred	*lastcmdentry = cmdp->next;
567184610Salfred	ckfree(cmdp);
568184610Salfred	INTON;
569184610Salfred}
570184610Salfred
571184610Salfred
572184610Salfred
573184610Salfred/*
574184610Salfred * Add a new command entry, replacing any existing command entry for
575184610Salfred * the same name.
576184610Salfred */
577184610Salfred
578184610Salfredstatic void
579184610Salfredaddcmdentry(const char *name, struct cmdentry *entry)
580184610Salfred{
581184610Salfred	struct tblentry *cmdp;
582184610Salfred
583184610Salfred	INTOFF;
584184610Salfred	cmdp = cmdlookup(name, 1);
585184610Salfred	if (cmdp->cmdtype == CMDFUNCTION) {
586184610Salfred		unreffunc(cmdp->param.func);
587184610Salfred	}
588184610Salfred	cmdp->cmdtype = entry->cmdtype;
589184610Salfred	cmdp->param = entry->u;
590184610Salfred	INTON;
591184610Salfred}
592184610Salfred
593184610Salfred
594184610Salfred/*
595184610Salfred * Define a shell function.
596184610Salfred */
597184610Salfred
598184610Salfredvoid
599184610Salfreddefun(const char *name, union node *func)
600184610Salfred{
601184610Salfred	struct cmdentry entry;
602184610Salfred
603184610Salfred	INTOFF;
604194228Sthompsa	entry.cmdtype = CMDFUNCTION;
605184610Salfred	entry.u.func = copyfunc(func);
606184610Salfred	addcmdentry(name, &entry);
607194228Sthompsa	INTON;
608184610Salfred}
609184610Salfred
610184610Salfred
611184610Salfred/*
612184610Salfred * Delete a function if it exists.
613184610Salfred * Called with interrupts off.
614184610Salfred */
615184610Salfred
616184610Salfredint
617184610Salfredunsetfunc(const char *name)
618184610Salfred{
619184610Salfred	struct tblentry *cmdp;
620184610Salfred
621184610Salfred	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
622190180Sthompsa		unreffunc(cmdp->param.func);
623184610Salfred		delete_cmd_entry();
624184610Salfred		return (0);
625184610Salfred	}
626184610Salfred	return (0);
627184610Salfred}
628184610Salfred
629184610Salfred
630184610Salfred/*
631192984Sthompsa * Check if a function by a certain name exists.
632184610Salfred */
633184610Salfredint
634184610Salfredisfunc(const char *name)
635184610Salfred{
636184610Salfred	struct tblentry *cmdp;
637184610Salfred	cmdp = cmdlookup(name, 0);
638184610Salfred	return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
639184610Salfred}
640184610Salfred
641184610Salfred
642184610Salfred/*
643184610Salfred * Shared code for the following builtin commands:
644184610Salfred *    type, command -v, command -V
645184610Salfred */
646184610Salfred
647184610Salfredint
648184610Salfredtypecmd_impl(int argc, char **argv, int cmd, const char *path)
649184610Salfred{
650190180Sthompsa	struct cmdentry entry;
651184610Salfred	struct tblentry *cmdp;
652184610Salfred	const char *const *pp;
653184610Salfred	struct alias *ap;
654184610Salfred	int i;
655184610Salfred	int error1 = 0;
656184610Salfred
657190181Sthompsa	if (path != pathval())
658184610Salfred		clearcmdentry();
659184610Salfred
660184610Salfred	for (i = 1; i < argc; i++) {
661190181Sthompsa		/* First look at the keywords */
662184610Salfred		for (pp = parsekwd; *pp; pp++)
663184610Salfred			if (**pp == *argv[i] && equal(*pp, argv[i]))
664184610Salfred				break;
665184610Salfred
666184610Salfred		if (*pp) {
667184610Salfred			if (cmd == TYPECMD_SMALLV)
668184610Salfred				out1fmt("%s\n", argv[i]);
669184610Salfred			else
670184610Salfred				out1fmt("%s is a shell keyword\n", argv[i]);
671184610Salfred			continue;
672184610Salfred		}
673184610Salfred
674187173Sthompsa		/* Then look at the aliases */
675190180Sthompsa		if ((ap = lookupalias(argv[i], 1)) != NULL) {
676184610Salfred			if (cmd == TYPECMD_SMALLV) {
677184610Salfred				out1fmt("alias %s=", argv[i]);
678184610Salfred				out1qstr(ap->val);
679194228Sthompsa				outcslow('\n', out1);
680184610Salfred			} else
681184610Salfred				out1fmt("%s is an alias for %s\n", argv[i],
682184610Salfred				    ap->val);
683184610Salfred			continue;
684184610Salfred		}
685184610Salfred
686190180Sthompsa		/* Then check if it is a tracked alias */
687184610Salfred		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
688184610Salfred			entry.cmdtype = cmdp->cmdtype;
689184610Salfred			entry.u = cmdp->param;
690184610Salfred			entry.special = cmdp->special;
691184610Salfred		}
692184610Salfred		else {
693184610Salfred			/* Finally use brute force */
694190181Sthompsa			find_command(argv[i], &entry, 0, path);
695184610Salfred		}
696184610Salfred
697184610Salfred		switch (entry.cmdtype) {
698184610Salfred		case CMDNORMAL: {
699184610Salfred			if (strchr(argv[i], '/') == NULL) {
700184610Salfred				const char *path2 = path;
701184610Salfred				char *name;
702184610Salfred				int j = entry.u.index;
703184610Salfred				do {
704194228Sthompsa					name = padvance(&path2, argv[i]);
705184610Salfred					stunalloc(name);
706194228Sthompsa				} while (--j >= 0);
707194228Sthompsa				if (cmd == TYPECMD_SMALLV)
708184610Salfred					out1fmt("%s\n", name);
709184610Salfred				else
710184610Salfred					out1fmt("%s is%s %s\n", argv[i],
711184610Salfred					    (cmdp && cmd == TYPECMD_TYPE) ?
712184610Salfred						" a tracked alias for" : "",
713184610Salfred					    name);
714184610Salfred			} else {
715184610Salfred				if (eaccess(argv[i], X_OK) == 0) {
716193045Sthompsa					if (cmd == TYPECMD_SMALLV)
717194228Sthompsa						out1fmt("%s\n", argv[i]);
718192984Sthompsa					else
719192984Sthompsa						out1fmt("%s is %s\n", argv[i],
720187173Sthompsa						    argv[i]);
721184610Salfred				} else {
722192984Sthompsa					if (cmd != TYPECMD_SMALLV)
723192984Sthompsa						outfmt(out2, "%s: %s\n",
724192984Sthompsa						    argv[i], strerror(errno));
725192984Sthompsa					error1 |= 127;
726193644Sthompsa				}
727192984Sthompsa			}
728192984Sthompsa			break;
729184610Salfred		}
730184610Salfred		case CMDFUNCTION:
731184610Salfred			if (cmd == TYPECMD_SMALLV)
732184610Salfred				out1fmt("%s\n", argv[i]);
733184610Salfred			else
734184610Salfred				out1fmt("%s is a shell function\n", argv[i]);
735184610Salfred			break;
736184610Salfred
737184610Salfred		case CMDBUILTIN:
738194228Sthompsa			if (cmd == TYPECMD_SMALLV)
739184610Salfred				out1fmt("%s\n", argv[i]);
740184610Salfred			else if (entry.special)
741184610Salfred				out1fmt("%s is a special shell builtin\n",
742184610Salfred				    argv[i]);
743184610Salfred			else
744184610Salfred				out1fmt("%s is a shell builtin\n", argv[i]);
745184610Salfred			break;
746184610Salfred
747184610Salfred		default:
748184610Salfred			if (cmd != TYPECMD_SMALLV)
749184610Salfred				outfmt(out2, "%s: not found\n", argv[i]);
750187173Sthompsa			error1 |= 127;
751184610Salfred			break;
752187173Sthompsa		}
753184610Salfred	}
754184610Salfred
755184610Salfred	if (path != pathval())
756184610Salfred		clearcmdentry();
757193045Sthompsa
758184610Salfred	return error1;
759184610Salfred}
760184610Salfred
761190734Sthompsa/*
762184610Salfred * Locate and print what a word is...
763184610Salfred */
764184610Salfred
765184610Salfredint
766184610Salfredtypecmd(int argc, char **argv)
767184610Salfred{
768184610Salfred	if (argc > 2 && strcmp(argv[1], "--") == 0)
769184610Salfred		argc--, argv++;
770184610Salfred	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
771184610Salfred}
772184610Salfred