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