11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD$");
401556Srgrimes
4117987Speter#include <sys/types.h>
4217987Speter#include <sys/stat.h>
4317987Speter#include <unistd.h>
4417987Speter#include <fcntl.h>
4517987Speter#include <errno.h>
46218306Sjilles#include <paths.h>
4717987Speter#include <stdlib.h>
4817987Speter
491556Srgrimes/*
501556Srgrimes * When commands are first encountered, they are entered in a hash table.
511556Srgrimes * This ensures that a full path search will not have to be done for them
521556Srgrimes * on each invocation.
531556Srgrimes *
541556Srgrimes * We should investigate converting to a linear search, even though that
551556Srgrimes * would make the command name "hash" a misnomer.
561556Srgrimes */
571556Srgrimes
581556Srgrimes#include "shell.h"
591556Srgrimes#include "main.h"
601556Srgrimes#include "nodes.h"
611556Srgrimes#include "parser.h"
621556Srgrimes#include "redir.h"
631556Srgrimes#include "eval.h"
641556Srgrimes#include "exec.h"
651556Srgrimes#include "builtins.h"
661556Srgrimes#include "var.h"
671556Srgrimes#include "options.h"
681556Srgrimes#include "input.h"
691556Srgrimes#include "output.h"
701556Srgrimes#include "syntax.h"
711556Srgrimes#include "memalloc.h"
721556Srgrimes#include "error.h"
731556Srgrimes#include "mystring.h"
7417987Speter#include "show.h"
751556Srgrimes#include "jobs.h"
7625223Ssteve#include "alias.h"
771556Srgrimes
781556Srgrimes
791556Srgrimes#define CMDTABLESIZE 31		/* should be prime */
801556Srgrimes
811556Srgrimes
821556Srgrimes
831556Srgrimesstruct tblentry {
841556Srgrimes	struct tblentry *next;	/* next entry in hash chain */
851556Srgrimes	union param param;	/* definition of builtin function */
86157601Sstefanf	int special;		/* flag for special builtin commands */
87242620Sjilles	signed char cmdtype;	/* index identifying command */
88242530Sjilles	char cmdname[];		/* name of command */
891556Srgrimes};
901556Srgrimes
911556Srgrimes
92213760Sobrienstatic struct tblentry *cmdtable[CMDTABLESIZE];
93245426Sjillesstatic int cmdtable_cd = 0;	/* cmdtable contains cd-dependent entries */
9420425Ssteveint exerrno = 0;			/* Last exec error */
951556Srgrimes
961556Srgrimes
97213811Sobrienstatic void tryexec(char *, char **, char **);
98213811Sobrienstatic void printentry(struct tblentry *, int);
99213811Sobrienstatic struct tblentry *cmdlookup(const char *, int);
100213811Sobrienstatic void delete_cmd_entry(void);
101229220Sjillesstatic void addcmdentry(const char *, struct cmdentry *);
1021556Srgrimes
1031556Srgrimes
1041556Srgrimes
1051556Srgrimes/*
1061556Srgrimes * Exec a program.  Never returns.  If you change this routine, you may
1071556Srgrimes * have to change the find_command routine as well.
108218306Sjilles *
109218306Sjilles * The argv array may be changed and element argv[-1] should be writable.
1101556Srgrimes */
1111556Srgrimes
1121556Srgrimesvoid
113201053Sjillesshellexec(char **argv, char **envp, const char *path, int idx)
11417987Speter{
1151556Srgrimes	char *cmdname;
1161556Srgrimes	int e;
1171556Srgrimes
1181556Srgrimes	if (strchr(argv[0], '/') != NULL) {
1191556Srgrimes		tryexec(argv[0], argv, envp);
1201556Srgrimes		e = errno;
1211556Srgrimes	} else {
1221556Srgrimes		e = ENOENT;
1231556Srgrimes		while ((cmdname = padvance(&path, argv[0])) != NULL) {
124201053Sjilles			if (--idx < 0 && pathopt == NULL) {
1251556Srgrimes				tryexec(cmdname, argv, envp);
1261556Srgrimes				if (errno != ENOENT && errno != ENOTDIR)
1271556Srgrimes					e = errno;
128218320Sjilles				if (e == ENOEXEC)
129218320Sjilles					break;
1301556Srgrimes			}
1311556Srgrimes			stunalloc(cmdname);
1321556Srgrimes		}
1331556Srgrimes	}
13420425Ssteve
13520425Ssteve	/* Map to POSIX errors */
136218242Sjilles	if (e == ENOENT || e == ENOTDIR) {
137218242Sjilles		exerrno = 127;
138218242Sjilles		exerror(EXEXEC, "%s: not found", argv[0]);
139218242Sjilles	} else {
14020425Ssteve		exerrno = 126;
141218242Sjilles		exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
14220425Ssteve	}
1431556Srgrimes}
1441556Srgrimes
1451556Srgrimes
146213811Sobrienstatic void
14790111Simptryexec(char *cmd, char **argv, char **envp)
14890111Simp{
149218320Sjilles	int e, in;
150218320Sjilles	ssize_t n;
151218320Sjilles	char buf[256];
1521556Srgrimes
1531556Srgrimes	execve(cmd, argv, envp);
1541556Srgrimes	e = errno;
1551556Srgrimes	if (e == ENOEXEC) {
156218320Sjilles		INTOFF;
157218320Sjilles		in = open(cmd, O_RDONLY | O_NONBLOCK);
158218320Sjilles		if (in != -1) {
159218320Sjilles			n = pread(in, buf, sizeof buf, 0);
160218320Sjilles			close(in);
161218320Sjilles			if (n > 0 && memchr(buf, '\0', n) != NULL) {
162218320Sjilles				errno = ENOEXEC;
163218320Sjilles				return;
164218320Sjilles			}
165218320Sjilles		}
166218306Sjilles		*argv = cmd;
167248980Sjilles		*--argv = __DECONST(char *, _PATH_BSHELL);
168218306Sjilles		execve(_PATH_BSHELL, argv, envp);
1691556Srgrimes	}
1701556Srgrimes	errno = e;
1711556Srgrimes}
1721556Srgrimes
1731556Srgrimes/*
1741556Srgrimes * Do a path search.  The variable path (passed by reference) should be
1751556Srgrimes * set to the start of the path before the first call; padvance will update
1761556Srgrimes * this value as it proceeds.  Successive calls to padvance will return
1771556Srgrimes * the possible path expansions in sequence.  If an option (indicated by
1781556Srgrimes * a percent sign) appears in the path entry then the global variable
1791556Srgrimes * pathopt will be set to point to it; otherwise pathopt will be set to
1801556Srgrimes * NULL.
1811556Srgrimes */
1821556Srgrimes
183200956Sjillesconst char *pathopt;
1841556Srgrimes
1851556Srgrimeschar *
186200956Sjillespadvance(const char **path, const char *name)
18790111Simp{
188200956Sjilles	const char *p, *start;
189200956Sjilles	char *q;
190262951Sjmmv	size_t len, namelen;
1911556Srgrimes
1921556Srgrimes	if (*path == NULL)
1931556Srgrimes		return NULL;
1941556Srgrimes	start = *path;
195193223Srse	for (p = start; *p && *p != ':' && *p != '%'; p++)
196193223Srse		; /* nothing */
197262951Sjmmv	namelen = strlen(name);
198262951Sjmmv	len = p - start + namelen + 2;	/* "2" is for '/' and '\0' */
199216706Sjilles	STARTSTACKSTR(q);
200216706Sjilles	CHECKSTRSPACE(len, q);
2011556Srgrimes	if (p != start) {
20217987Speter		memcpy(q, start, p - start);
2031556Srgrimes		q += p - start;
2041556Srgrimes		*q++ = '/';
2051556Srgrimes	}
206262951Sjmmv	memcpy(q, name, namelen + 1);
2071556Srgrimes	pathopt = NULL;
2081556Srgrimes	if (*p == '%') {
2091556Srgrimes		pathopt = ++p;
2101556Srgrimes		while (*p && *p != ':')  p++;
2111556Srgrimes	}
2121556Srgrimes	if (*p == ':')
2131556Srgrimes		*path = p + 1;
2141556Srgrimes	else
2151556Srgrimes		*path = NULL;
2161556Srgrimes	return stalloc(len);
2171556Srgrimes}
2181556Srgrimes
2191556Srgrimes
2201556Srgrimes
2211556Srgrimes/*** Command hashing code ***/
2221556Srgrimes
2231556Srgrimes
22417987Speterint
22590111Simphashcmd(int argc __unused, char **argv __unused)
22617987Speter{
2271556Srgrimes	struct tblentry **pp;
2281556Srgrimes	struct tblentry *cmdp;
2291556Srgrimes	int c;
2301556Srgrimes	int verbose;
2311556Srgrimes	struct cmdentry entry;
2321556Srgrimes	char *name;
233231535Sjilles	int errors;
2341556Srgrimes
235231535Sjilles	errors = 0;
2361556Srgrimes	verbose = 0;
2371556Srgrimes	while ((c = nextopt("rv")) != '\0') {
2381556Srgrimes		if (c == 'r') {
239218324Sjilles			clearcmdentry();
2401556Srgrimes		} else if (c == 'v') {
2411556Srgrimes			verbose++;
2421556Srgrimes		}
2431556Srgrimes	}
2441556Srgrimes	if (*argptr == NULL) {
2451556Srgrimes		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
2461556Srgrimes			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
24798157Stjr				if (cmdp->cmdtype == CMDNORMAL)
24898157Stjr					printentry(cmdp, verbose);
2491556Srgrimes			}
2501556Srgrimes		}
2511556Srgrimes		return 0;
2521556Srgrimes	}
2531556Srgrimes	while ((name = *argptr) != NULL) {
2541556Srgrimes		if ((cmdp = cmdlookup(name, 0)) != NULL
255217206Sjilles		 && cmdp->cmdtype == CMDNORMAL)
2561556Srgrimes			delete_cmd_entry();
257204800Sjilles		find_command(name, &entry, DO_ERR, pathval());
258231535Sjilles		if (entry.cmdtype == CMDUNKNOWN)
259231535Sjilles			errors = 1;
260231535Sjilles		else if (verbose) {
261231535Sjilles			cmdp = cmdlookup(name, 0);
262231535Sjilles			if (cmdp != NULL)
263231535Sjilles				printentry(cmdp, verbose);
264231535Sjilles			else {
265231535Sjilles				outfmt(out2, "%s: not found\n", name);
266231535Sjilles				errors = 1;
2671556Srgrimes			}
2681556Srgrimes			flushall();
2691556Srgrimes		}
2701556Srgrimes		argptr++;
2711556Srgrimes	}
272231535Sjilles	return errors;
2731556Srgrimes}
2741556Srgrimes
2751556Srgrimes
276213811Sobrienstatic void
27790111Simpprintentry(struct tblentry *cmdp, int verbose)
27890111Simp{
279201053Sjilles	int idx;
280200956Sjilles	const char *path;
2811556Srgrimes	char *name;
2821556Srgrimes
2831556Srgrimes	if (cmdp->cmdtype == CMDNORMAL) {
284201053Sjilles		idx = cmdp->param.index;
2851556Srgrimes		path = pathval();
2861556Srgrimes		do {
2871556Srgrimes			name = padvance(&path, cmdp->cmdname);
2881556Srgrimes			stunalloc(name);
289201053Sjilles		} while (--idx >= 0);
2901556Srgrimes		out1str(name);
2911556Srgrimes	} else if (cmdp->cmdtype == CMDBUILTIN) {
2921556Srgrimes		out1fmt("builtin %s", cmdp->cmdname);
2931556Srgrimes	} else if (cmdp->cmdtype == CMDFUNCTION) {
2941556Srgrimes		out1fmt("function %s", cmdp->cmdname);
2951556Srgrimes		if (verbose) {
2961556Srgrimes			INTOFF;
297196634Sjilles			name = commandtext(getfuncnode(cmdp->param.func));
2981556Srgrimes			out1c(' ');
2991556Srgrimes			out1str(name);
3001556Srgrimes			ckfree(name);
3011556Srgrimes			INTON;
3021556Srgrimes		}
3031556Srgrimes#ifdef DEBUG
3041556Srgrimes	} else {
3051556Srgrimes		error("internal error: cmdtype %d", cmdp->cmdtype);
3061556Srgrimes#endif
3071556Srgrimes	}
3081556Srgrimes	out1c('\n');
3091556Srgrimes}
3101556Srgrimes
3111556Srgrimes
3121556Srgrimes
3131556Srgrimes/*
3141556Srgrimes * Resolve a command name.  If you change this routine, you may have to
3151556Srgrimes * change the shellexec routine as well.
3161556Srgrimes */
3171556Srgrimes
3181556Srgrimesvoid
319204800Sjillesfind_command(const char *name, struct cmdentry *entry, int act,
320200956Sjilles    const char *path)
32117987Speter{
322204800Sjilles	struct tblentry *cmdp, loc_cmd;
323201053Sjilles	int idx;
3241556Srgrimes	char *fullname;
3251556Srgrimes	struct stat statb;
3261556Srgrimes	int e;
3271556Srgrimes	int i;
328157601Sstefanf	int spec;
329245426Sjilles	int cd;
3301556Srgrimes
3311556Srgrimes	/* If name contains a slash, don't use the hash table */
3321556Srgrimes	if (strchr(name, '/') != NULL) {
3331556Srgrimes		entry->cmdtype = CMDNORMAL;
3341556Srgrimes		entry->u.index = 0;
3351556Srgrimes		return;
3361556Srgrimes	}
3371556Srgrimes
338245426Sjilles	cd = 0;
339245426Sjilles
3401556Srgrimes	/* If name is in the table, and not invalidated by cd, we're done */
341245426Sjilles	if ((cmdp = cmdlookup(name, 0)) != NULL) {
342204800Sjilles		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
343204800Sjilles			cmdp = NULL;
344204800Sjilles		else
345204800Sjilles			goto success;
346204800Sjilles	}
3471556Srgrimes
348217206Sjilles	/* Check for builtin next */
349217206Sjilles	if ((i = find_builtin(name, &spec)) >= 0) {
3501556Srgrimes		INTOFF;
3511556Srgrimes		cmdp = cmdlookup(name, 1);
352204800Sjilles		if (cmdp->cmdtype == CMDFUNCTION)
353204800Sjilles			cmdp = &loc_cmd;
3541556Srgrimes		cmdp->cmdtype = CMDBUILTIN;
3551556Srgrimes		cmdp->param.index = i;
356157601Sstefanf		cmdp->special = spec;
3571556Srgrimes		INTON;
3581556Srgrimes		goto success;
3591556Srgrimes	}
3601556Srgrimes
3611556Srgrimes	/* We have to search path. */
3621556Srgrimes
3631556Srgrimes	e = ENOENT;
364201053Sjilles	idx = -1;
3651556Srgrimesloop:
3661556Srgrimes	while ((fullname = padvance(&path, name)) != NULL) {
3671556Srgrimes		stunalloc(fullname);
368201053Sjilles		idx++;
3691556Srgrimes		if (pathopt) {
370217206Sjilles			if (prefix("func", pathopt)) {
3711556Srgrimes				/* handled below */
3721556Srgrimes			} else {
3731556Srgrimes				goto loop;	/* ignore unimplemented options */
3741556Srgrimes			}
3751556Srgrimes		}
376245426Sjilles		if (fullname[0] != '/')
377245426Sjilles			cd = 1;
378100351Stjr		if (stat(fullname, &statb) < 0) {
3791556Srgrimes			if (errno != ENOENT && errno != ENOTDIR)
3801556Srgrimes				e = errno;
3811556Srgrimes			goto loop;
3821556Srgrimes		}
3831556Srgrimes		e = EACCES;	/* if we fail, this will be the error */
38417987Speter		if (!S_ISREG(statb.st_mode))
3851556Srgrimes			goto loop;
3861556Srgrimes		if (pathopt) {		/* this is a %func directory */
3871556Srgrimes			stalloc(strlen(fullname) + 1);
3881556Srgrimes			readcmdfile(fullname);
3891556Srgrimes			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
3901556Srgrimes				error("%s not defined in %s", name, fullname);
3911556Srgrimes			stunalloc(fullname);
3921556Srgrimes			goto success;
3931556Srgrimes		}
3941556Srgrimes#ifdef notdef
3951556Srgrimes		if (statb.st_uid == geteuid()) {
3961556Srgrimes			if ((statb.st_mode & 0100) == 0)
3971556Srgrimes				goto loop;
3981556Srgrimes		} else if (statb.st_gid == getegid()) {
3991556Srgrimes			if ((statb.st_mode & 010) == 0)
4001556Srgrimes				goto loop;
4011556Srgrimes		} else {
4021556Srgrimes			if ((statb.st_mode & 01) == 0)
4031556Srgrimes				goto loop;
4041556Srgrimes		}
4051556Srgrimes#endif
4061556Srgrimes		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
4071556Srgrimes		INTOFF;
4081556Srgrimes		cmdp = cmdlookup(name, 1);
409204800Sjilles		if (cmdp->cmdtype == CMDFUNCTION)
410204800Sjilles			cmdp = &loc_cmd;
4111556Srgrimes		cmdp->cmdtype = CMDNORMAL;
412201053Sjilles		cmdp->param.index = idx;
4131556Srgrimes		INTON;
4141556Srgrimes		goto success;
4151556Srgrimes	}
4161556Srgrimes
417204800Sjilles	if (act & DO_ERR) {
418104283Stjr		if (e == ENOENT || e == ENOTDIR)
419104283Stjr			outfmt(out2, "%s: not found\n", name);
420104283Stjr		else
421104283Stjr			outfmt(out2, "%s: %s\n", name, strerror(e));
422104283Stjr	}
4231556Srgrimes	entry->cmdtype = CMDUNKNOWN;
424197820Sjilles	entry->u.index = 0;
4251556Srgrimes	return;
4261556Srgrimes
4271556Srgrimessuccess:
428245426Sjilles	if (cd)
429245426Sjilles		cmdtable_cd = 1;
4301556Srgrimes	entry->cmdtype = cmdp->cmdtype;
4311556Srgrimes	entry->u = cmdp->param;
432157601Sstefanf	entry->special = cmdp->special;
4331556Srgrimes}
4341556Srgrimes
4351556Srgrimes
4361556Srgrimes
4371556Srgrimes/*
4381556Srgrimes * Search the table of builtin commands.
4391556Srgrimes */
4401556Srgrimes
4411556Srgrimesint
442200956Sjillesfind_builtin(const char *name, int *special)
44317987Speter{
44425223Ssteve	const struct builtincmd *bp;
4451556Srgrimes
4461556Srgrimes	for (bp = builtincmd ; bp->name ; bp++) {
447157601Sstefanf		if (*bp->name == *name && equal(bp->name, name)) {
448157601Sstefanf			*special = bp->special;
4491556Srgrimes			return bp->code;
450157601Sstefanf		}
4511556Srgrimes	}
4521556Srgrimes	return -1;
4531556Srgrimes}
4541556Srgrimes
4551556Srgrimes
4561556Srgrimes
4571556Srgrimes/*
458245426Sjilles * Called when a cd is done.  If any entry in cmdtable depends on the current
459245426Sjilles * directory, simply clear cmdtable completely.
4601556Srgrimes */
4611556Srgrimes
4621556Srgrimesvoid
46390111Simphashcd(void)
46490111Simp{
465245426Sjilles	if (cmdtable_cd)
466245426Sjilles		clearcmdentry();
4671556Srgrimes}
4681556Srgrimes
4691556Srgrimes
4701556Srgrimes
4711556Srgrimes/*
4721556Srgrimes * Called before PATH is changed.  The argument is the new value of PATH;
4731556Srgrimes * pathval() still returns the old value at this point.  Called with
4741556Srgrimes * interrupts off.
4751556Srgrimes */
4761556Srgrimes
4771556Srgrimesvoid
478230530Scharnierchangepath(const char *newval __unused)
47917987Speter{
480218324Sjilles	clearcmdentry();
4811556Srgrimes}
4821556Srgrimes
4831556Srgrimes
4841556Srgrimes/*
4851556Srgrimes * Clear out command entries.  The argument specifies the first entry in
4861556Srgrimes * PATH which has changed.
4871556Srgrimes */
4881556Srgrimes
48954884Scracauervoid
490218324Sjillesclearcmdentry(void)
49117987Speter{
4921556Srgrimes	struct tblentry **tblp;
4931556Srgrimes	struct tblentry **pp;
4941556Srgrimes	struct tblentry *cmdp;
4951556Srgrimes
4961556Srgrimes	INTOFF;
4971556Srgrimes	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4981556Srgrimes		pp = tblp;
4991556Srgrimes		while ((cmdp = *pp) != NULL) {
500218324Sjilles			if (cmdp->cmdtype == CMDNORMAL) {
5011556Srgrimes				*pp = cmdp->next;
5021556Srgrimes				ckfree(cmdp);
5031556Srgrimes			} else {
5041556Srgrimes				pp = &cmdp->next;
5051556Srgrimes			}
5061556Srgrimes		}
5071556Srgrimes	}
508245426Sjilles	cmdtable_cd = 0;
5091556Srgrimes	INTON;
5101556Srgrimes}
5111556Srgrimes
5121556Srgrimes
5131556Srgrimes/*
5141556Srgrimes * Locate a command in the command hash table.  If "add" is nonzero,
5151556Srgrimes * add the command to the table if it is not already present.  The
5161556Srgrimes * variable "lastcmdentry" is set to point to the address of the link
5171556Srgrimes * pointing to the entry, so that delete_cmd_entry can delete the
5181556Srgrimes * entry.
5191556Srgrimes */
5201556Srgrimes
521213760Sobrienstatic struct tblentry **lastcmdentry;
5221556Srgrimes
5231556Srgrimes
524213811Sobrienstatic struct tblentry *
525200956Sjillescmdlookup(const char *name, int add)
52617987Speter{
5271556Srgrimes	int hashval;
528200956Sjilles	const char *p;
5291556Srgrimes	struct tblentry *cmdp;
5301556Srgrimes	struct tblentry **pp;
531262951Sjmmv	size_t len;
5321556Srgrimes
5331556Srgrimes	p = name;
5341556Srgrimes	hashval = *p << 4;
5351556Srgrimes	while (*p)
5361556Srgrimes		hashval += *p++;
5371556Srgrimes	hashval &= 0x7FFF;
5381556Srgrimes	pp = &cmdtable[hashval % CMDTABLESIZE];
5391556Srgrimes	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
5401556Srgrimes		if (equal(cmdp->cmdname, name))
5411556Srgrimes			break;
5421556Srgrimes		pp = &cmdp->next;
5431556Srgrimes	}
5441556Srgrimes	if (add && cmdp == NULL) {
5451556Srgrimes		INTOFF;
546262951Sjmmv		len = strlen(name);
547262951Sjmmv		cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
5481556Srgrimes		cmdp->next = NULL;
5491556Srgrimes		cmdp->cmdtype = CMDUNKNOWN;
550262951Sjmmv		memcpy(cmdp->cmdname, name, len + 1);
5511556Srgrimes		INTON;
5521556Srgrimes	}
5531556Srgrimes	lastcmdentry = pp;
5541556Srgrimes	return cmdp;
5551556Srgrimes}
5561556Srgrimes
5571556Srgrimes/*
5581556Srgrimes * Delete the command entry returned on the last lookup.
5591556Srgrimes */
5601556Srgrimes
561213811Sobrienstatic void
56290111Simpdelete_cmd_entry(void)
56390111Simp{
5641556Srgrimes	struct tblentry *cmdp;
5651556Srgrimes
5661556Srgrimes	INTOFF;
5671556Srgrimes	cmdp = *lastcmdentry;
5681556Srgrimes	*lastcmdentry = cmdp->next;
5691556Srgrimes	ckfree(cmdp);
5701556Srgrimes	INTON;
5711556Srgrimes}
5721556Srgrimes
5731556Srgrimes
5741556Srgrimes
5751556Srgrimes/*
5761556Srgrimes * Add a new command entry, replacing any existing command entry for
5771556Srgrimes * the same name.
5781556Srgrimes */
5791556Srgrimes
580229220Sjillesstatic void
581200956Sjillesaddcmdentry(const char *name, struct cmdentry *entry)
58290111Simp{
5831556Srgrimes	struct tblentry *cmdp;
5841556Srgrimes
5851556Srgrimes	INTOFF;
5861556Srgrimes	cmdp = cmdlookup(name, 1);
5871556Srgrimes	if (cmdp->cmdtype == CMDFUNCTION) {
588196483Sjilles		unreffunc(cmdp->param.func);
5891556Srgrimes	}
5901556Srgrimes	cmdp->cmdtype = entry->cmdtype;
5911556Srgrimes	cmdp->param = entry->u;
5921556Srgrimes	INTON;
5931556Srgrimes}
5941556Srgrimes
5951556Srgrimes
5961556Srgrimes/*
5971556Srgrimes * Define a shell function.
5981556Srgrimes */
5991556Srgrimes
6001556Srgrimesvoid
601200956Sjillesdefun(const char *name, union node *func)
60290111Simp{
6031556Srgrimes	struct cmdentry entry;
6041556Srgrimes
6051556Srgrimes	INTOFF;
6061556Srgrimes	entry.cmdtype = CMDFUNCTION;
6071556Srgrimes	entry.u.func = copyfunc(func);
6081556Srgrimes	addcmdentry(name, &entry);
6091556Srgrimes	INTON;
6101556Srgrimes}
6111556Srgrimes
6121556Srgrimes
6131556Srgrimes/*
6141556Srgrimes * Delete a function if it exists.
615264478Sjilles * Called with interrupts off.
6161556Srgrimes */
6171556Srgrimes
6181556Srgrimesint
619200956Sjillesunsetfunc(const char *name)
62090111Simp{
6211556Srgrimes	struct tblentry *cmdp;
6221556Srgrimes
6231556Srgrimes	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
624196483Sjilles		unreffunc(cmdp->param.func);
6251556Srgrimes		delete_cmd_entry();
6261556Srgrimes		return (0);
6271556Srgrimes	}
628135856Sdes	return (0);
6291556Srgrimes}
63025223Ssteve
631238468Sjilles
63225223Ssteve/*
633238468Sjilles * Check if a function by a certain name exists.
634238468Sjilles */
635238468Sjillesint
636238468Sjillesisfunc(const char *name)
637238468Sjilles{
638238468Sjilles	struct tblentry *cmdp;
639238468Sjilles	cmdp = cmdlookup(name, 0);
640238468Sjilles	return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
641238468Sjilles}
642238468Sjilles
643238468Sjilles
644238468Sjilles/*
645151810Sstefanf * Shared code for the following builtin commands:
646151810Sstefanf *    type, command -v, command -V
64725223Ssteve */
64825223Ssteve
64925223Ssteveint
650201343Sjillestypecmd_impl(int argc, char **argv, int cmd, const char *path)
65125223Ssteve{
65225223Ssteve	struct cmdentry entry;
65325223Ssteve	struct tblentry *cmdp;
654201053Sjilles	const char *const *pp;
65525223Ssteve	struct alias *ap;
65625223Ssteve	int i;
657201053Sjilles	int error1 = 0;
65825223Ssteve
659201343Sjilles	if (path != pathval())
660218324Sjilles		clearcmdentry();
661201343Sjilles
66225223Ssteve	for (i = 1; i < argc; i++) {
66325223Ssteve		/* First look at the keywords */
664201053Sjilles		for (pp = parsekwd; *pp; pp++)
66525223Ssteve			if (**pp == *argv[i] && equal(*pp, argv[i]))
66625223Ssteve				break;
66725223Ssteve
66825223Ssteve		if (*pp) {
669151810Sstefanf			if (cmd == TYPECMD_SMALLV)
670151810Sstefanf				out1fmt("%s\n", argv[i]);
671151810Sstefanf			else
672185401Sstefanf				out1fmt("%s is a shell keyword\n", argv[i]);
67325223Ssteve			continue;
67425223Ssteve		}
67525223Ssteve
67625223Ssteve		/* Then look at the aliases */
67725223Ssteve		if ((ap = lookupalias(argv[i], 1)) != NULL) {
678262951Sjmmv			if (cmd == TYPECMD_SMALLV) {
679262951Sjmmv				out1fmt("alias %s=", argv[i]);
680262951Sjmmv				out1qstr(ap->val);
681262951Sjmmv				outcslow('\n', out1);
682262951Sjmmv			} else
683185401Sstefanf				out1fmt("%s is an alias for %s\n", argv[i],
684185401Sstefanf				    ap->val);
68525223Ssteve			continue;
68625223Ssteve		}
68725223Ssteve
68825223Ssteve		/* Then check if it is a tracked alias */
68925223Ssteve		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
69025223Ssteve			entry.cmdtype = cmdp->cmdtype;
69125223Ssteve			entry.u = cmdp->param;
692194922Sjilles			entry.special = cmdp->special;
69325223Ssteve		}
69425223Ssteve		else {
69525223Ssteve			/* Finally use brute force */
696201343Sjilles			find_command(argv[i], &entry, 0, path);
69725223Ssteve		}
69825223Ssteve
69925223Ssteve		switch (entry.cmdtype) {
70025223Ssteve		case CMDNORMAL: {
70164704Scracauer			if (strchr(argv[i], '/') == NULL) {
702201343Sjilles				const char *path2 = path;
703200956Sjilles				char *name;
70464704Scracauer				int j = entry.u.index;
70564704Scracauer				do {
706201343Sjilles					name = padvance(&path2, argv[i]);
70764704Scracauer					stunalloc(name);
70864704Scracauer				} while (--j >= 0);
709151810Sstefanf				if (cmd == TYPECMD_SMALLV)
710151810Sstefanf					out1fmt("%s\n", name);
711151810Sstefanf				else
712185401Sstefanf					out1fmt("%s is%s %s\n", argv[i],
713151810Sstefanf					    (cmdp && cmd == TYPECMD_TYPE) ?
714151810Sstefanf						" a tracked alias for" : "",
715151810Sstefanf					    name);
71664704Scracauer			} else {
717166101Sstefanf				if (eaccess(argv[i], X_OK) == 0) {
718151810Sstefanf					if (cmd == TYPECMD_SMALLV)
719151810Sstefanf						out1fmt("%s\n", argv[i]);
720151810Sstefanf					else
721185401Sstefanf						out1fmt("%s is %s\n", argv[i],
722185401Sstefanf						    argv[i]);
723165930Sstefanf				} else {
724165930Sstefanf					if (cmd != TYPECMD_SMALLV)
725185401Sstefanf						outfmt(out2, "%s: %s\n",
726185401Sstefanf						    argv[i], strerror(errno));
727201053Sjilles					error1 |= 127;
728151810Sstefanf				}
72964704Scracauer			}
73025223Ssteve			break;
73125223Ssteve		}
73225223Ssteve		case CMDFUNCTION:
733151810Sstefanf			if (cmd == TYPECMD_SMALLV)
734151810Sstefanf				out1fmt("%s\n", argv[i]);
735151810Sstefanf			else
736185401Sstefanf				out1fmt("%s is a shell function\n", argv[i]);
73725223Ssteve			break;
73825223Ssteve
73925223Ssteve		case CMDBUILTIN:
740151810Sstefanf			if (cmd == TYPECMD_SMALLV)
741151810Sstefanf				out1fmt("%s\n", argv[i]);
742194922Sjilles			else if (entry.special)
743194922Sjilles				out1fmt("%s is a special shell builtin\n",
744194922Sjilles				    argv[i]);
745151810Sstefanf			else
746185401Sstefanf				out1fmt("%s is a shell builtin\n", argv[i]);
74725223Ssteve			break;
74825223Ssteve
74925223Ssteve		default:
750151810Sstefanf			if (cmd != TYPECMD_SMALLV)
751185401Sstefanf				outfmt(out2, "%s: not found\n", argv[i]);
752201053Sjilles			error1 |= 127;
75325223Ssteve			break;
75425223Ssteve		}
75525223Ssteve	}
756201343Sjilles
757201343Sjilles	if (path != pathval())
758218324Sjilles		clearcmdentry();
759201343Sjilles
760201053Sjilles	return error1;
76125223Ssteve}
762151810Sstefanf
763151810Sstefanf/*
764151810Sstefanf * Locate and print what a word is...
765151810Sstefanf */
766151810Sstefanf
767151810Sstefanfint
768151810Sstefanftypecmd(int argc, char **argv)
769151810Sstefanf{
770255072Sjilles	if (argc > 2 && strcmp(argv[1], "--") == 0)
771255072Sjilles		argc--, argv++;
772201344Sjilles	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
773151810Sstefanf}
774