exec.c revision 1.39 1 /* $NetBSD: exec.c,v 1.39 2006/03/18 05:23:08 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. 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/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
39 #else
40 __RCSID("$NetBSD: exec.c,v 1.39 2006/03/18 05:23:08 christos Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52
53 /*
54 * When commands are first encountered, they are entered in a hash table.
55 * This ensures that a full path search will not have to be done for them
56 * on each invocation.
57 *
58 * We should investigate converting to a linear search, even though that
59 * would make the command name "hash" a misnomer.
60 */
61
62 #include "shell.h"
63 #include "main.h"
64 #include "nodes.h"
65 #include "parser.h"
66 #include "redir.h"
67 #include "eval.h"
68 #include "exec.h"
69 #include "builtins.h"
70 #include "var.h"
71 #include "options.h"
72 #include "input.h"
73 #include "output.h"
74 #include "syntax.h"
75 #include "memalloc.h"
76 #include "error.h"
77 #include "init.h"
78 #include "mystring.h"
79 #include "show.h"
80 #include "jobs.h"
81 #include "alias.h"
82
83
84 #define CMDTABLESIZE 31 /* should be prime */
85 #define ARB 1 /* actual size determined at run time */
86
87
88
89 struct tblentry {
90 struct tblentry *next; /* next entry in hash chain */
91 union param param; /* definition of builtin function */
92 short cmdtype; /* index identifying command */
93 char rehash; /* if set, cd done since entry created */
94 char cmdname[ARB]; /* name of command */
95 };
96
97
98 STATIC struct tblentry *cmdtable[CMDTABLESIZE];
99 STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
100 int exerrno = 0; /* Last exec error */
101
102
103 STATIC void tryexec(char *, char **, char **, int);
104 STATIC void execinterp(char **, char **);
105 STATIC void printentry(struct tblentry *, int);
106 STATIC void clearcmdentry(int);
107 STATIC struct tblentry *cmdlookup(const char *, int);
108 STATIC void delete_cmd_entry(void);
109
110
111 extern char *const parsekwd[];
112
113 /*
114 * Exec a program. Never returns. If you change this routine, you may
115 * have to change the find_command routine as well.
116 */
117
118 void
119 shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
120 {
121 char *cmdname;
122 int e;
123
124 if (strchr(argv[0], '/') != NULL) {
125 tryexec(argv[0], argv, envp, vforked);
126 e = errno;
127 } else {
128 e = ENOENT;
129 while ((cmdname = padvance(&path, argv[0])) != NULL) {
130 if (--idx < 0 && pathopt == NULL) {
131 tryexec(cmdname, argv, envp, vforked);
132 if (errno != ENOENT && errno != ENOTDIR)
133 e = errno;
134 }
135 stunalloc(cmdname);
136 }
137 }
138
139 /* Map to POSIX errors */
140 switch (e) {
141 case EACCES:
142 exerrno = 126;
143 break;
144 case ENOENT:
145 exerrno = 127;
146 break;
147 default:
148 exerrno = 2;
149 break;
150 }
151 TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
152 argv[0], e, vforked, suppressint ));
153 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
154 /* NOTREACHED */
155 }
156
157
158 STATIC void
159 tryexec(char *cmd, char **argv, char **envp, int vforked)
160 {
161 int e;
162 #ifndef BSD
163 char *p;
164 #endif
165
166 #ifdef SYSV
167 do {
168 execve(cmd, argv, envp);
169 } while (errno == EINTR);
170 #else
171 execve(cmd, argv, envp);
172 #endif
173 e = errno;
174 if (e == ENOEXEC) {
175 if (vforked) {
176 /* We are currently vfork(2)ed, so raise an
177 * exception, and evalcommand will try again
178 * with a normal fork(2).
179 */
180 exraise(EXSHELLPROC);
181 }
182 initshellproc();
183 setinputfile(cmd, 0);
184 commandname = arg0 = savestr(argv[0]);
185 #ifndef BSD
186 pgetc(); pungetc(); /* fill up input buffer */
187 p = parsenextc;
188 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
189 argv[0] = cmd;
190 execinterp(argv, envp);
191 }
192 #endif
193 setparam(argv + 1);
194 exraise(EXSHELLPROC);
195 }
196 errno = e;
197 }
198
199
200 #ifndef BSD
201 /*
202 * Execute an interpreter introduced by "#!", for systems where this
203 * feature has not been built into the kernel. If the interpreter is
204 * the shell, return (effectively ignoring the "#!"). If the execution
205 * of the interpreter fails, exit.
206 *
207 * This code peeks inside the input buffer in order to avoid actually
208 * reading any input. It would benefit from a rewrite.
209 */
210
211 #define NEWARGS 5
212
213 STATIC void
214 execinterp(char **argv, char **envp)
215 {
216 int n;
217 char *inp;
218 char *outp;
219 char c;
220 char *p;
221 char **ap;
222 char *newargs[NEWARGS];
223 int i;
224 char **ap2;
225 char **new;
226
227 n = parsenleft - 2;
228 inp = parsenextc + 2;
229 ap = newargs;
230 for (;;) {
231 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
232 inp++;
233 if (n < 0)
234 goto bad;
235 if ((c = *inp++) == '\n')
236 break;
237 if (ap == &newargs[NEWARGS])
238 bad: error("Bad #! line");
239 STARTSTACKSTR(outp);
240 do {
241 STPUTC(c, outp);
242 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
243 STPUTC('\0', outp);
244 n++, inp--;
245 *ap++ = grabstackstr(outp);
246 }
247 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
248 p = newargs[0];
249 for (;;) {
250 if (equal(p, "sh") || equal(p, "ash")) {
251 return;
252 }
253 while (*p != '/') {
254 if (*p == '\0')
255 goto break2;
256 p++;
257 }
258 p++;
259 }
260 break2:;
261 }
262 i = (char *)ap - (char *)newargs; /* size in bytes */
263 if (i == 0)
264 error("Bad #! line");
265 for (ap2 = argv ; *ap2++ != NULL ; );
266 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
267 ap = newargs, ap2 = new;
268 while ((i -= sizeof (char **)) >= 0)
269 *ap2++ = *ap++;
270 ap = argv;
271 while (*ap2++ = *ap++);
272 shellexec(new, envp, pathval(), 0);
273 /* NOTREACHED */
274 }
275 #endif
276
277
278
279 /*
280 * Do a path search. The variable path (passed by reference) should be
281 * set to the start of the path before the first call; padvance will update
282 * this value as it proceeds. Successive calls to padvance will return
283 * the possible path expansions in sequence. If an option (indicated by
284 * a percent sign) appears in the path entry then the global variable
285 * pathopt will be set to point to it; otherwise pathopt will be set to
286 * NULL.
287 */
288
289 const char *pathopt;
290
291 char *
292 padvance(const char **path, const char *name)
293 {
294 const char *p;
295 char *q;
296 const char *start;
297 int len;
298
299 if (*path == NULL)
300 return NULL;
301 start = *path;
302 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
303 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
304 while (stackblocksize() < len)
305 growstackblock();
306 q = stackblock();
307 if (p != start) {
308 memcpy(q, start, p - start);
309 q += p - start;
310 *q++ = '/';
311 }
312 strcpy(q, name);
313 pathopt = NULL;
314 if (*p == '%') {
315 pathopt = ++p;
316 while (*p && *p != ':') p++;
317 }
318 if (*p == ':')
319 *path = p + 1;
320 else
321 *path = NULL;
322 return stalloc(len);
323 }
324
325
326
327 /*** Command hashing code ***/
328
329
330 int
331 hashcmd(int argc, char **argv)
332 {
333 struct tblentry **pp;
334 struct tblentry *cmdp;
335 int c;
336 int verbose;
337 struct cmdentry entry;
338 char *name;
339
340 verbose = 0;
341 while ((c = nextopt("rv")) != '\0') {
342 if (c == 'r') {
343 clearcmdentry(0);
344 } else if (c == 'v') {
345 verbose++;
346 }
347 }
348 if (*argptr == NULL) {
349 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
350 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
351 if (verbose || cmdp->cmdtype == CMDNORMAL)
352 printentry(cmdp, verbose);
353 }
354 }
355 return 0;
356 }
357 while ((name = *argptr) != NULL) {
358 if ((cmdp = cmdlookup(name, 0)) != NULL
359 && (cmdp->cmdtype == CMDNORMAL
360 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
361 delete_cmd_entry();
362 find_command(name, &entry, DO_ERR, pathval());
363 if (verbose) {
364 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
365 cmdp = cmdlookup(name, 0);
366 if (cmdp != NULL)
367 printentry(cmdp, verbose);
368 }
369 flushall();
370 }
371 argptr++;
372 }
373 return 0;
374 }
375
376
377 STATIC void
378 printentry(struct tblentry *cmdp, int verbose)
379 {
380 int idx;
381 const char *path;
382 char *name;
383
384 switch (cmdp->cmdtype) {
385 case CMDNORMAL:
386 idx = cmdp->param.index;
387 path = pathval();
388 do {
389 name = padvance(&path, cmdp->cmdname);
390 stunalloc(name);
391 } while (--idx >= 0);
392 out1str(name);
393 break;
394 case CMDSPLBLTIN:
395 out1fmt("special builtin %s", cmdp->cmdname);
396 break;
397 case CMDBUILTIN:
398 out1fmt("builtin %s", cmdp->cmdname);
399 break;
400 case CMDFUNCTION:
401 out1fmt("function %s", cmdp->cmdname);
402 if (verbose) {
403 struct procstat ps;
404 INTOFF;
405 commandtext(&ps, cmdp->param.func);
406 INTON;
407 out1str("() { ");
408 out1str(ps.cmd);
409 out1str("; }");
410 }
411 break;
412 default:
413 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
414 }
415 if (cmdp->rehash)
416 out1c('*');
417 out1c('\n');
418 }
419
420
421
422 /*
423 * Resolve a command name. If you change this routine, you may have to
424 * change the shellexec routine as well.
425 */
426
427 void
428 find_command(char *name, struct cmdentry *entry, int act, const char *path)
429 {
430 struct tblentry *cmdp, loc_cmd;
431 int idx;
432 int prev;
433 char *fullname;
434 struct stat statb;
435 int e;
436 int (*bltin)(int,char **);
437
438 /* If name contains a slash, don't use PATH or hash table */
439 if (strchr(name, '/') != NULL) {
440 if (act & DO_ABS) {
441 while (stat(name, &statb) < 0) {
442 #ifdef SYSV
443 if (errno == EINTR)
444 continue;
445 #endif
446 if (errno != ENOENT && errno != ENOTDIR)
447 e = errno;
448 entry->cmdtype = CMDUNKNOWN;
449 entry->u.index = -1;
450 return;
451 }
452 entry->cmdtype = CMDNORMAL;
453 entry->u.index = -1;
454 return;
455 }
456 entry->cmdtype = CMDNORMAL;
457 entry->u.index = 0;
458 return;
459 }
460
461 if (path != pathval())
462 act |= DO_ALTPATH;
463
464 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
465 act |= DO_ALTBLTIN;
466
467 /* If name is in the table, check answer will be ok */
468 if ((cmdp = cmdlookup(name, 0)) != NULL) {
469 do {
470 switch (cmdp->cmdtype) {
471 case CMDNORMAL:
472 if (act & DO_ALTPATH) {
473 cmdp = NULL;
474 continue;
475 }
476 break;
477 case CMDFUNCTION:
478 if (act & DO_NOFUNC) {
479 cmdp = NULL;
480 continue;
481 }
482 break;
483 case CMDBUILTIN:
484 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
485 cmdp = NULL;
486 continue;
487 }
488 break;
489 }
490 /* if not invalidated by cd, we're done */
491 if (cmdp->rehash == 0)
492 goto success;
493 } while (0);
494 }
495
496 /* If %builtin not in path, check for builtin next */
497 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
498 (bltin = find_builtin(name)) != 0)
499 goto builtin_success;
500
501 /* We have to search path. */
502 prev = -1; /* where to start */
503 if (cmdp) { /* doing a rehash */
504 if (cmdp->cmdtype == CMDBUILTIN)
505 prev = builtinloc;
506 else
507 prev = cmdp->param.index;
508 }
509
510 e = ENOENT;
511 idx = -1;
512 loop:
513 while ((fullname = padvance(&path, name)) != NULL) {
514 stunalloc(fullname);
515 idx++;
516 if (pathopt) {
517 if (prefix("builtin", pathopt)) {
518 if ((bltin = find_builtin(name)) == 0)
519 goto loop;
520 goto builtin_success;
521 } else if (prefix("func", pathopt)) {
522 /* handled below */
523 } else {
524 /* ignore unimplemented options */
525 goto loop;
526 }
527 }
528 /* if rehash, don't redo absolute path names */
529 if (fullname[0] == '/' && idx <= prev) {
530 if (idx < prev)
531 goto loop;
532 TRACE(("searchexec \"%s\": no change\n", name));
533 goto success;
534 }
535 while (stat(fullname, &statb) < 0) {
536 #ifdef SYSV
537 if (errno == EINTR)
538 continue;
539 #endif
540 if (errno != ENOENT && errno != ENOTDIR)
541 e = errno;
542 goto loop;
543 }
544 e = EACCES; /* if we fail, this will be the error */
545 if (!S_ISREG(statb.st_mode))
546 goto loop;
547 if (pathopt) { /* this is a %func directory */
548 if (act & DO_NOFUNC)
549 goto loop;
550 stalloc(strlen(fullname) + 1);
551 readcmdfile(fullname);
552 if ((cmdp = cmdlookup(name, 0)) == NULL ||
553 cmdp->cmdtype != CMDFUNCTION)
554 error("%s not defined in %s", name, fullname);
555 stunalloc(fullname);
556 goto success;
557 }
558 #ifdef notdef
559 /* XXX this code stops root executing stuff, and is buggy
560 if you need a group from the group list. */
561 if (statb.st_uid == geteuid()) {
562 if ((statb.st_mode & 0100) == 0)
563 goto loop;
564 } else if (statb.st_gid == getegid()) {
565 if ((statb.st_mode & 010) == 0)
566 goto loop;
567 } else {
568 if ((statb.st_mode & 01) == 0)
569 goto loop;
570 }
571 #endif
572 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
573 INTOFF;
574 if (act & DO_ALTPATH) {
575 stalloc(strlen(fullname) + 1);
576 cmdp = &loc_cmd;
577 } else
578 cmdp = cmdlookup(name, 1);
579 cmdp->cmdtype = CMDNORMAL;
580 cmdp->param.index = idx;
581 INTON;
582 goto success;
583 }
584
585 /* We failed. If there was an entry for this command, delete it */
586 if (cmdp)
587 delete_cmd_entry();
588 if (act & DO_ERR)
589 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
590 entry->cmdtype = CMDUNKNOWN;
591 return;
592
593 builtin_success:
594 INTOFF;
595 if (act & DO_ALTPATH)
596 cmdp = &loc_cmd;
597 else
598 cmdp = cmdlookup(name, 1);
599 if (cmdp->cmdtype == CMDFUNCTION)
600 /* DO_NOFUNC must have been set */
601 cmdp = &loc_cmd;
602 cmdp->cmdtype = CMDBUILTIN;
603 cmdp->param.bltin = bltin;
604 INTON;
605 success:
606 if (cmdp) {
607 cmdp->rehash = 0;
608 entry->cmdtype = cmdp->cmdtype;
609 entry->u = cmdp->param;
610 } else
611 entry->cmdtype = CMDUNKNOWN;
612 }
613
614
615
616 /*
617 * Search the table of builtin commands.
618 */
619
620 int
621 (*find_builtin(name))(int, char **)
622 char *name;
623 {
624 const struct builtincmd *bp;
625
626 for (bp = builtincmd ; bp->name ; bp++) {
627 if (*bp->name == *name && equal(bp->name, name))
628 return bp->builtin;
629 }
630 return 0;
631 }
632
633 int
634 (*find_splbltin(name))(int, char **)
635 char *name;
636 {
637 const struct builtincmd *bp;
638
639 for (bp = splbltincmd ; bp->name ; bp++) {
640 if (*bp->name == *name && equal(bp->name, name))
641 return bp->builtin;
642 }
643 return 0;
644 }
645
646 /*
647 * At shell startup put special builtins into hash table.
648 * ensures they are executed first (see posix).
649 * We stop functions being added with the same name
650 * (as they are impossible to call)
651 */
652
653 void
654 hash_special_builtins(void)
655 {
656 const struct builtincmd *bp;
657 struct tblentry *cmdp;
658
659 for (bp = splbltincmd ; bp->name ; bp++) {
660 cmdp = cmdlookup(bp->name, 1);
661 cmdp->cmdtype = CMDSPLBLTIN;
662 cmdp->param.bltin = bp->builtin;
663 }
664 }
665
666
667
668 /*
669 * Called when a cd is done. Marks all commands so the next time they
670 * are executed they will be rehashed.
671 */
672
673 void
674 hashcd(void)
675 {
676 struct tblentry **pp;
677 struct tblentry *cmdp;
678
679 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
680 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
681 if (cmdp->cmdtype == CMDNORMAL
682 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
683 cmdp->rehash = 1;
684 }
685 }
686 }
687
688
689
690 /*
691 * Fix command hash table when PATH changed.
692 * Called before PATH is changed. The argument is the new value of PATH;
693 * pathval() still returns the old value at this point.
694 * Called with interrupts off.
695 */
696
697 void
698 changepath(const char *newval)
699 {
700 const char *old, *new;
701 int idx;
702 int firstchange;
703 int bltin;
704
705 old = pathval();
706 new = newval;
707 firstchange = 9999; /* assume no change */
708 idx = 0;
709 bltin = -1;
710 for (;;) {
711 if (*old != *new) {
712 firstchange = idx;
713 if ((*old == '\0' && *new == ':')
714 || (*old == ':' && *new == '\0'))
715 firstchange++;
716 old = new; /* ignore subsequent differences */
717 }
718 if (*new == '\0')
719 break;
720 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
721 bltin = idx;
722 if (*new == ':') {
723 idx++;
724 }
725 new++, old++;
726 }
727 if (builtinloc < 0 && bltin >= 0)
728 builtinloc = bltin; /* zap builtins */
729 if (builtinloc >= 0 && bltin < 0)
730 firstchange = 0;
731 clearcmdentry(firstchange);
732 builtinloc = bltin;
733 }
734
735
736 /*
737 * Clear out command entries. The argument specifies the first entry in
738 * PATH which has changed.
739 */
740
741 STATIC void
742 clearcmdentry(int firstchange)
743 {
744 struct tblentry **tblp;
745 struct tblentry **pp;
746 struct tblentry *cmdp;
747
748 INTOFF;
749 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
750 pp = tblp;
751 while ((cmdp = *pp) != NULL) {
752 if ((cmdp->cmdtype == CMDNORMAL &&
753 cmdp->param.index >= firstchange)
754 || (cmdp->cmdtype == CMDBUILTIN &&
755 builtinloc >= firstchange)) {
756 *pp = cmdp->next;
757 ckfree(cmdp);
758 } else {
759 pp = &cmdp->next;
760 }
761 }
762 }
763 INTON;
764 }
765
766
767 /*
768 * Delete all functions.
769 */
770
771 #ifdef mkinit
772 MKINIT void deletefuncs(void);
773 MKINIT void hash_special_builtins(void);
774
775 INIT {
776 hash_special_builtins();
777 }
778
779 SHELLPROC {
780 deletefuncs();
781 }
782 #endif
783
784 void
785 deletefuncs(void)
786 {
787 struct tblentry **tblp;
788 struct tblentry **pp;
789 struct tblentry *cmdp;
790
791 INTOFF;
792 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
793 pp = tblp;
794 while ((cmdp = *pp) != NULL) {
795 if (cmdp->cmdtype == CMDFUNCTION) {
796 *pp = cmdp->next;
797 freefunc(cmdp->param.func);
798 ckfree(cmdp);
799 } else {
800 pp = &cmdp->next;
801 }
802 }
803 }
804 INTON;
805 }
806
807
808
809 /*
810 * Locate a command in the command hash table. If "add" is nonzero,
811 * add the command to the table if it is not already present. The
812 * variable "lastcmdentry" is set to point to the address of the link
813 * pointing to the entry, so that delete_cmd_entry can delete the
814 * entry.
815 */
816
817 struct tblentry **lastcmdentry;
818
819
820 STATIC struct tblentry *
821 cmdlookup(const char *name, int add)
822 {
823 int hashval;
824 const char *p;
825 struct tblentry *cmdp;
826 struct tblentry **pp;
827
828 p = name;
829 hashval = *p << 4;
830 while (*p)
831 hashval += *p++;
832 hashval &= 0x7FFF;
833 pp = &cmdtable[hashval % CMDTABLESIZE];
834 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
835 if (equal(cmdp->cmdname, name))
836 break;
837 pp = &cmdp->next;
838 }
839 if (add && cmdp == NULL) {
840 INTOFF;
841 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
842 + strlen(name) + 1);
843 cmdp->next = NULL;
844 cmdp->cmdtype = CMDUNKNOWN;
845 cmdp->rehash = 0;
846 strcpy(cmdp->cmdname, name);
847 INTON;
848 }
849 lastcmdentry = pp;
850 return cmdp;
851 }
852
853 /*
854 * Delete the command entry returned on the last lookup.
855 */
856
857 STATIC void
858 delete_cmd_entry(void)
859 {
860 struct tblentry *cmdp;
861
862 INTOFF;
863 cmdp = *lastcmdentry;
864 *lastcmdentry = cmdp->next;
865 ckfree(cmdp);
866 INTON;
867 }
868
869
870
871 #ifdef notdef
872 void
873 getcmdentry(char *name, struct cmdentry *entry)
874 {
875 struct tblentry *cmdp = cmdlookup(name, 0);
876
877 if (cmdp) {
878 entry->u = cmdp->param;
879 entry->cmdtype = cmdp->cmdtype;
880 } else {
881 entry->cmdtype = CMDUNKNOWN;
882 entry->u.index = 0;
883 }
884 }
885 #endif
886
887
888 /*
889 * Add a new command entry, replacing any existing command entry for
890 * the same name - except special builtins.
891 */
892
893 STATIC void
894 addcmdentry(char *name, struct cmdentry *entry)
895 {
896 struct tblentry *cmdp;
897
898 INTOFF;
899 cmdp = cmdlookup(name, 1);
900 if (cmdp->cmdtype != CMDSPLBLTIN) {
901 if (cmdp->cmdtype == CMDFUNCTION) {
902 freefunc(cmdp->param.func);
903 }
904 cmdp->cmdtype = entry->cmdtype;
905 cmdp->param = entry->u;
906 }
907 INTON;
908 }
909
910
911 /*
912 * Define a shell function.
913 */
914
915 void
916 defun(char *name, union node *func)
917 {
918 struct cmdentry entry;
919
920 INTOFF;
921 entry.cmdtype = CMDFUNCTION;
922 entry.u.func = copyfunc(func);
923 addcmdentry(name, &entry);
924 INTON;
925 }
926
927
928 /*
929 * Delete a function if it exists.
930 */
931
932 int
933 unsetfunc(char *name)
934 {
935 struct tblentry *cmdp;
936
937 if ((cmdp = cmdlookup(name, 0)) != NULL &&
938 cmdp->cmdtype == CMDFUNCTION) {
939 freefunc(cmdp->param.func);
940 delete_cmd_entry();
941 return (0);
942 }
943 return (1);
944 }
945
946 /*
947 * Locate and print what a word is...
948 * also used for 'command -[v|V]'
949 */
950
951 int
952 typecmd(int argc, char **argv)
953 {
954 struct cmdentry entry;
955 struct tblentry *cmdp;
956 char * const *pp;
957 struct alias *ap;
958 int err = 0;
959 char *arg;
960 int c;
961 int V_flag = 0;
962 int v_flag = 0;
963 int p_flag = 0;
964
965 while ((c = nextopt("vVp")) != 0) {
966 switch (c) {
967 case 'v': v_flag = 1; break;
968 case 'V': V_flag = 1; break;
969 case 'p': p_flag = 1; break;
970 }
971 }
972
973 if (p_flag && (v_flag || V_flag))
974 error("cannot specify -p with -v or -V");
975
976 while ((arg = *argptr++)) {
977 if (!v_flag)
978 out1str(arg);
979 /* First look at the keywords */
980 for (pp = parsekwd; *pp; pp++)
981 if (**pp == *arg && equal(*pp, arg))
982 break;
983
984 if (*pp) {
985 if (v_flag)
986 err = 1;
987 else
988 out1str(" is a shell keyword\n");
989 continue;
990 }
991
992 /* Then look at the aliases */
993 if ((ap = lookupalias(arg, 1)) != NULL) {
994 if (!v_flag)
995 out1fmt(" is an alias for \n");
996 out1fmt("%s\n", ap->val);
997 continue;
998 }
999
1000 /* Then check if it is a tracked alias */
1001 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
1002 entry.cmdtype = cmdp->cmdtype;
1003 entry.u = cmdp->param;
1004 } else {
1005 /* Finally use brute force */
1006 find_command(arg, &entry, DO_ABS, pathval());
1007 }
1008
1009 switch (entry.cmdtype) {
1010 case CMDNORMAL: {
1011 if (strchr(arg, '/') == NULL) {
1012 const char *path = pathval();
1013 char *name;
1014 int j = entry.u.index;
1015 do {
1016 name = padvance(&path, arg);
1017 stunalloc(name);
1018 } while (--j >= 0);
1019 if (!v_flag)
1020 out1fmt(" is%s ",
1021 cmdp ? " a tracked alias for" : "");
1022 out1fmt("%s\n", name);
1023 } else {
1024 if (access(arg, X_OK) == 0) {
1025 if (!v_flag)
1026 out1fmt(" is ");
1027 out1fmt("%s\n", arg);
1028 } else {
1029 if (!v_flag)
1030 out1fmt(": %s\n",
1031 strerror(errno));
1032 else
1033 err = 126;
1034 }
1035 }
1036 break;
1037 }
1038 case CMDFUNCTION:
1039 if (!v_flag)
1040 out1str(" is a shell function\n");
1041 else
1042 out1fmt("%s\n", arg);
1043 break;
1044
1045 case CMDBUILTIN:
1046 if (!v_flag)
1047 out1str(" is a shell builtin\n");
1048 else
1049 out1fmt("%s\n", arg);
1050 break;
1051
1052 case CMDSPLBLTIN:
1053 if (!v_flag)
1054 out1str(" is a special shell builtin\n");
1055 else
1056 out1fmt("%s\n", arg);
1057 break;
1058
1059 default:
1060 if (!v_flag)
1061 out1str(": not found\n");
1062 err = 127;
1063 break;
1064 }
1065 }
1066 return err;
1067 }
1068