exec.c revision 1.36 1 /* $NetBSD: exec.c,v 1.36 2003/02/04 08:51:30 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.36 2003/02/04 08:51:30 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 if (verbose || cmdp->cmdtype == CMDNORMAL)
356 printentry(cmdp, verbose);
357 }
358 }
359 return 0;
360 }
361 while ((name = *argptr) != NULL) {
362 if ((cmdp = cmdlookup(name, 0)) != NULL
363 && (cmdp->cmdtype == CMDNORMAL
364 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
365 delete_cmd_entry();
366 find_command(name, &entry, DO_ERR, pathval());
367 if (verbose) {
368 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
369 cmdp = cmdlookup(name, 0);
370 printentry(cmdp, verbose);
371 }
372 flushall();
373 }
374 argptr++;
375 }
376 return 0;
377 }
378
379
380 STATIC void
381 printentry(struct tblentry *cmdp, int verbose)
382 {
383 int idx;
384 const char *path;
385 char *name;
386
387 switch (cmdp->cmdtype) {
388 case CMDNORMAL:
389 idx = cmdp->param.index;
390 path = pathval();
391 do {
392 name = padvance(&path, cmdp->cmdname);
393 stunalloc(name);
394 } while (--idx >= 0);
395 out1str(name);
396 break;
397 case CMDSPLBLTIN:
398 out1fmt("special builtin %s", cmdp->cmdname);
399 break;
400 case CMDBUILTIN:
401 out1fmt("builtin %s", cmdp->cmdname);
402 break;
403 case CMDFUNCTION:
404 out1fmt("function %s", cmdp->cmdname);
405 if (verbose) {
406 struct procstat ps;
407 INTOFF;
408 commandtext(&ps, cmdp->param.func);
409 INTON;
410 out1str("() { ");
411 out1str(ps.cmd);
412 out1str("; }");
413 }
414 break;
415 default:
416 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
417 }
418 if (cmdp->rehash)
419 out1c('*');
420 out1c('\n');
421 }
422
423
424
425 /*
426 * Resolve a command name. If you change this routine, you may have to
427 * change the shellexec routine as well.
428 */
429
430 void
431 find_command(char *name, struct cmdentry *entry, int act, const char *path)
432 {
433 struct tblentry *cmdp, loc_cmd;
434 int idx;
435 int prev;
436 char *fullname;
437 struct stat statb;
438 int e;
439 int (*bltin)(int,char **);
440
441 /* If name contains a slash, don't use PATH or hash table */
442 if (strchr(name, '/') != NULL) {
443 if (act & DO_ABS) {
444 while (stat(name, &statb) < 0) {
445 #ifdef SYSV
446 if (errno == EINTR)
447 continue;
448 #endif
449 if (errno != ENOENT && errno != ENOTDIR)
450 e = errno;
451 entry->cmdtype = CMDUNKNOWN;
452 entry->u.index = -1;
453 return;
454 }
455 entry->cmdtype = CMDNORMAL;
456 entry->u.index = -1;
457 return;
458 }
459 entry->cmdtype = CMDNORMAL;
460 entry->u.index = 0;
461 return;
462 }
463
464 if (path != pathval())
465 act |= DO_ALTPATH;
466
467 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
468 act |= DO_ALTBLTIN;
469
470 /* If name is in the table, check answer will be ok */
471 if ((cmdp = cmdlookup(name, 0)) != NULL) {
472 do {
473 switch (cmdp->cmdtype) {
474 case CMDNORMAL:
475 if (act & DO_ALTPATH) {
476 cmdp = NULL;
477 continue;
478 }
479 break;
480 case CMDFUNCTION:
481 if (act & DO_NOFUNC) {
482 cmdp = NULL;
483 continue;
484 }
485 break;
486 case CMDBUILTIN:
487 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
488 cmdp = NULL;
489 continue;
490 }
491 break;
492 }
493 /* if not invalidated by cd, we're done */
494 if (cmdp->rehash == 0)
495 goto success;
496 } while (0);
497 }
498
499 /* If %builtin not in path, check for builtin next */
500 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
501 (bltin = find_builtin(name)) != 0)
502 goto builtin_success;
503
504 /* We have to search path. */
505 prev = -1; /* where to start */
506 if (cmdp) { /* doing a rehash */
507 if (cmdp->cmdtype == CMDBUILTIN)
508 prev = builtinloc;
509 else
510 prev = cmdp->param.index;
511 }
512
513 e = ENOENT;
514 idx = -1;
515 loop:
516 while ((fullname = padvance(&path, name)) != NULL) {
517 stunalloc(fullname);
518 idx++;
519 if (pathopt) {
520 if (prefix("builtin", pathopt)) {
521 if ((bltin = find_builtin(name)) == 0)
522 goto loop;
523 goto builtin_success;
524 } else if (prefix("func", pathopt)) {
525 /* handled below */
526 } else {
527 /* ignore unimplemented options */
528 goto loop;
529 }
530 }
531 /* if rehash, don't redo absolute path names */
532 if (fullname[0] == '/' && idx <= prev) {
533 if (idx < prev)
534 goto loop;
535 TRACE(("searchexec \"%s\": no change\n", name));
536 goto success;
537 }
538 while (stat(fullname, &statb) < 0) {
539 #ifdef SYSV
540 if (errno == EINTR)
541 continue;
542 #endif
543 if (errno != ENOENT && errno != ENOTDIR)
544 e = errno;
545 goto loop;
546 }
547 e = EACCES; /* if we fail, this will be the error */
548 if (!S_ISREG(statb.st_mode))
549 goto loop;
550 if (pathopt) { /* this is a %func directory */
551 if (act & DO_NOFUNC)
552 goto loop;
553 stalloc(strlen(fullname) + 1);
554 readcmdfile(fullname);
555 if ((cmdp = cmdlookup(name, 0)) == NULL ||
556 cmdp->cmdtype != CMDFUNCTION)
557 error("%s not defined in %s", name, fullname);
558 stunalloc(fullname);
559 goto success;
560 }
561 #ifdef notdef
562 /* XXX this code stops root executing stuff, and is buggy
563 if you need a group from the group list. */
564 if (statb.st_uid == geteuid()) {
565 if ((statb.st_mode & 0100) == 0)
566 goto loop;
567 } else if (statb.st_gid == getegid()) {
568 if ((statb.st_mode & 010) == 0)
569 goto loop;
570 } else {
571 if ((statb.st_mode & 01) == 0)
572 goto loop;
573 }
574 #endif
575 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
576 INTOFF;
577 if (act & DO_ALTPATH) {
578 stalloc(strlen(fullname) + 1);
579 cmdp = &loc_cmd;
580 } else
581 cmdp = cmdlookup(name, 1);
582 cmdp->cmdtype = CMDNORMAL;
583 cmdp->param.index = idx;
584 INTON;
585 goto success;
586 }
587
588 /* We failed. If there was an entry for this command, delete it */
589 if (cmdp)
590 delete_cmd_entry();
591 if (act & DO_ERR)
592 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
593 entry->cmdtype = CMDUNKNOWN;
594 return;
595
596 builtin_success:
597 INTOFF;
598 if (act & DO_ALTPATH)
599 cmdp = &loc_cmd;
600 else
601 cmdp = cmdlookup(name, 1);
602 if (cmdp->cmdtype == CMDFUNCTION)
603 /* DO_NOFUNC must have been set */
604 cmdp = &loc_cmd;
605 cmdp->cmdtype = CMDBUILTIN;
606 cmdp->param.bltin = bltin;
607 INTON;
608 success:
609 cmdp->rehash = 0;
610 entry->cmdtype = cmdp->cmdtype;
611 entry->u = cmdp->param;
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