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