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