Annotation of /trunk/mkinitrd-magellan/klibc/usr/dash/exec.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 18121 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 18121 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | /*- |
2 | * Copyright (c) 1991, 1993 | ||
3 | * The Regents of the University of California. All rights reserved. | ||
4 | * Copyright (c) 1997-2005 | ||
5 | * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. | ||
6 | * | ||
7 | * This code is derived from software contributed to Berkeley by | ||
8 | * Kenneth Almquist. | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions | ||
12 | * are met: | ||
13 | * 1. Redistributions of source code must retain the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer. | ||
15 | * 2. Redistributions in binary form must reproduce the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer in the | ||
17 | * documentation and/or other materials provided with the distribution. | ||
18 | * 3. Neither the name of the University nor the names of its contributors | ||
19 | * may be used to endorse or promote products derived from this software | ||
20 | * without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
32 | * SUCH DAMAGE. | ||
33 | */ | ||
34 | |||
35 | #include <sys/types.h> | ||
36 | #include <sys/stat.h> | ||
37 | #include <unistd.h> | ||
38 | #include <fcntl.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <paths.h> | ||
41 | |||
42 | /* | ||
43 | * When commands are first encountered, they are entered in a hash table. | ||
44 | * This ensures that a full path search will not have to be done for them | ||
45 | * on each invocation. | ||
46 | * | ||
47 | * We should investigate converting to a linear search, even though that | ||
48 | * would make the command name "hash" a misnomer. | ||
49 | */ | ||
50 | |||
51 | #include "shell.h" | ||
52 | #include "main.h" | ||
53 | #include "nodes.h" | ||
54 | #include "parser.h" | ||
55 | #include "redir.h" | ||
56 | #include "eval.h" | ||
57 | #include "exec.h" | ||
58 | #include "builtins.h" | ||
59 | #include "var.h" | ||
60 | #include "options.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 "show.h" | ||
68 | #include "jobs.h" | ||
69 | #include "alias.h" | ||
70 | #include "system.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]; | ||
88 | STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ | ||
89 | |||
90 | |||
91 | STATIC void tryexec(char *, char **, char **); | ||
92 | STATIC void printentry(struct tblentry *); | ||
93 | STATIC void clearcmdentry(int); | ||
94 | STATIC struct tblentry *cmdlookup(const char *, int); | ||
95 | STATIC void delete_cmd_entry(void); | ||
96 | STATIC void addcmdentry(char *, struct cmdentry *); | ||
97 | STATIC int describe_command(struct output *, char *, int); | ||
98 | |||
99 | |||
100 | /* | ||
101 | * Exec a program. Never returns. If you change this routine, you may | ||
102 | * have to change the find_command routine as well. | ||
103 | */ | ||
104 | |||
105 | void | ||
106 | shellexec(char **argv, const char *path, int idx) | ||
107 | { | ||
108 | char *cmdname; | ||
109 | int e; | ||
110 | char **envp; | ||
111 | int exerrno; | ||
112 | |||
113 | clearredir(1); | ||
114 | envp = environment(); | ||
115 | if (strchr(argv[0], '/') != NULL) { | ||
116 | tryexec(argv[0], argv, envp); | ||
117 | e = errno; | ||
118 | } else { | ||
119 | e = ENOENT; | ||
120 | while ((cmdname = padvance(&path, argv[0])) != NULL) { | ||
121 | if (--idx < 0 && pathopt == NULL) { | ||
122 | tryexec(cmdname, argv, envp); | ||
123 | if (errno != ENOENT && errno != ENOTDIR) | ||
124 | e = errno; | ||
125 | } | ||
126 | stunalloc(cmdname); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* Map to POSIX errors */ | ||
131 | switch (e) { | ||
132 | case EACCES: | ||
133 | exerrno = 126; | ||
134 | break; | ||
135 | case ENOENT: | ||
136 | exerrno = 127; | ||
137 | break; | ||
138 | default: | ||
139 | exerrno = 2; | ||
140 | break; | ||
141 | } | ||
142 | exitstatus = exerrno; | ||
143 | TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", | ||
144 | argv[0], e, suppressint )); | ||
145 | exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); | ||
146 | /* NOTREACHED */ | ||
147 | } | ||
148 | |||
149 | |||
150 | STATIC void | ||
151 | tryexec(char *cmd, char **argv, char **envp) | ||
152 | { | ||
153 | int repeated = 0; | ||
154 | #if !defined(BSD) && !defined(linux) | ||
155 | char *p; | ||
156 | #endif | ||
157 | |||
158 | repeat: | ||
159 | #ifdef SYSV | ||
160 | do { | ||
161 | execve(cmd, argv, envp); | ||
162 | } while (errno == EINTR); | ||
163 | #else | ||
164 | execve(cmd, argv, envp); | ||
165 | #endif | ||
166 | if (repeated++) { | ||
167 | ckfree(argv); | ||
168 | } else if (errno == ENOEXEC) { | ||
169 | char **ap; | ||
170 | char **new; | ||
171 | |||
172 | for (ap = argv; *ap; ap++) | ||
173 | ; | ||
174 | ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); | ||
175 | *ap++ = cmd = _PATH_BSHELL; | ||
176 | while ((*ap++ = *argv++)) | ||
177 | ; | ||
178 | argv = new; | ||
179 | goto repeat; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | |||
184 | |||
185 | /* | ||
186 | * Do a path search. The variable path (passed by reference) should be | ||
187 | * set to the start of the path before the first call; padvance will update | ||
188 | * this value as it proceeds. Successive calls to padvance will return | ||
189 | * the possible path expansions in sequence. If an option (indicated by | ||
190 | * a percent sign) appears in the path entry then the global variable | ||
191 | * pathopt will be set to point to it; otherwise pathopt will be set to | ||
192 | * NULL. | ||
193 | */ | ||
194 | |||
195 | const char *pathopt; | ||
196 | |||
197 | char * | ||
198 | padvance(const char **path, const char *name) | ||
199 | { | ||
200 | const char *p; | ||
201 | char *q; | ||
202 | const char *start; | ||
203 | size_t len; | ||
204 | |||
205 | if (*path == NULL) | ||
206 | return NULL; | ||
207 | start = *path; | ||
208 | for (p = start ; *p && *p != ':' && *p != '%' ; p++); | ||
209 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ | ||
210 | while (stackblocksize() < len) | ||
211 | growstackblock(); | ||
212 | q = stackblock(); | ||
213 | if (p != start) { | ||
214 | memcpy(q, start, p - start); | ||
215 | q += p - start; | ||
216 | *q++ = '/'; | ||
217 | } | ||
218 | strcpy(q, name); | ||
219 | pathopt = NULL; | ||
220 | if (*p == '%') { | ||
221 | pathopt = ++p; | ||
222 | while (*p && *p != ':') p++; | ||
223 | } | ||
224 | if (*p == ':') | ||
225 | *path = p + 1; | ||
226 | else | ||
227 | *path = NULL; | ||
228 | return stalloc(len); | ||
229 | } | ||
230 | |||
231 | |||
232 | |||
233 | /*** Command hashing code ***/ | ||
234 | |||
235 | |||
236 | int | ||
237 | hashcmd(int argc, char **argv) | ||
238 | { | ||
239 | struct tblentry **pp; | ||
240 | struct tblentry *cmdp; | ||
241 | int c; | ||
242 | struct cmdentry entry; | ||
243 | char *name; | ||
244 | |||
245 | while ((c = nextopt("r")) != '\0') { | ||
246 | clearcmdentry(0); | ||
247 | return 0; | ||
248 | } | ||
249 | if (*argptr == NULL) { | ||
250 | for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { | ||
251 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | ||
252 | if (cmdp->cmdtype == CMDNORMAL) | ||
253 | printentry(cmdp); | ||
254 | } | ||
255 | } | ||
256 | return 0; | ||
257 | } | ||
258 | c = 0; | ||
259 | while ((name = *argptr) != NULL) { | ||
260 | if ((cmdp = cmdlookup(name, 0)) != NULL | ||
261 | && (cmdp->cmdtype == CMDNORMAL | ||
262 | || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) | ||
263 | delete_cmd_entry(); | ||
264 | find_command(name, &entry, DO_ERR, pathval()); | ||
265 | if (entry.cmdtype == CMDUNKNOWN) | ||
266 | c = 1; | ||
267 | argptr++; | ||
268 | } | ||
269 | return c; | ||
270 | } | ||
271 | |||
272 | |||
273 | STATIC void | ||
274 | printentry(struct tblentry *cmdp) | ||
275 | { | ||
276 | int idx; | ||
277 | const char *path; | ||
278 | char *name; | ||
279 | |||
280 | idx = cmdp->param.index; | ||
281 | path = pathval(); | ||
282 | do { | ||
283 | name = padvance(&path, cmdp->cmdname); | ||
284 | stunalloc(name); | ||
285 | } while (--idx >= 0); | ||
286 | out1str(name); | ||
287 | out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr); | ||
288 | } | ||
289 | |||
290 | |||
291 | |||
292 | /* | ||
293 | * Resolve a command name. If you change this routine, you may have to | ||
294 | * change the shellexec routine as well. | ||
295 | */ | ||
296 | |||
297 | void | ||
298 | find_command(char *name, struct cmdentry *entry, int act, const char *path) | ||
299 | { | ||
300 | struct tblentry *cmdp; | ||
301 | int idx; | ||
302 | int prev; | ||
303 | char *fullname; | ||
304 | struct stat64 statb; | ||
305 | int e; | ||
306 | int updatetbl; | ||
307 | struct builtincmd *bcmd; | ||
308 | |||
309 | /* If name contains a slash, don't use PATH or hash table */ | ||
310 | if (strchr(name, '/') != NULL) { | ||
311 | entry->u.index = -1; | ||
312 | if (act & DO_ABS) { | ||
313 | while (stat64(name, &statb) < 0) { | ||
314 | #ifdef SYSV | ||
315 | if (errno == EINTR) | ||
316 | continue; | ||
317 | #endif | ||
318 | entry->cmdtype = CMDUNKNOWN; | ||
319 | return; | ||
320 | } | ||
321 | } | ||
322 | entry->cmdtype = CMDNORMAL; | ||
323 | return; | ||
324 | } | ||
325 | |||
326 | updatetbl = (path == pathval()); | ||
327 | if (!updatetbl) { | ||
328 | act |= DO_ALTPATH; | ||
329 | if (strstr(path, "%builtin") != NULL) | ||
330 | act |= DO_ALTBLTIN; | ||
331 | } | ||
332 | |||
333 | /* If name is in the table, check answer will be ok */ | ||
334 | if ((cmdp = cmdlookup(name, 0)) != NULL) { | ||
335 | int bit; | ||
336 | |||
337 | switch (cmdp->cmdtype) { | ||
338 | default: | ||
339 | #if DEBUG | ||
340 | abort(); | ||
341 | #endif | ||
342 | case CMDNORMAL: | ||
343 | bit = DO_ALTPATH; | ||
344 | break; | ||
345 | case CMDFUNCTION: | ||
346 | bit = DO_NOFUNC; | ||
347 | break; | ||
348 | case CMDBUILTIN: | ||
349 | bit = DO_ALTBLTIN; | ||
350 | break; | ||
351 | } | ||
352 | if (act & bit) { | ||
353 | updatetbl = 0; | ||
354 | cmdp = NULL; | ||
355 | } else if (cmdp->rehash == 0) | ||
356 | /* if not invalidated by cd, we're done */ | ||
357 | goto success; | ||
358 | } | ||
359 | |||
360 | /* If %builtin not in path, check for builtin next */ | ||
361 | bcmd = find_builtin(name); | ||
362 | if (bcmd && (bcmd->flags & BUILTIN_REGULAR || ( | ||
363 | act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0 | ||
364 | ))) | ||
365 | goto builtin_success; | ||
366 | |||
367 | /* We have to search path. */ | ||
368 | prev = -1; /* where to start */ | ||
369 | if (cmdp && cmdp->rehash) { /* doing a rehash */ | ||
370 | if (cmdp->cmdtype == CMDBUILTIN) | ||
371 | prev = builtinloc; | ||
372 | else | ||
373 | prev = cmdp->param.index; | ||
374 | } | ||
375 | |||
376 | e = ENOENT; | ||
377 | idx = -1; | ||
378 | loop: | ||
379 | while ((fullname = padvance(&path, name)) != NULL) { | ||
380 | stunalloc(fullname); | ||
381 | idx++; | ||
382 | if (pathopt) { | ||
383 | if (prefix(pathopt, "builtin")) { | ||
384 | if (bcmd) | ||
385 | goto builtin_success; | ||
386 | continue; | ||
387 | } else if (!(act & DO_NOFUNC) && | ||
388 | prefix(pathopt, "func")) { | ||
389 | /* handled below */ | ||
390 | } else { | ||
391 | /* ignore unimplemented options */ | ||
392 | continue; | ||
393 | } | ||
394 | } | ||
395 | /* if rehash, don't redo absolute path names */ | ||
396 | if (fullname[0] == '/' && idx <= prev) { | ||
397 | if (idx < prev) | ||
398 | continue; | ||
399 | TRACE(("searchexec \"%s\": no change\n", name)); | ||
400 | goto success; | ||
401 | } | ||
402 | while (stat64(fullname, &statb) < 0) { | ||
403 | #ifdef SYSV | ||
404 | if (errno == EINTR) | ||
405 | continue; | ||
406 | #endif | ||
407 | if (errno != ENOENT && errno != ENOTDIR) | ||
408 | e = errno; | ||
409 | goto loop; | ||
410 | } | ||
411 | e = EACCES; /* if we fail, this will be the error */ | ||
412 | if (!S_ISREG(statb.st_mode)) | ||
413 | continue; | ||
414 | if (pathopt) { /* this is a %func directory */ | ||
415 | stalloc(strlen(fullname) + 1); | ||
416 | readcmdfile(fullname); | ||
417 | if ((cmdp = cmdlookup(name, 0)) == NULL || | ||
418 | cmdp->cmdtype != CMDFUNCTION) | ||
419 | sh_error("%s not defined in %s", name, | ||
420 | fullname); | ||
421 | stunalloc(fullname); | ||
422 | goto success; | ||
423 | } | ||
424 | #ifdef notdef | ||
425 | /* XXX this code stops root executing stuff, and is buggy | ||
426 | if you need a group from the group list. */ | ||
427 | if (statb.st_uid == geteuid()) { | ||
428 | if ((statb.st_mode & 0100) == 0) | ||
429 | goto loop; | ||
430 | } else if (statb.st_gid == getegid()) { | ||
431 | if ((statb.st_mode & 010) == 0) | ||
432 | goto loop; | ||
433 | } else { | ||
434 | if ((statb.st_mode & 01) == 0) | ||
435 | goto loop; | ||
436 | } | ||
437 | #endif | ||
438 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); | ||
439 | if (!updatetbl) { | ||
440 | entry->cmdtype = CMDNORMAL; | ||
441 | entry->u.index = idx; | ||
442 | return; | ||
443 | } | ||
444 | INTOFF; | ||
445 | cmdp = cmdlookup(name, 1); | ||
446 | cmdp->cmdtype = CMDNORMAL; | ||
447 | cmdp->param.index = idx; | ||
448 | INTON; | ||
449 | goto success; | ||
450 | } | ||
451 | |||
452 | /* We failed. If there was an entry for this command, delete it */ | ||
453 | if (cmdp && updatetbl) | ||
454 | delete_cmd_entry(); | ||
455 | if (act & DO_ERR) | ||
456 | sh_warnx("%s: %s", name, errmsg(e, E_EXEC)); | ||
457 | entry->cmdtype = CMDUNKNOWN; | ||
458 | return; | ||
459 | |||
460 | builtin_success: | ||
461 | if (!updatetbl) { | ||
462 | entry->cmdtype = CMDBUILTIN; | ||
463 | entry->u.cmd = bcmd; | ||
464 | return; | ||
465 | } | ||
466 | INTOFF; | ||
467 | cmdp = cmdlookup(name, 1); | ||
468 | cmdp->cmdtype = CMDBUILTIN; | ||
469 | cmdp->param.cmd = bcmd; | ||
470 | INTON; | ||
471 | success: | ||
472 | cmdp->rehash = 0; | ||
473 | entry->cmdtype = cmdp->cmdtype; | ||
474 | entry->u = cmdp->param; | ||
475 | } | ||
476 | |||
477 | |||
478 | |||
479 | /* | ||
480 | * Search the table of builtin commands. | ||
481 | */ | ||
482 | |||
483 | struct builtincmd * | ||
484 | find_builtin(const char *name) | ||
485 | { | ||
486 | struct builtincmd *bp; | ||
487 | |||
488 | bp = bsearch( | ||
489 | &name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd), | ||
490 | pstrcmp | ||
491 | ); | ||
492 | return bp; | ||
493 | } | ||
494 | |||
495 | |||
496 | |||
497 | /* | ||
498 | * Called when a cd is done. Marks all commands so the next time they | ||
499 | * are executed they will be rehashed. | ||
500 | */ | ||
501 | |||
502 | void | ||
503 | hashcd(void) | ||
504 | { | ||
505 | struct tblentry **pp; | ||
506 | struct tblentry *cmdp; | ||
507 | |||
508 | for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { | ||
509 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | ||
510 | if (cmdp->cmdtype == CMDNORMAL || ( | ||
511 | cmdp->cmdtype == CMDBUILTIN && | ||
512 | !(cmdp->param.cmd->flags & BUILTIN_REGULAR) && | ||
513 | builtinloc > 0 | ||
514 | )) | ||
515 | cmdp->rehash = 1; | ||
516 | } | ||
517 | } | ||
518 | } | ||
519 | |||
520 | |||
521 | |||
522 | /* | ||
523 | * Fix command hash table when PATH changed. | ||
524 | * Called before PATH is changed. The argument is the new value of PATH; | ||
525 | * pathval() still returns the old value at this point. | ||
526 | * Called with interrupts off. | ||
527 | */ | ||
528 | |||
529 | void | ||
530 | changepath(const char *newval) | ||
531 | { | ||
532 | const char *old, *new; | ||
533 | int idx; | ||
534 | int firstchange; | ||
535 | int bltin; | ||
536 | |||
537 | old = pathval(); | ||
538 | new = newval; | ||
539 | firstchange = 9999; /* assume no change */ | ||
540 | idx = 0; | ||
541 | bltin = -1; | ||
542 | for (;;) { | ||
543 | if (*old != *new) { | ||
544 | firstchange = idx; | ||
545 | if ((*old == '\0' && *new == ':') | ||
546 | || (*old == ':' && *new == '\0')) | ||
547 | firstchange++; | ||
548 | old = new; /* ignore subsequent differences */ | ||
549 | } | ||
550 | if (*new == '\0') | ||
551 | break; | ||
552 | if (*new == '%' && bltin < 0 && prefix(new + 1, "builtin")) | ||
553 | bltin = idx; | ||
554 | if (*new == ':') { | ||
555 | idx++; | ||
556 | } | ||
557 | new++, old++; | ||
558 | } | ||
559 | if (builtinloc < 0 && bltin >= 0) | ||
560 | builtinloc = bltin; /* zap builtins */ | ||
561 | if (builtinloc >= 0 && bltin < 0) | ||
562 | firstchange = 0; | ||
563 | clearcmdentry(firstchange); | ||
564 | builtinloc = bltin; | ||
565 | } | ||
566 | |||
567 | |||
568 | /* | ||
569 | * Clear out command entries. The argument specifies the first entry in | ||
570 | * PATH which has changed. | ||
571 | */ | ||
572 | |||
573 | STATIC void | ||
574 | clearcmdentry(int firstchange) | ||
575 | { | ||
576 | struct tblentry **tblp; | ||
577 | struct tblentry **pp; | ||
578 | struct tblentry *cmdp; | ||
579 | |||
580 | INTOFF; | ||
581 | for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { | ||
582 | pp = tblp; | ||
583 | while ((cmdp = *pp) != NULL) { | ||
584 | if ((cmdp->cmdtype == CMDNORMAL && | ||
585 | cmdp->param.index >= firstchange) | ||
586 | || (cmdp->cmdtype == CMDBUILTIN && | ||
587 | builtinloc >= firstchange)) { | ||
588 | *pp = cmdp->next; | ||
589 | ckfree(cmdp); | ||
590 | } else { | ||
591 | pp = &cmdp->next; | ||
592 | } | ||
593 | } | ||
594 | } | ||
595 | INTON; | ||
596 | } | ||
597 | |||
598 | |||
599 | |||
600 | /* | ||
601 | * Locate a command in the command hash table. If "add" is nonzero, | ||
602 | * add the command to the table if it is not already present. The | ||
603 | * variable "lastcmdentry" is set to point to the address of the link | ||
604 | * pointing to the entry, so that delete_cmd_entry can delete the | ||
605 | * entry. | ||
606 | * | ||
607 | * Interrupts must be off if called with add != 0. | ||
608 | */ | ||
609 | |||
610 | struct tblentry **lastcmdentry; | ||
611 | |||
612 | |||
613 | STATIC struct tblentry * | ||
614 | cmdlookup(const char *name, int add) | ||
615 | { | ||
616 | unsigned int hashval; | ||
617 | const char *p; | ||
618 | struct tblentry *cmdp; | ||
619 | struct tblentry **pp; | ||
620 | |||
621 | p = name; | ||
622 | hashval = (unsigned char)*p << 4; | ||
623 | while (*p) | ||
624 | hashval += (unsigned char)*p++; | ||
625 | hashval &= 0x7FFF; | ||
626 | pp = &cmdtable[hashval % CMDTABLESIZE]; | ||
627 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | ||
628 | if (equal(cmdp->cmdname, name)) | ||
629 | break; | ||
630 | pp = &cmdp->next; | ||
631 | } | ||
632 | if (add && cmdp == NULL) { | ||
633 | cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB | ||
634 | + strlen(name) + 1); | ||
635 | cmdp->next = NULL; | ||
636 | cmdp->cmdtype = CMDUNKNOWN; | ||
637 | strcpy(cmdp->cmdname, name); | ||
638 | } | ||
639 | lastcmdentry = pp; | ||
640 | return cmdp; | ||
641 | } | ||
642 | |||
643 | /* | ||
644 | * Delete the command entry returned on the last lookup. | ||
645 | */ | ||
646 | |||
647 | STATIC void | ||
648 | delete_cmd_entry(void) | ||
649 | { | ||
650 | struct tblentry *cmdp; | ||
651 | |||
652 | INTOFF; | ||
653 | cmdp = *lastcmdentry; | ||
654 | *lastcmdentry = cmdp->next; | ||
655 | if (cmdp->cmdtype == CMDFUNCTION) | ||
656 | freefunc(cmdp->param.func); | ||
657 | ckfree(cmdp); | ||
658 | INTON; | ||
659 | } | ||
660 | |||
661 | |||
662 | |||
663 | #ifdef notdef | ||
664 | void | ||
665 | getcmdentry(char *name, struct cmdentry *entry) | ||
666 | { | ||
667 | struct tblentry *cmdp = cmdlookup(name, 0); | ||
668 | |||
669 | if (cmdp) { | ||
670 | entry->u = cmdp->param; | ||
671 | entry->cmdtype = cmdp->cmdtype; | ||
672 | } else { | ||
673 | entry->cmdtype = CMDUNKNOWN; | ||
674 | entry->u.index = 0; | ||
675 | } | ||
676 | } | ||
677 | #endif | ||
678 | |||
679 | |||
680 | /* | ||
681 | * Add a new command entry, replacing any existing command entry for | ||
682 | * the same name - except special builtins. | ||
683 | */ | ||
684 | |||
685 | STATIC void | ||
686 | addcmdentry(char *name, struct cmdentry *entry) | ||
687 | { | ||
688 | struct tblentry *cmdp; | ||
689 | |||
690 | cmdp = cmdlookup(name, 1); | ||
691 | if (cmdp->cmdtype == CMDFUNCTION) { | ||
692 | freefunc(cmdp->param.func); | ||
693 | } | ||
694 | cmdp->cmdtype = entry->cmdtype; | ||
695 | cmdp->param = entry->u; | ||
696 | cmdp->rehash = 0; | ||
697 | } | ||
698 | |||
699 | |||
700 | /* | ||
701 | * Define a shell function. | ||
702 | */ | ||
703 | |||
704 | void | ||
705 | defun(char *name, union node *func) | ||
706 | { | ||
707 | struct cmdentry entry; | ||
708 | |||
709 | INTOFF; | ||
710 | entry.cmdtype = CMDFUNCTION; | ||
711 | entry.u.func = copyfunc(func); | ||
712 | addcmdentry(name, &entry); | ||
713 | INTON; | ||
714 | } | ||
715 | |||
716 | |||
717 | /* | ||
718 | * Delete a function if it exists. | ||
719 | */ | ||
720 | |||
721 | void | ||
722 | unsetfunc(const char *name) | ||
723 | { | ||
724 | struct tblentry *cmdp; | ||
725 | |||
726 | if ((cmdp = cmdlookup(name, 0)) != NULL && | ||
727 | cmdp->cmdtype == CMDFUNCTION) | ||
728 | delete_cmd_entry(); | ||
729 | } | ||
730 | |||
731 | /* | ||
732 | * Locate and print what a word is... | ||
733 | */ | ||
734 | |||
735 | int | ||
736 | typecmd(int argc, char **argv) | ||
737 | { | ||
738 | int i; | ||
739 | int err = 0; | ||
740 | |||
741 | for (i = 1; i < argc; i++) { | ||
742 | err |= describe_command(out1, argv[i], 1); | ||
743 | } | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | STATIC int | ||
748 | describe_command(out, command, verbose) | ||
749 | struct output *out; | ||
750 | char *command; | ||
751 | int verbose; | ||
752 | { | ||
753 | struct cmdentry entry; | ||
754 | struct tblentry *cmdp; | ||
755 | const struct alias *ap; | ||
756 | const char *path = pathval(); | ||
757 | |||
758 | if (verbose) { | ||
759 | outstr(command, out); | ||
760 | } | ||
761 | |||
762 | /* First look at the keywords */ | ||
763 | if (findkwd(command)) { | ||
764 | outstr(verbose ? " is a shell keyword" : command, out); | ||
765 | goto out; | ||
766 | } | ||
767 | |||
768 | /* Then look at the aliases */ | ||
769 | if ((ap = lookupalias(command, 0)) != NULL) { | ||
770 | if (verbose) { | ||
771 | outfmt(out, " is an alias for %s", ap->val); | ||
772 | } else { | ||
773 | outstr("alias ", out); | ||
774 | printalias(ap); | ||
775 | return 0; | ||
776 | } | ||
777 | goto out; | ||
778 | } | ||
779 | |||
780 | /* Then check if it is a tracked alias */ | ||
781 | if ((cmdp = cmdlookup(command, 0)) != NULL) { | ||
782 | entry.cmdtype = cmdp->cmdtype; | ||
783 | entry.u = cmdp->param; | ||
784 | } else { | ||
785 | /* Finally use brute force */ | ||
786 | find_command(command, &entry, DO_ABS, path); | ||
787 | } | ||
788 | |||
789 | switch (entry.cmdtype) { | ||
790 | case CMDNORMAL: { | ||
791 | int j = entry.u.index; | ||
792 | char *p; | ||
793 | if (j == -1) { | ||
794 | p = command; | ||
795 | } else { | ||
796 | do { | ||
797 | p = padvance(&path, command); | ||
798 | stunalloc(p); | ||
799 | } while (--j >= 0); | ||
800 | } | ||
801 | if (verbose) { | ||
802 | outfmt( | ||
803 | out, " is%s %s", | ||
804 | cmdp ? " a tracked alias for" : nullstr, p | ||
805 | ); | ||
806 | } else { | ||
807 | outstr(p, out); | ||
808 | } | ||
809 | break; | ||
810 | } | ||
811 | |||
812 | case CMDFUNCTION: | ||
813 | if (verbose) { | ||
814 | outstr(" is a shell function", out); | ||
815 | } else { | ||
816 | outstr(command, out); | ||
817 | } | ||
818 | break; | ||
819 | |||
820 | case CMDBUILTIN: | ||
821 | if (verbose) { | ||
822 | outfmt( | ||
823 | out, " is a %sshell builtin", | ||
824 | entry.u.cmd->flags & BUILTIN_SPECIAL ? | ||
825 | "special " : nullstr | ||
826 | ); | ||
827 | } else { | ||
828 | outstr(command, out); | ||
829 | } | ||
830 | break; | ||
831 | |||
832 | default: | ||
833 | if (verbose) { | ||
834 | outstr(": not found\n", out); | ||
835 | } | ||
836 | return 127; | ||
837 | } | ||
838 | |||
839 | out: | ||
840 | outc('\n', out); | ||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | int | ||
845 | commandcmd(argc, argv) | ||
846 | int argc; | ||
847 | char **argv; | ||
848 | { | ||
849 | int c; | ||
850 | enum { | ||
851 | VERIFY_BRIEF = 1, | ||
852 | VERIFY_VERBOSE = 2, | ||
853 | } verify = 0; | ||
854 | |||
855 | while ((c = nextopt("pvV")) != '\0') | ||
856 | if (c == 'V') | ||
857 | verify |= VERIFY_VERBOSE; | ||
858 | else if (c == 'v') | ||
859 | verify |= VERIFY_BRIEF; | ||
860 | #ifdef DEBUG | ||
861 | else if (c != 'p') | ||
862 | abort(); | ||
863 | #endif | ||
864 | |||
865 | if (verify) | ||
866 | return describe_command(out1, *argptr, verify - VERIFY_BRIEF); | ||
867 | |||
868 | return 0; | ||
869 | } |