xref: /original-bsd/bin/sh/exec.c (revision 18fb888a)
1f2a20ce3Sbostic /*-
29216a44aSbostic  * Copyright (c) 1991, 1993
39216a44aSbostic  *	The Regents of the University of California.  All rights reserved.
4f2a20ce3Sbostic  *
5f2a20ce3Sbostic  * This code is derived from software contributed to Berkeley by
6f2a20ce3Sbostic  * Kenneth Almquist.
7f2a20ce3Sbostic  *
89216a44aSbostic  * %sccs.include.redist.c%
9f2a20ce3Sbostic  */
10f2a20ce3Sbostic 
11f2a20ce3Sbostic #ifndef lint
12*18fb888aSchristos static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 06/08/95";
13f2a20ce3Sbostic #endif /* not lint */
14f2a20ce3Sbostic 
1514dedd6eSchristos #include <sys/types.h>
1614dedd6eSchristos #include <sys/stat.h>
1714dedd6eSchristos #include <unistd.h>
1814dedd6eSchristos #include <fcntl.h>
1914dedd6eSchristos #include <errno.h>
2014dedd6eSchristos #include <stdlib.h>
2114dedd6eSchristos 
22f2a20ce3Sbostic /*
23f2a20ce3Sbostic  * When commands are first encountered, they are entered in a hash table.
24f2a20ce3Sbostic  * This ensures that a full path search will not have to be done for them
25f2a20ce3Sbostic  * on each invocation.
26f2a20ce3Sbostic  *
27f2a20ce3Sbostic  * We should investigate converting to a linear search, even though that
28f2a20ce3Sbostic  * would make the command name "hash" a misnomer.
29f2a20ce3Sbostic  */
30f2a20ce3Sbostic 
31f2a20ce3Sbostic #include "shell.h"
32f2a20ce3Sbostic #include "main.h"
33f2a20ce3Sbostic #include "nodes.h"
34f2a20ce3Sbostic #include "parser.h"
35f2a20ce3Sbostic #include "redir.h"
36f2a20ce3Sbostic #include "eval.h"
37f2a20ce3Sbostic #include "exec.h"
38f2a20ce3Sbostic #include "builtins.h"
39f2a20ce3Sbostic #include "var.h"
40f2a20ce3Sbostic #include "options.h"
41f2a20ce3Sbostic #include "input.h"
42f2a20ce3Sbostic #include "output.h"
43f2a20ce3Sbostic #include "syntax.h"
44f2a20ce3Sbostic #include "memalloc.h"
45f2a20ce3Sbostic #include "error.h"
46f2a20ce3Sbostic #include "init.h"
47f2a20ce3Sbostic #include "mystring.h"
4814dedd6eSchristos #include "show.h"
49af3be746Smarc #include "jobs.h"
50f2a20ce3Sbostic 
51f2a20ce3Sbostic 
52f2a20ce3Sbostic #define CMDTABLESIZE 31		/* should be prime */
53f2a20ce3Sbostic #define ARB 1			/* actual size determined at run time */
54f2a20ce3Sbostic 
55f2a20ce3Sbostic 
56f2a20ce3Sbostic 
57f2a20ce3Sbostic struct tblentry {
58f2a20ce3Sbostic 	struct tblentry *next;	/* next entry in hash chain */
59f2a20ce3Sbostic 	union param param;	/* definition of builtin function */
60f2a20ce3Sbostic 	short cmdtype;		/* index identifying command */
61f2a20ce3Sbostic 	char rehash;		/* if set, cd done since entry created */
62f2a20ce3Sbostic 	char cmdname[ARB];	/* name of command */
63f2a20ce3Sbostic };
64f2a20ce3Sbostic 
65f2a20ce3Sbostic 
66f2a20ce3Sbostic STATIC struct tblentry *cmdtable[CMDTABLESIZE];
670e183414Smarc STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
68f2a20ce3Sbostic 
69f2a20ce3Sbostic 
7014dedd6eSchristos STATIC void tryexec __P((char *, char **, char **));
7114dedd6eSchristos STATIC void execinterp __P((char **, char **));
7214dedd6eSchristos STATIC void printentry __P((struct tblentry *, int));
7314dedd6eSchristos STATIC void clearcmdentry __P((int));
7414dedd6eSchristos STATIC struct tblentry *cmdlookup __P((char *, int));
7514dedd6eSchristos STATIC void delete_cmd_entry __P((void));
76f2a20ce3Sbostic 
77f2a20ce3Sbostic 
78f2a20ce3Sbostic 
79f2a20ce3Sbostic /*
80f2a20ce3Sbostic  * Exec a program.  Never returns.  If you change this routine, you may
81f2a20ce3Sbostic  * have to change the find_command routine as well.
82f2a20ce3Sbostic  */
83f2a20ce3Sbostic 
84f2a20ce3Sbostic void
shellexec(argv,envp,path,index)85f2a20ce3Sbostic shellexec(argv, envp, path, index)
86f2a20ce3Sbostic 	char **argv, **envp;
87f2a20ce3Sbostic 	char *path;
8814dedd6eSchristos 	int index;
89f2a20ce3Sbostic {
90f2a20ce3Sbostic 	char *cmdname;
91f2a20ce3Sbostic 	int e;
92f2a20ce3Sbostic 
93f2a20ce3Sbostic 	if (strchr(argv[0], '/') != NULL) {
94f2a20ce3Sbostic 		tryexec(argv[0], argv, envp);
95f2a20ce3Sbostic 		e = errno;
96f2a20ce3Sbostic 	} else {
97f2a20ce3Sbostic 		e = ENOENT;
98f2a20ce3Sbostic 		while ((cmdname = padvance(&path, argv[0])) != NULL) {
99f2a20ce3Sbostic 			if (--index < 0 && pathopt == NULL) {
100f2a20ce3Sbostic 				tryexec(cmdname, argv, envp);
101f2a20ce3Sbostic 				if (errno != ENOENT && errno != ENOTDIR)
102f2a20ce3Sbostic 					e = errno;
103f2a20ce3Sbostic 			}
104f2a20ce3Sbostic 			stunalloc(cmdname);
105f2a20ce3Sbostic 		}
106f2a20ce3Sbostic 	}
107f2a20ce3Sbostic 	error2(argv[0], errmsg(e, E_EXEC));
108f2a20ce3Sbostic }
109f2a20ce3Sbostic 
110f2a20ce3Sbostic 
111f2a20ce3Sbostic STATIC void
tryexec(cmd,argv,envp)112f2a20ce3Sbostic tryexec(cmd, argv, envp)
113f2a20ce3Sbostic 	char *cmd;
114f2a20ce3Sbostic 	char **argv;
115f2a20ce3Sbostic 	char **envp;
116f2a20ce3Sbostic 	{
117f2a20ce3Sbostic 	int e;
11814dedd6eSchristos #ifndef BSD
119f2a20ce3Sbostic 	char *p;
12014dedd6eSchristos #endif
121f2a20ce3Sbostic 
122f2a20ce3Sbostic #ifdef SYSV
123f2a20ce3Sbostic 	do {
124f2a20ce3Sbostic 		execve(cmd, argv, envp);
125f2a20ce3Sbostic 	} while (errno == EINTR);
126f2a20ce3Sbostic #else
127f2a20ce3Sbostic 	execve(cmd, argv, envp);
128f2a20ce3Sbostic #endif
129f2a20ce3Sbostic 	e = errno;
130f2a20ce3Sbostic 	if (e == ENOEXEC) {
131f2a20ce3Sbostic 		initshellproc();
132f2a20ce3Sbostic 		setinputfile(cmd, 0);
133f2a20ce3Sbostic 		commandname = arg0 = savestr(argv[0]);
134f2a20ce3Sbostic #ifndef BSD
135f2a20ce3Sbostic 		pgetc(); pungetc();		/* fill up input buffer */
136f2a20ce3Sbostic 		p = parsenextc;
137f2a20ce3Sbostic 		if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
138f2a20ce3Sbostic 			argv[0] = cmd;
139f2a20ce3Sbostic 			execinterp(argv, envp);
140f2a20ce3Sbostic 		}
141f2a20ce3Sbostic #endif
142f2a20ce3Sbostic 		setparam(argv + 1);
143f2a20ce3Sbostic 		exraise(EXSHELLPROC);
144f2a20ce3Sbostic 		/*NOTREACHED*/
145f2a20ce3Sbostic 	}
146f2a20ce3Sbostic 	errno = e;
147f2a20ce3Sbostic }
148f2a20ce3Sbostic 
149f2a20ce3Sbostic 
150f2a20ce3Sbostic #ifndef BSD
151f2a20ce3Sbostic /*
152f2a20ce3Sbostic  * Execute an interpreter introduced by "#!", for systems where this
153f2a20ce3Sbostic  * feature has not been built into the kernel.  If the interpreter is
154f2a20ce3Sbostic  * the shell, return (effectively ignoring the "#!").  If the execution
155f2a20ce3Sbostic  * of the interpreter fails, exit.
156f2a20ce3Sbostic  *
157f2a20ce3Sbostic  * This code peeks inside the input buffer in order to avoid actually
158f2a20ce3Sbostic  * reading any input.  It would benefit from a rewrite.
159f2a20ce3Sbostic  */
160f2a20ce3Sbostic 
161f2a20ce3Sbostic #define NEWARGS 5
162f2a20ce3Sbostic 
163f2a20ce3Sbostic STATIC void
execinterp(argv,envp)164f2a20ce3Sbostic execinterp(argv, envp)
165f2a20ce3Sbostic 	char **argv, **envp;
166f2a20ce3Sbostic 	{
167f2a20ce3Sbostic 	int n;
168f2a20ce3Sbostic 	char *inp;
169f2a20ce3Sbostic 	char *outp;
170f2a20ce3Sbostic 	char c;
171f2a20ce3Sbostic 	char *p;
172f2a20ce3Sbostic 	char **ap;
173f2a20ce3Sbostic 	char *newargs[NEWARGS];
174f2a20ce3Sbostic 	int i;
175f2a20ce3Sbostic 	char **ap2;
176f2a20ce3Sbostic 	char **new;
177f2a20ce3Sbostic 
178f2a20ce3Sbostic 	n = parsenleft - 2;
179f2a20ce3Sbostic 	inp = parsenextc + 2;
180f2a20ce3Sbostic 	ap = newargs;
181f2a20ce3Sbostic 	for (;;) {
182f2a20ce3Sbostic 		while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
183f2a20ce3Sbostic 			inp++;
184f2a20ce3Sbostic 		if (n < 0)
185f2a20ce3Sbostic 			goto bad;
186f2a20ce3Sbostic 		if ((c = *inp++) == '\n')
187f2a20ce3Sbostic 			break;
188f2a20ce3Sbostic 		if (ap == &newargs[NEWARGS])
189f2a20ce3Sbostic bad:		  error("Bad #! line");
190f2a20ce3Sbostic 		STARTSTACKSTR(outp);
191f2a20ce3Sbostic 		do {
192f2a20ce3Sbostic 			STPUTC(c, outp);
193f2a20ce3Sbostic 		} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
194f2a20ce3Sbostic 		STPUTC('\0', outp);
195f2a20ce3Sbostic 		n++, inp--;
196f2a20ce3Sbostic 		*ap++ = grabstackstr(outp);
197f2a20ce3Sbostic 	}
198f2a20ce3Sbostic 	if (ap == newargs + 1) {	/* if no args, maybe no exec is needed */
199f2a20ce3Sbostic 		p = newargs[0];
200f2a20ce3Sbostic 		for (;;) {
201f2a20ce3Sbostic 			if (equal(p, "sh") || equal(p, "ash")) {
202f2a20ce3Sbostic 				return;
203f2a20ce3Sbostic 			}
204f2a20ce3Sbostic 			while (*p != '/') {
205f2a20ce3Sbostic 				if (*p == '\0')
206f2a20ce3Sbostic 					goto break2;
207f2a20ce3Sbostic 				p++;
208f2a20ce3Sbostic 			}
209f2a20ce3Sbostic 			p++;
210f2a20ce3Sbostic 		}
211f2a20ce3Sbostic break2:;
212f2a20ce3Sbostic 	}
213f2a20ce3Sbostic 	i = (char *)ap - (char *)newargs;		/* size in bytes */
214f2a20ce3Sbostic 	if (i == 0)
215f2a20ce3Sbostic 		error("Bad #! line");
216f2a20ce3Sbostic 	for (ap2 = argv ; *ap2++ != NULL ; );
217f2a20ce3Sbostic 	new = ckmalloc(i + ((char *)ap2 - (char *)argv));
218f2a20ce3Sbostic 	ap = newargs, ap2 = new;
219f2a20ce3Sbostic 	while ((i -= sizeof (char **)) >= 0)
220f2a20ce3Sbostic 		*ap2++ = *ap++;
221f2a20ce3Sbostic 	ap = argv;
222f2a20ce3Sbostic 	while (*ap2++ = *ap++);
223f2a20ce3Sbostic 	shellexec(new, envp, pathval(), 0);
224f2a20ce3Sbostic }
225f2a20ce3Sbostic #endif
226f2a20ce3Sbostic 
227f2a20ce3Sbostic 
228f2a20ce3Sbostic 
229f2a20ce3Sbostic /*
230f2a20ce3Sbostic  * Do a path search.  The variable path (passed by reference) should be
231f2a20ce3Sbostic  * set to the start of the path before the first call; padvance will update
232f2a20ce3Sbostic  * this value as it proceeds.  Successive calls to padvance will return
233f2a20ce3Sbostic  * the possible path expansions in sequence.  If an option (indicated by
234f2a20ce3Sbostic  * a percent sign) appears in the path entry then the global variable
235f2a20ce3Sbostic  * pathopt will be set to point to it; otherwise pathopt will be set to
236f2a20ce3Sbostic  * NULL.
237f2a20ce3Sbostic  */
238f2a20ce3Sbostic 
239f2a20ce3Sbostic char *pathopt;
240f2a20ce3Sbostic 
241f2a20ce3Sbostic char *
padvance(path,name)242f2a20ce3Sbostic padvance(path, name)
243f2a20ce3Sbostic 	char **path;
244f2a20ce3Sbostic 	char *name;
245f2a20ce3Sbostic 	{
246f2a20ce3Sbostic 	register char *p, *q;
247f2a20ce3Sbostic 	char *start;
248f2a20ce3Sbostic 	int len;
249f2a20ce3Sbostic 
250f2a20ce3Sbostic 	if (*path == NULL)
251f2a20ce3Sbostic 		return NULL;
252f2a20ce3Sbostic 	start = *path;
253f2a20ce3Sbostic 	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
254f2a20ce3Sbostic 	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
255f2a20ce3Sbostic 	while (stackblocksize() < len)
256f2a20ce3Sbostic 		growstackblock();
257f2a20ce3Sbostic 	q = stackblock();
258f2a20ce3Sbostic 	if (p != start) {
25914dedd6eSchristos 		memcpy(q, start, p - start);
260f2a20ce3Sbostic 		q += p - start;
261f2a20ce3Sbostic 		*q++ = '/';
262f2a20ce3Sbostic 	}
263f2a20ce3Sbostic 	strcpy(q, name);
264f2a20ce3Sbostic 	pathopt = NULL;
265f2a20ce3Sbostic 	if (*p == '%') {
266f2a20ce3Sbostic 		pathopt = ++p;
267f2a20ce3Sbostic 		while (*p && *p != ':')  p++;
268f2a20ce3Sbostic 	}
269f2a20ce3Sbostic 	if (*p == ':')
270f2a20ce3Sbostic 		*path = p + 1;
271f2a20ce3Sbostic 	else
272f2a20ce3Sbostic 		*path = NULL;
273f2a20ce3Sbostic 	return stalloc(len);
274f2a20ce3Sbostic }
275f2a20ce3Sbostic 
276f2a20ce3Sbostic 
277f2a20ce3Sbostic 
278f2a20ce3Sbostic /*** Command hashing code ***/
279f2a20ce3Sbostic 
280f2a20ce3Sbostic 
28114dedd6eSchristos int
hashcmd(argc,argv)28214dedd6eSchristos hashcmd(argc, argv)
28314dedd6eSchristos 	int argc;
28414dedd6eSchristos 	char **argv;
28514dedd6eSchristos {
286f2a20ce3Sbostic 	struct tblentry **pp;
287f2a20ce3Sbostic 	struct tblentry *cmdp;
288f2a20ce3Sbostic 	int c;
289f2a20ce3Sbostic 	int verbose;
290f2a20ce3Sbostic 	struct cmdentry entry;
291f2a20ce3Sbostic 	char *name;
292f2a20ce3Sbostic 
293f2a20ce3Sbostic 	verbose = 0;
2940e183414Smarc 	while ((c = nextopt("rv")) != '\0') {
295f2a20ce3Sbostic 		if (c == 'r') {
296f2a20ce3Sbostic 			clearcmdentry(0);
297f2a20ce3Sbostic 		} else if (c == 'v') {
298f2a20ce3Sbostic 			verbose++;
299f2a20ce3Sbostic 		}
300f2a20ce3Sbostic 	}
301f37e4ee1Smarc 	if (*argptr == NULL) {
302f37e4ee1Smarc 		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
303f37e4ee1Smarc 			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
304f37e4ee1Smarc 				printentry(cmdp, verbose);
305f37e4ee1Smarc 			}
306f37e4ee1Smarc 		}
307f37e4ee1Smarc 		return 0;
308f37e4ee1Smarc 	}
309f2a20ce3Sbostic 	while ((name = *argptr) != NULL) {
310f2a20ce3Sbostic 		if ((cmdp = cmdlookup(name, 0)) != NULL
311f2a20ce3Sbostic 		 && (cmdp->cmdtype == CMDNORMAL
31214dedd6eSchristos 		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
313f2a20ce3Sbostic 			delete_cmd_entry();
314*18fb888aSchristos 		find_command(name, &entry, 1, pathval());
315f2a20ce3Sbostic 		if (verbose) {
316f2a20ce3Sbostic 			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
317f2a20ce3Sbostic 				cmdp = cmdlookup(name, 0);
318f37e4ee1Smarc 				printentry(cmdp, verbose);
319f2a20ce3Sbostic 			}
320f2a20ce3Sbostic 			flushall();
321f2a20ce3Sbostic 		}
322f2a20ce3Sbostic 		argptr++;
323f2a20ce3Sbostic 	}
324f2a20ce3Sbostic 	return 0;
325f2a20ce3Sbostic }
326f2a20ce3Sbostic 
327f2a20ce3Sbostic 
328f2a20ce3Sbostic STATIC void
printentry(cmdp,verbose)329f37e4ee1Smarc printentry(cmdp, verbose)
330f2a20ce3Sbostic 	struct tblentry *cmdp;
331f37e4ee1Smarc 	int verbose;
332f2a20ce3Sbostic 	{
333f2a20ce3Sbostic 	int index;
334f2a20ce3Sbostic 	char *path;
335f2a20ce3Sbostic 	char *name;
336f2a20ce3Sbostic 
337f2a20ce3Sbostic 	if (cmdp->cmdtype == CMDNORMAL) {
338f2a20ce3Sbostic 		index = cmdp->param.index;
339f2a20ce3Sbostic 		path = pathval();
340f2a20ce3Sbostic 		do {
341f2a20ce3Sbostic 			name = padvance(&path, cmdp->cmdname);
342f2a20ce3Sbostic 			stunalloc(name);
343f2a20ce3Sbostic 		} while (--index >= 0);
344f2a20ce3Sbostic 		out1str(name);
345f2a20ce3Sbostic 	} else if (cmdp->cmdtype == CMDBUILTIN) {
346f2a20ce3Sbostic 		out1fmt("builtin %s", cmdp->cmdname);
347f2a20ce3Sbostic 	} else if (cmdp->cmdtype == CMDFUNCTION) {
348f2a20ce3Sbostic 		out1fmt("function %s", cmdp->cmdname);
349f37e4ee1Smarc 		if (verbose) {
350f37e4ee1Smarc 			INTOFF;
351f37e4ee1Smarc 			name = commandtext(cmdp->param.func);
352f37e4ee1Smarc 			out1c(' ');
353f37e4ee1Smarc 			out1str(name);
354f37e4ee1Smarc 			ckfree(name);
355f37e4ee1Smarc 			INTON;
356f37e4ee1Smarc 		}
357f2a20ce3Sbostic #ifdef DEBUG
358f2a20ce3Sbostic 	} else {
359f2a20ce3Sbostic 		error("internal error: cmdtype %d", cmdp->cmdtype);
360f2a20ce3Sbostic #endif
361f2a20ce3Sbostic 	}
362f2a20ce3Sbostic 	if (cmdp->rehash)
363f2a20ce3Sbostic 		out1c('*');
364f2a20ce3Sbostic 	out1c('\n');
365f2a20ce3Sbostic }
366f2a20ce3Sbostic 
367f2a20ce3Sbostic 
368f2a20ce3Sbostic 
369f2a20ce3Sbostic /*
370f2a20ce3Sbostic  * Resolve a command name.  If you change this routine, you may have to
371f2a20ce3Sbostic  * change the shellexec routine as well.
372f2a20ce3Sbostic  */
373f2a20ce3Sbostic 
374f2a20ce3Sbostic void
find_command(name,entry,printerr,path)375*18fb888aSchristos find_command(name, entry, printerr, path)
376f2a20ce3Sbostic 	char *name;
377f2a20ce3Sbostic 	struct cmdentry *entry;
37814dedd6eSchristos 	int printerr;
379*18fb888aSchristos 	char *path;
380f2a20ce3Sbostic {
381f2a20ce3Sbostic 	struct tblentry *cmdp;
382f2a20ce3Sbostic 	int index;
383f2a20ce3Sbostic 	int prev;
384f2a20ce3Sbostic 	char *fullname;
385f2a20ce3Sbostic 	struct stat statb;
386f2a20ce3Sbostic 	int e;
387f2a20ce3Sbostic 	int i;
388f2a20ce3Sbostic 
389f2a20ce3Sbostic 	/* If name contains a slash, don't use the hash table */
390f2a20ce3Sbostic 	if (strchr(name, '/') != NULL) {
391f2a20ce3Sbostic 		entry->cmdtype = CMDNORMAL;
392f2a20ce3Sbostic 		entry->u.index = 0;
393f2a20ce3Sbostic 		return;
394f2a20ce3Sbostic 	}
395f2a20ce3Sbostic 
396f2a20ce3Sbostic 	/* If name is in the table, and not invalidated by cd, we're done */
397f2a20ce3Sbostic 	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
398f2a20ce3Sbostic 		goto success;
399f2a20ce3Sbostic 
400f2a20ce3Sbostic 	/* If %builtin not in path, check for builtin next */
401f2a20ce3Sbostic 	if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
402f2a20ce3Sbostic 		INTOFF;
403f2a20ce3Sbostic 		cmdp = cmdlookup(name, 1);
404f2a20ce3Sbostic 		cmdp->cmdtype = CMDBUILTIN;
405f2a20ce3Sbostic 		cmdp->param.index = i;
406f2a20ce3Sbostic 		INTON;
407f2a20ce3Sbostic 		goto success;
408f2a20ce3Sbostic 	}
409f2a20ce3Sbostic 
410f2a20ce3Sbostic 	/* We have to search path. */
411f2a20ce3Sbostic 	prev = -1;		/* where to start */
412f2a20ce3Sbostic 	if (cmdp) {		/* doing a rehash */
413f2a20ce3Sbostic 		if (cmdp->cmdtype == CMDBUILTIN)
414f2a20ce3Sbostic 			prev = builtinloc;
415f2a20ce3Sbostic 		else
416f2a20ce3Sbostic 			prev = cmdp->param.index;
417f2a20ce3Sbostic 	}
418f2a20ce3Sbostic 
419f2a20ce3Sbostic 	e = ENOENT;
420f2a20ce3Sbostic 	index = -1;
421f2a20ce3Sbostic loop:
422f2a20ce3Sbostic 	while ((fullname = padvance(&path, name)) != NULL) {
423f2a20ce3Sbostic 		stunalloc(fullname);
424f2a20ce3Sbostic 		index++;
425f2a20ce3Sbostic 		if (pathopt) {
426f2a20ce3Sbostic 			if (prefix("builtin", pathopt)) {
427f2a20ce3Sbostic 				if ((i = find_builtin(name)) < 0)
428f2a20ce3Sbostic 					goto loop;
429f2a20ce3Sbostic 				INTOFF;
430f2a20ce3Sbostic 				cmdp = cmdlookup(name, 1);
431f2a20ce3Sbostic 				cmdp->cmdtype = CMDBUILTIN;
432f2a20ce3Sbostic 				cmdp->param.index = i;
433f2a20ce3Sbostic 				INTON;
434f2a20ce3Sbostic 				goto success;
435f2a20ce3Sbostic 			} else if (prefix("func", pathopt)) {
436f2a20ce3Sbostic 				/* handled below */
437f2a20ce3Sbostic 			} else {
438f2a20ce3Sbostic 				goto loop;	/* ignore unimplemented options */
439f2a20ce3Sbostic 			}
440f2a20ce3Sbostic 		}
441f2a20ce3Sbostic 		/* if rehash, don't redo absolute path names */
442f2a20ce3Sbostic 		if (fullname[0] == '/' && index <= prev) {
443f2a20ce3Sbostic 			if (index < prev)
444f2a20ce3Sbostic 				goto loop;
445f2a20ce3Sbostic 			TRACE(("searchexec \"%s\": no change\n", name));
446f2a20ce3Sbostic 			goto success;
447f2a20ce3Sbostic 		}
448f2a20ce3Sbostic 		while (stat(fullname, &statb) < 0) {
449f2a20ce3Sbostic #ifdef SYSV
450f2a20ce3Sbostic 			if (errno == EINTR)
451f2a20ce3Sbostic 				continue;
452f2a20ce3Sbostic #endif
453f2a20ce3Sbostic 			if (errno != ENOENT && errno != ENOTDIR)
454f2a20ce3Sbostic 				e = errno;
455f2a20ce3Sbostic 			goto loop;
456f2a20ce3Sbostic 		}
457f2a20ce3Sbostic 		e = EACCES;	/* if we fail, this will be the error */
45814dedd6eSchristos 		if (!S_ISREG(statb.st_mode))
459f2a20ce3Sbostic 			goto loop;
460f2a20ce3Sbostic 		if (pathopt) {		/* this is a %func directory */
461f2a20ce3Sbostic 			stalloc(strlen(fullname) + 1);
462f2a20ce3Sbostic 			readcmdfile(fullname);
463f2a20ce3Sbostic 			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
464f2a20ce3Sbostic 				error("%s not defined in %s", name, fullname);
465f2a20ce3Sbostic 			stunalloc(fullname);
466f2a20ce3Sbostic 			goto success;
467f2a20ce3Sbostic 		}
468b3c33852Smarc #ifdef notdef
469f2a20ce3Sbostic 		if (statb.st_uid == geteuid()) {
470f2a20ce3Sbostic 			if ((statb.st_mode & 0100) == 0)
471f2a20ce3Sbostic 				goto loop;
472f2a20ce3Sbostic 		} else if (statb.st_gid == getegid()) {
473f2a20ce3Sbostic 			if ((statb.st_mode & 010) == 0)
474f2a20ce3Sbostic 				goto loop;
475f2a20ce3Sbostic 		} else {
476f2a20ce3Sbostic 			if ((statb.st_mode & 01) == 0)
477f2a20ce3Sbostic 				goto loop;
478f2a20ce3Sbostic 		}
479b3c33852Smarc #endif
480f2a20ce3Sbostic 		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
481f2a20ce3Sbostic 		INTOFF;
482f2a20ce3Sbostic 		cmdp = cmdlookup(name, 1);
483f2a20ce3Sbostic 		cmdp->cmdtype = CMDNORMAL;
484f2a20ce3Sbostic 		cmdp->param.index = index;
485f2a20ce3Sbostic 		INTON;
486f2a20ce3Sbostic 		goto success;
487f2a20ce3Sbostic 	}
488f2a20ce3Sbostic 
489f2a20ce3Sbostic 	/* We failed.  If there was an entry for this command, delete it */
490f2a20ce3Sbostic 	if (cmdp)
491f2a20ce3Sbostic 		delete_cmd_entry();
492f2a20ce3Sbostic 	if (printerr)
493f2a20ce3Sbostic 		outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
494f2a20ce3Sbostic 	entry->cmdtype = CMDUNKNOWN;
495f2a20ce3Sbostic 	return;
496f2a20ce3Sbostic 
497f2a20ce3Sbostic success:
498f2a20ce3Sbostic 	cmdp->rehash = 0;
499f2a20ce3Sbostic 	entry->cmdtype = cmdp->cmdtype;
500f2a20ce3Sbostic 	entry->u = cmdp->param;
501f2a20ce3Sbostic }
502f2a20ce3Sbostic 
503f2a20ce3Sbostic 
504f2a20ce3Sbostic 
505f2a20ce3Sbostic /*
506f2a20ce3Sbostic  * Search the table of builtin commands.
507f2a20ce3Sbostic  */
508f2a20ce3Sbostic 
509f2a20ce3Sbostic int
find_builtin(name)510f2a20ce3Sbostic find_builtin(name)
511f2a20ce3Sbostic 	char *name;
512f2a20ce3Sbostic {
51314dedd6eSchristos 	register const struct builtincmd *bp;
514f2a20ce3Sbostic 
515f2a20ce3Sbostic 	for (bp = builtincmd ; bp->name ; bp++) {
516f2a20ce3Sbostic 		if (*bp->name == *name && equal(bp->name, name))
517f2a20ce3Sbostic 			return bp->code;
518f2a20ce3Sbostic 	}
519f2a20ce3Sbostic 	return -1;
520f2a20ce3Sbostic }
521f2a20ce3Sbostic 
522f2a20ce3Sbostic 
523f2a20ce3Sbostic 
524f2a20ce3Sbostic /*
525f2a20ce3Sbostic  * Called when a cd is done.  Marks all commands so the next time they
526f2a20ce3Sbostic  * are executed they will be rehashed.
527f2a20ce3Sbostic  */
528f2a20ce3Sbostic 
529f2a20ce3Sbostic void
hashcd()530f2a20ce3Sbostic hashcd() {
531f2a20ce3Sbostic 	struct tblentry **pp;
532f2a20ce3Sbostic 	struct tblentry *cmdp;
533f2a20ce3Sbostic 
534f2a20ce3Sbostic 	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
535f2a20ce3Sbostic 		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
536f2a20ce3Sbostic 			if (cmdp->cmdtype == CMDNORMAL
53714dedd6eSchristos 			 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
538f2a20ce3Sbostic 				cmdp->rehash = 1;
539f2a20ce3Sbostic 		}
540f2a20ce3Sbostic 	}
541f2a20ce3Sbostic }
542f2a20ce3Sbostic 
543f2a20ce3Sbostic 
544f2a20ce3Sbostic 
545f2a20ce3Sbostic /*
546f2a20ce3Sbostic  * Called before PATH is changed.  The argument is the new value of PATH;
547f2a20ce3Sbostic  * pathval() still returns the old value at this point.  Called with
548f2a20ce3Sbostic  * interrupts off.
549f2a20ce3Sbostic  */
550f2a20ce3Sbostic 
551f2a20ce3Sbostic void
changepath(newval)552f2a20ce3Sbostic changepath(newval)
553f2a20ce3Sbostic 	char *newval;
554f2a20ce3Sbostic {
555f2a20ce3Sbostic 	char *old, *new;
556f2a20ce3Sbostic 	int index;
557f2a20ce3Sbostic 	int firstchange;
558f2a20ce3Sbostic 	int bltin;
559f2a20ce3Sbostic 
560f2a20ce3Sbostic 	old = pathval();
561f2a20ce3Sbostic 	new = newval;
562f2a20ce3Sbostic 	firstchange = 9999;	/* assume no change */
56314dedd6eSchristos 	index = 0;
564f2a20ce3Sbostic 	bltin = -1;
565f2a20ce3Sbostic 	for (;;) {
566f2a20ce3Sbostic 		if (*old != *new) {
567f2a20ce3Sbostic 			firstchange = index;
56814dedd6eSchristos 			if ((*old == '\0' && *new == ':')
56914dedd6eSchristos 			 || (*old == ':' && *new == '\0'))
570f2a20ce3Sbostic 				firstchange++;
571f2a20ce3Sbostic 			old = new;	/* ignore subsequent differences */
572f2a20ce3Sbostic 		}
573f2a20ce3Sbostic 		if (*new == '\0')
574f2a20ce3Sbostic 			break;
575f2a20ce3Sbostic 		if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
576f2a20ce3Sbostic 			bltin = index;
577f2a20ce3Sbostic 		if (*new == ':') {
578f2a20ce3Sbostic 			index++;
579f2a20ce3Sbostic 		}
580f2a20ce3Sbostic 		new++, old++;
581f2a20ce3Sbostic 	}
582f2a20ce3Sbostic 	if (builtinloc < 0 && bltin >= 0)
583f2a20ce3Sbostic 		builtinloc = bltin;		/* zap builtins */
584f2a20ce3Sbostic 	if (builtinloc >= 0 && bltin < 0)
585f2a20ce3Sbostic 		firstchange = 0;
586f2a20ce3Sbostic 	clearcmdentry(firstchange);
587f2a20ce3Sbostic 	builtinloc = bltin;
588f2a20ce3Sbostic }
589f2a20ce3Sbostic 
590f2a20ce3Sbostic 
591f2a20ce3Sbostic /*
592f2a20ce3Sbostic  * Clear out command entries.  The argument specifies the first entry in
593f2a20ce3Sbostic  * PATH which has changed.
594f2a20ce3Sbostic  */
595f2a20ce3Sbostic 
596f2a20ce3Sbostic STATIC void
clearcmdentry(firstchange)59714dedd6eSchristos clearcmdentry(firstchange)
59814dedd6eSchristos 	int firstchange;
59914dedd6eSchristos {
600f2a20ce3Sbostic 	struct tblentry **tblp;
601f2a20ce3Sbostic 	struct tblentry **pp;
602f2a20ce3Sbostic 	struct tblentry *cmdp;
603f2a20ce3Sbostic 
604f2a20ce3Sbostic 	INTOFF;
605f2a20ce3Sbostic 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
606f2a20ce3Sbostic 		pp = tblp;
607f2a20ce3Sbostic 		while ((cmdp = *pp) != NULL) {
60814dedd6eSchristos 			if ((cmdp->cmdtype == CMDNORMAL &&
60914dedd6eSchristos 			     cmdp->param.index >= firstchange)
61014dedd6eSchristos 			 || (cmdp->cmdtype == CMDBUILTIN &&
61114dedd6eSchristos 			     builtinloc >= firstchange)) {
612f2a20ce3Sbostic 				*pp = cmdp->next;
613f2a20ce3Sbostic 				ckfree(cmdp);
614f2a20ce3Sbostic 			} else {
615f2a20ce3Sbostic 				pp = &cmdp->next;
616f2a20ce3Sbostic 			}
617f2a20ce3Sbostic 		}
618f2a20ce3Sbostic 	}
619f2a20ce3Sbostic 	INTON;
620f2a20ce3Sbostic }
621f2a20ce3Sbostic 
622f2a20ce3Sbostic 
623f2a20ce3Sbostic /*
624f2a20ce3Sbostic  * Delete all functions.
625f2a20ce3Sbostic  */
626f2a20ce3Sbostic 
627f2a20ce3Sbostic #ifdef mkinit
628f2a20ce3Sbostic MKINIT void deletefuncs();
629f2a20ce3Sbostic 
630f2a20ce3Sbostic SHELLPROC {
631f2a20ce3Sbostic 	deletefuncs();
632f2a20ce3Sbostic }
633f2a20ce3Sbostic #endif
634f2a20ce3Sbostic 
635f2a20ce3Sbostic void
deletefuncs()636f2a20ce3Sbostic deletefuncs() {
637f2a20ce3Sbostic 	struct tblentry **tblp;
638f2a20ce3Sbostic 	struct tblentry **pp;
639f2a20ce3Sbostic 	struct tblentry *cmdp;
640f2a20ce3Sbostic 
641f2a20ce3Sbostic 	INTOFF;
642f2a20ce3Sbostic 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
643f2a20ce3Sbostic 		pp = tblp;
644f2a20ce3Sbostic 		while ((cmdp = *pp) != NULL) {
645f2a20ce3Sbostic 			if (cmdp->cmdtype == CMDFUNCTION) {
646f2a20ce3Sbostic 				*pp = cmdp->next;
647f2a20ce3Sbostic 				freefunc(cmdp->param.func);
648f2a20ce3Sbostic 				ckfree(cmdp);
649f2a20ce3Sbostic 			} else {
650f2a20ce3Sbostic 				pp = &cmdp->next;
651f2a20ce3Sbostic 			}
652f2a20ce3Sbostic 		}
653f2a20ce3Sbostic 	}
654f2a20ce3Sbostic 	INTON;
655f2a20ce3Sbostic }
656f2a20ce3Sbostic 
657f2a20ce3Sbostic 
658f2a20ce3Sbostic 
659f2a20ce3Sbostic /*
660f2a20ce3Sbostic  * Locate a command in the command hash table.  If "add" is nonzero,
661f2a20ce3Sbostic  * add the command to the table if it is not already present.  The
662f2a20ce3Sbostic  * variable "lastcmdentry" is set to point to the address of the link
663f2a20ce3Sbostic  * pointing to the entry, so that delete_cmd_entry can delete the
664f2a20ce3Sbostic  * entry.
665f2a20ce3Sbostic  */
666f2a20ce3Sbostic 
667f2a20ce3Sbostic struct tblentry **lastcmdentry;
668f2a20ce3Sbostic 
669f2a20ce3Sbostic 
670f2a20ce3Sbostic STATIC struct tblentry *
cmdlookup(name,add)671f2a20ce3Sbostic cmdlookup(name, add)
672f2a20ce3Sbostic 	char *name;
67314dedd6eSchristos 	int add;
674f2a20ce3Sbostic {
675f2a20ce3Sbostic 	int hashval;
676f2a20ce3Sbostic 	register char *p;
677f2a20ce3Sbostic 	struct tblentry *cmdp;
678f2a20ce3Sbostic 	struct tblentry **pp;
679f2a20ce3Sbostic 
680f2a20ce3Sbostic 	p = name;
681f2a20ce3Sbostic 	hashval = *p << 4;
682f2a20ce3Sbostic 	while (*p)
683f2a20ce3Sbostic 		hashval += *p++;
684f2a20ce3Sbostic 	hashval &= 0x7FFF;
685f2a20ce3Sbostic 	pp = &cmdtable[hashval % CMDTABLESIZE];
686f2a20ce3Sbostic 	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
687f2a20ce3Sbostic 		if (equal(cmdp->cmdname, name))
688f2a20ce3Sbostic 			break;
689f2a20ce3Sbostic 		pp = &cmdp->next;
690f2a20ce3Sbostic 	}
691f2a20ce3Sbostic 	if (add && cmdp == NULL) {
692f2a20ce3Sbostic 		INTOFF;
693f2a20ce3Sbostic 		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
694f2a20ce3Sbostic 					+ strlen(name) + 1);
695f2a20ce3Sbostic 		cmdp->next = NULL;
696f2a20ce3Sbostic 		cmdp->cmdtype = CMDUNKNOWN;
697f2a20ce3Sbostic 		cmdp->rehash = 0;
698f2a20ce3Sbostic 		strcpy(cmdp->cmdname, name);
699f2a20ce3Sbostic 		INTON;
700f2a20ce3Sbostic 	}
701f2a20ce3Sbostic 	lastcmdentry = pp;
702f2a20ce3Sbostic 	return cmdp;
703f2a20ce3Sbostic }
704f2a20ce3Sbostic 
705f2a20ce3Sbostic /*
706f2a20ce3Sbostic  * Delete the command entry returned on the last lookup.
707f2a20ce3Sbostic  */
708f2a20ce3Sbostic 
709f2a20ce3Sbostic STATIC void
delete_cmd_entry()710f2a20ce3Sbostic delete_cmd_entry() {
711f2a20ce3Sbostic 	struct tblentry *cmdp;
712f2a20ce3Sbostic 
713f2a20ce3Sbostic 	INTOFF;
714f2a20ce3Sbostic 	cmdp = *lastcmdentry;
715f2a20ce3Sbostic 	*lastcmdentry = cmdp->next;
716f2a20ce3Sbostic 	ckfree(cmdp);
717f2a20ce3Sbostic 	INTON;
718f2a20ce3Sbostic }
719f2a20ce3Sbostic 
720f2a20ce3Sbostic 
721f2a20ce3Sbostic 
722f2a20ce3Sbostic #ifdef notdef
723f2a20ce3Sbostic void
getcmdentry(name,entry)724f2a20ce3Sbostic getcmdentry(name, entry)
725f2a20ce3Sbostic 	char *name;
726f2a20ce3Sbostic 	struct cmdentry *entry;
727f2a20ce3Sbostic 	{
728f2a20ce3Sbostic 	struct tblentry *cmdp = cmdlookup(name, 0);
729f2a20ce3Sbostic 
730f2a20ce3Sbostic 	if (cmdp) {
731f2a20ce3Sbostic 		entry->u = cmdp->param;
732f2a20ce3Sbostic 		entry->cmdtype = cmdp->cmdtype;
733f2a20ce3Sbostic 	} else {
734f2a20ce3Sbostic 		entry->cmdtype = CMDUNKNOWN;
735f2a20ce3Sbostic 		entry->u.index = 0;
736f2a20ce3Sbostic 	}
737f2a20ce3Sbostic }
738f2a20ce3Sbostic #endif
739f2a20ce3Sbostic 
740f2a20ce3Sbostic 
741f2a20ce3Sbostic /*
742f2a20ce3Sbostic  * Add a new command entry, replacing any existing command entry for
743f2a20ce3Sbostic  * the same name.
744f2a20ce3Sbostic  */
745f2a20ce3Sbostic 
746f2a20ce3Sbostic void
addcmdentry(name,entry)747f2a20ce3Sbostic addcmdentry(name, entry)
748f2a20ce3Sbostic 	char *name;
749f2a20ce3Sbostic 	struct cmdentry *entry;
750f2a20ce3Sbostic 	{
751f2a20ce3Sbostic 	struct tblentry *cmdp;
752f2a20ce3Sbostic 
753f2a20ce3Sbostic 	INTOFF;
754f2a20ce3Sbostic 	cmdp = cmdlookup(name, 1);
755f2a20ce3Sbostic 	if (cmdp->cmdtype == CMDFUNCTION) {
756f2a20ce3Sbostic 		freefunc(cmdp->param.func);
757f2a20ce3Sbostic 	}
758f2a20ce3Sbostic 	cmdp->cmdtype = entry->cmdtype;
759f2a20ce3Sbostic 	cmdp->param = entry->u;
760f2a20ce3Sbostic 	INTON;
761f2a20ce3Sbostic }
762f2a20ce3Sbostic 
763f2a20ce3Sbostic 
764f2a20ce3Sbostic /*
765f2a20ce3Sbostic  * Define a shell function.
766f2a20ce3Sbostic  */
767f2a20ce3Sbostic 
768f2a20ce3Sbostic void
defun(name,func)769f2a20ce3Sbostic defun(name, func)
770f2a20ce3Sbostic 	char *name;
771f2a20ce3Sbostic 	union node *func;
772f2a20ce3Sbostic 	{
773f2a20ce3Sbostic 	struct cmdentry entry;
774f2a20ce3Sbostic 
775f2a20ce3Sbostic 	INTOFF;
776f2a20ce3Sbostic 	entry.cmdtype = CMDFUNCTION;
777f2a20ce3Sbostic 	entry.u.func = copyfunc(func);
778f2a20ce3Sbostic 	addcmdentry(name, &entry);
779f2a20ce3Sbostic 	INTON;
780f2a20ce3Sbostic }
781f2a20ce3Sbostic 
782f2a20ce3Sbostic 
783f2a20ce3Sbostic /*
784f2a20ce3Sbostic  * Delete a function if it exists.
785f2a20ce3Sbostic  */
786f2a20ce3Sbostic 
78722767f1aSmarc int
unsetfunc(name)788f2a20ce3Sbostic unsetfunc(name)
789f2a20ce3Sbostic 	char *name;
790f2a20ce3Sbostic 	{
791f2a20ce3Sbostic 	struct tblentry *cmdp;
792f2a20ce3Sbostic 
793f2a20ce3Sbostic 	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
794f2a20ce3Sbostic 		freefunc(cmdp->param.func);
795f2a20ce3Sbostic 		delete_cmd_entry();
79622767f1aSmarc 		return (0);
797f2a20ce3Sbostic 	}
79822767f1aSmarc 	return (1);
799f2a20ce3Sbostic }
800