exec.c revision 1.38 1 /* $NetBSD: exec.c,v 1.38 2006/03/18 05:17:36 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.38 2006/03/18 05:17:36 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 cmdp->rehash = 0;
607 entry->cmdtype = cmdp->cmdtype;
608 entry->u = cmdp->param;
609 }
610
611
612
613 /*
614 * Search the table of builtin commands.
615 */
616
617 int
618 (*find_builtin(name))(int, char **)
619 char *name;
620 {
621 const struct builtincmd *bp;
622
623 for (bp = builtincmd ; bp->name ; bp++) {
624 if (*bp->name == *name && equal(bp->name, name))
625 return bp->builtin;
626 }
627 return 0;
628 }
629
630 int
631 (*find_splbltin(name))(int, char **)
632 char *name;
633 {
634 const struct builtincmd *bp;
635
636 for (bp = splbltincmd ; bp->name ; bp++) {
637 if (*bp->name == *name && equal(bp->name, name))
638 return bp->builtin;
639 }
640 return 0;
641 }
642
643 /*
644 * At shell startup put special builtins into hash table.
645 * ensures they are executed first (see posix).
646 * We stop functions being added with the same name
647 * (as they are impossible to call)
648 */
649
650 void
651 hash_special_builtins(void)
652 {
653 const struct builtincmd *bp;
654 struct tblentry *cmdp;
655
656 for (bp = splbltincmd ; bp->name ; bp++) {
657 cmdp = cmdlookup(bp->name, 1);
658 cmdp->cmdtype = CMDSPLBLTIN;
659 cmdp->param.bltin = bp->builtin;
660 }
661 }
662
663
664
665 /*
666 * Called when a cd is done. Marks all commands so the next time they
667 * are executed they will be rehashed.
668 */
669
670 void
671 hashcd(void)
672 {
673 struct tblentry **pp;
674 struct tblentry *cmdp;
675
676 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
677 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
678 if (cmdp->cmdtype == CMDNORMAL
679 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
680 cmdp->rehash = 1;
681 }
682 }
683 }
684
685
686
687 /*
688 * Fix command hash table when PATH changed.
689 * Called before PATH is changed. The argument is the new value of PATH;
690 * pathval() still returns the old value at this point.
691 * Called with interrupts off.
692 */
693
694 void
695 changepath(const char *newval)
696 {
697 const char *old, *new;
698 int idx;
699 int firstchange;
700 int bltin;
701
702 old = pathval();
703 new = newval;
704 firstchange = 9999; /* assume no change */
705 idx = 0;
706 bltin = -1;
707 for (;;) {
708 if (*old != *new) {
709 firstchange = idx;
710 if ((*old == '\0' && *new == ':')
711 || (*old == ':' && *new == '\0'))
712 firstchange++;
713 old = new; /* ignore subsequent differences */
714 }
715 if (*new == '\0')
716 break;
717 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
718 bltin = idx;
719 if (*new == ':') {
720 idx++;
721 }
722 new++, old++;
723 }
724 if (builtinloc < 0 && bltin >= 0)
725 builtinloc = bltin; /* zap builtins */
726 if (builtinloc >= 0 && bltin < 0)
727 firstchange = 0;
728 clearcmdentry(firstchange);
729 builtinloc = bltin;
730 }
731
732
733 /*
734 * Clear out command entries. The argument specifies the first entry in
735 * PATH which has changed.
736 */
737
738 STATIC void
739 clearcmdentry(int firstchange)
740 {
741 struct tblentry **tblp;
742 struct tblentry **pp;
743 struct tblentry *cmdp;
744
745 INTOFF;
746 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
747 pp = tblp;
748 while ((cmdp = *pp) != NULL) {
749 if ((cmdp->cmdtype == CMDNORMAL &&
750 cmdp->param.index >= firstchange)
751 || (cmdp->cmdtype == CMDBUILTIN &&
752 builtinloc >= firstchange)) {
753 *pp = cmdp->next;
754 ckfree(cmdp);
755 } else {
756 pp = &cmdp->next;
757 }
758 }
759 }
760 INTON;
761 }
762
763
764 /*
765 * Delete all functions.
766 */
767
768 #ifdef mkinit
769 MKINIT void deletefuncs(void);
770 MKINIT void hash_special_builtins(void);
771
772 INIT {
773 hash_special_builtins();
774 }
775
776 SHELLPROC {
777 deletefuncs();
778 }
779 #endif
780
781 void
782 deletefuncs(void)
783 {
784 struct tblentry **tblp;
785 struct tblentry **pp;
786 struct tblentry *cmdp;
787
788 INTOFF;
789 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
790 pp = tblp;
791 while ((cmdp = *pp) != NULL) {
792 if (cmdp->cmdtype == CMDFUNCTION) {
793 *pp = cmdp->next;
794 freefunc(cmdp->param.func);
795 ckfree(cmdp);
796 } else {
797 pp = &cmdp->next;
798 }
799 }
800 }
801 INTON;
802 }
803
804
805
806 /*
807 * Locate a command in the command hash table. If "add" is nonzero,
808 * add the command to the table if it is not already present. The
809 * variable "lastcmdentry" is set to point to the address of the link
810 * pointing to the entry, so that delete_cmd_entry can delete the
811 * entry.
812 */
813
814 struct tblentry **lastcmdentry;
815
816
817 STATIC struct tblentry *
818 cmdlookup(const char *name, int add)
819 {
820 int hashval;
821 const char *p;
822 struct tblentry *cmdp;
823 struct tblentry **pp;
824
825 p = name;
826 hashval = *p << 4;
827 while (*p)
828 hashval += *p++;
829 hashval &= 0x7FFF;
830 pp = &cmdtable[hashval % CMDTABLESIZE];
831 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
832 if (equal(cmdp->cmdname, name))
833 break;
834 pp = &cmdp->next;
835 }
836 if (add && cmdp == NULL) {
837 INTOFF;
838 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
839 + strlen(name) + 1);
840 cmdp->next = NULL;
841 cmdp->cmdtype = CMDUNKNOWN;
842 cmdp->rehash = 0;
843 strcpy(cmdp->cmdname, name);
844 INTON;
845 }
846 lastcmdentry = pp;
847 return cmdp;
848 }
849
850 /*
851 * Delete the command entry returned on the last lookup.
852 */
853
854 STATIC void
855 delete_cmd_entry(void)
856 {
857 struct tblentry *cmdp;
858
859 INTOFF;
860 cmdp = *lastcmdentry;
861 *lastcmdentry = cmdp->next;
862 ckfree(cmdp);
863 INTON;
864 }
865
866
867
868 #ifdef notdef
869 void
870 getcmdentry(char *name, struct cmdentry *entry)
871 {
872 struct tblentry *cmdp = cmdlookup(name, 0);
873
874 if (cmdp) {
875 entry->u = cmdp->param;
876 entry->cmdtype = cmdp->cmdtype;
877 } else {
878 entry->cmdtype = CMDUNKNOWN;
879 entry->u.index = 0;
880 }
881 }
882 #endif
883
884
885 /*
886 * Add a new command entry, replacing any existing command entry for
887 * the same name - except special builtins.
888 */
889
890 STATIC void
891 addcmdentry(char *name, struct cmdentry *entry)
892 {
893 struct tblentry *cmdp;
894
895 INTOFF;
896 cmdp = cmdlookup(name, 1);
897 if (cmdp->cmdtype != CMDSPLBLTIN) {
898 if (cmdp->cmdtype == CMDFUNCTION) {
899 freefunc(cmdp->param.func);
900 }
901 cmdp->cmdtype = entry->cmdtype;
902 cmdp->param = entry->u;
903 }
904 INTON;
905 }
906
907
908 /*
909 * Define a shell function.
910 */
911
912 void
913 defun(char *name, union node *func)
914 {
915 struct cmdentry entry;
916
917 INTOFF;
918 entry.cmdtype = CMDFUNCTION;
919 entry.u.func = copyfunc(func);
920 addcmdentry(name, &entry);
921 INTON;
922 }
923
924
925 /*
926 * Delete a function if it exists.
927 */
928
929 int
930 unsetfunc(char *name)
931 {
932 struct tblentry *cmdp;
933
934 if ((cmdp = cmdlookup(name, 0)) != NULL &&
935 cmdp->cmdtype == CMDFUNCTION) {
936 freefunc(cmdp->param.func);
937 delete_cmd_entry();
938 return (0);
939 }
940 return (1);
941 }
942
943 /*
944 * Locate and print what a word is...
945 * also used for 'command -[v|V]'
946 */
947
948 int
949 typecmd(int argc, char **argv)
950 {
951 struct cmdentry entry;
952 struct tblentry *cmdp;
953 char * const *pp;
954 struct alias *ap;
955 int err = 0;
956 char *arg;
957 int c;
958 int V_flag = 0;
959 int v_flag = 0;
960 int p_flag = 0;
961
962 while ((c = nextopt("vVp")) != 0) {
963 switch (c) {
964 case 'v': v_flag = 1; break;
965 case 'V': V_flag = 1; break;
966 case 'p': p_flag = 1; break;
967 }
968 }
969
970 if (p_flag && (v_flag || V_flag))
971 error("cannot specify -p with -v or -V");
972
973 while ((arg = *argptr++)) {
974 if (!v_flag)
975 out1str(arg);
976 /* First look at the keywords */
977 for (pp = parsekwd; *pp; pp++)
978 if (**pp == *arg && equal(*pp, arg))
979 break;
980
981 if (*pp) {
982 if (v_flag)
983 err = 1;
984 else
985 out1str(" is a shell keyword\n");
986 continue;
987 }
988
989 /* Then look at the aliases */
990 if ((ap = lookupalias(arg, 1)) != NULL) {
991 if (!v_flag)
992 out1fmt(" is an alias for \n");
993 out1fmt("%s\n", ap->val);
994 continue;
995 }
996
997 /* Then check if it is a tracked alias */
998 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
999 entry.cmdtype = cmdp->cmdtype;
1000 entry.u = cmdp->param;
1001 } else {
1002 /* Finally use brute force */
1003 find_command(arg, &entry, DO_ABS, pathval());
1004 }
1005
1006 switch (entry.cmdtype) {
1007 case CMDNORMAL: {
1008 if (strchr(arg, '/') == NULL) {
1009 const char *path = pathval();
1010 char *name;
1011 int j = entry.u.index;
1012 do {
1013 name = padvance(&path, arg);
1014 stunalloc(name);
1015 } while (--j >= 0);
1016 if (!v_flag)
1017 out1fmt(" is%s ",
1018 cmdp ? " a tracked alias for" : "");
1019 out1fmt("%s\n", name);
1020 } else {
1021 if (access(arg, X_OK) == 0) {
1022 if (!v_flag)
1023 out1fmt(" is ");
1024 out1fmt("%s\n", arg);
1025 } else {
1026 if (!v_flag)
1027 out1fmt(": %s\n",
1028 strerror(errno));
1029 else
1030 err = 126;
1031 }
1032 }
1033 break;
1034 }
1035 case CMDFUNCTION:
1036 if (!v_flag)
1037 out1str(" is a shell function\n");
1038 else
1039 out1fmt("%s\n", arg);
1040 break;
1041
1042 case CMDBUILTIN:
1043 if (!v_flag)
1044 out1str(" is a shell builtin\n");
1045 else
1046 out1fmt("%s\n", arg);
1047 break;
1048
1049 case CMDSPLBLTIN:
1050 if (!v_flag)
1051 out1str(" is a special shell builtin\n");
1052 else
1053 out1fmt("%s\n", arg);
1054 break;
1055
1056 default:
1057 if (!v_flag)
1058 out1str(": not found\n");
1059 err = 127;
1060 break;
1061 }
1062 }
1063 return err;
1064 }
1065