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