1 /* $NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc 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.37 2003/08/07 09:05:31 agc 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
shellexec(char ** argv,char ** envp,const char * path,int idx,int vforked)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
tryexec(char * cmd,char ** argv,char ** envp,int vforked)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 #if !defined(BSD) && !defined(__linux__)
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 #if !defined(BSD) && !defined(__linux__)
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
execinterp(char ** argv,char ** envp)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 *
padvance(const char ** path,const char * name)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
hashcmd(int argc,char ** argv)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 printentry(cmdp, verbose);
367 }
368 flushall();
369 }
370 argptr++;
371 }
372 return 0;
373 }
374
375
376 STATIC void
printentry(struct tblentry * cmdp,int verbose)377 printentry(struct tblentry *cmdp, int verbose)
378 {
379 int idx;
380 const char *path;
381 char *name;
382
383 switch (cmdp->cmdtype) {
384 case CMDNORMAL:
385 idx = cmdp->param.index;
386 path = pathval();
387 do {
388 name = padvance(&path, cmdp->cmdname);
389 stunalloc(name);
390 } while (--idx >= 0);
391 out1str(name);
392 break;
393 case CMDSPLBLTIN:
394 out1fmt("special builtin %s", cmdp->cmdname);
395 break;
396 case CMDBUILTIN:
397 out1fmt("builtin %s", cmdp->cmdname);
398 break;
399 case CMDFUNCTION:
400 out1fmt("function %s", cmdp->cmdname);
401 if (verbose) {
402 struct procstat ps;
403 INTOFF;
404 commandtext(&ps, cmdp->param.func);
405 INTON;
406 out1str("() { ");
407 out1str(ps.cmd);
408 out1str("; }");
409 }
410 break;
411 default:
412 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
413 }
414 if (cmdp->rehash)
415 out1c('*');
416 out1c('\n');
417 }
418
419
420
421 /*
422 * Resolve a command name. If you change this routine, you may have to
423 * change the shellexec routine as well.
424 */
425
426 void
find_command(char * name,struct cmdentry * entry,int act,const char * path)427 find_command(char *name, struct cmdentry *entry, int act, const char *path)
428 {
429 struct tblentry *cmdp, loc_cmd;
430 int idx;
431 int prev;
432 char *fullname;
433 struct stat statb;
434 int e;
435 int (*bltin)(int,char **);
436
437 /* If name contains a slash, don't use PATH or hash table */
438 if (strchr(name, '/') != NULL) {
439 if (act & DO_ABS) {
440 while (stat(name, &statb) < 0) {
441 #ifdef SYSV
442 if (errno == EINTR)
443 continue;
444 #endif
445 if (errno != ENOENT && errno != ENOTDIR)
446 e = errno;
447 entry->cmdtype = CMDUNKNOWN;
448 entry->u.index = -1;
449 return;
450 }
451 entry->cmdtype = CMDNORMAL;
452 entry->u.index = -1;
453 return;
454 }
455 entry->cmdtype = CMDNORMAL;
456 entry->u.index = 0;
457 return;
458 }
459
460 if (path != pathval())
461 act |= DO_ALTPATH;
462
463 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
464 act |= DO_ALTBLTIN;
465
466 /* If name is in the table, check answer will be ok */
467 if ((cmdp = cmdlookup(name, 0)) != NULL) {
468 do {
469 switch (cmdp->cmdtype) {
470 case CMDNORMAL:
471 if (act & DO_ALTPATH) {
472 cmdp = NULL;
473 continue;
474 }
475 break;
476 case CMDFUNCTION:
477 if (act & DO_NOFUNC) {
478 cmdp = NULL;
479 continue;
480 }
481 break;
482 case CMDBUILTIN:
483 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
484 cmdp = NULL;
485 continue;
486 }
487 break;
488 }
489 /* if not invalidated by cd, we're done */
490 if (cmdp->rehash == 0)
491 goto success;
492 } while (0);
493 }
494
495 /* If %builtin not in path, check for builtin next */
496 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
497 (bltin = find_builtin(name)) != 0)
498 goto builtin_success;
499
500 /* We have to search path. */
501 prev = -1; /* where to start */
502 if (cmdp) { /* doing a rehash */
503 if (cmdp->cmdtype == CMDBUILTIN)
504 prev = builtinloc;
505 else
506 prev = cmdp->param.index;
507 }
508
509 e = ENOENT;
510 idx = -1;
511 loop:
512 while ((fullname = padvance(&path, name)) != NULL) {
513 stunalloc(fullname);
514 idx++;
515 if (pathopt) {
516 if (prefix("builtin", pathopt)) {
517 if ((bltin = find_builtin(name)) == 0)
518 goto loop;
519 goto builtin_success;
520 } else if (prefix("func", pathopt)) {
521 /* handled below */
522 } else {
523 /* ignore unimplemented options */
524 goto loop;
525 }
526 }
527 /* if rehash, don't redo absolute path names */
528 if (fullname[0] == '/' && idx <= prev) {
529 if (idx < prev)
530 goto loop;
531 TRACE(("searchexec \"%s\": no change\n", name));
532 goto success;
533 }
534 while (stat(fullname, &statb) < 0) {
535 #ifdef SYSV
536 if (errno == EINTR)
537 continue;
538 #endif
539 if (errno != ENOENT && errno != ENOTDIR)
540 e = errno;
541 goto loop;
542 }
543 e = EACCES; /* if we fail, this will be the error */
544 if (!S_ISREG(statb.st_mode))
545 goto loop;
546 if (pathopt) { /* this is a %func directory */
547 if (act & DO_NOFUNC)
548 goto loop;
549 stalloc(strlen(fullname) + 1);
550 readcmdfile(fullname);
551 if ((cmdp = cmdlookup(name, 0)) == NULL ||
552 cmdp->cmdtype != CMDFUNCTION)
553 error("%s not defined in %s", name, fullname);
554 stunalloc(fullname);
555 goto success;
556 }
557 #ifdef notdef
558 /* XXX this code stops root executing stuff, and is buggy
559 if you need a group from the group list. */
560 if (statb.st_uid == geteuid()) {
561 if ((statb.st_mode & 0100) == 0)
562 goto loop;
563 } else if (statb.st_gid == getegid()) {
564 if ((statb.st_mode & 010) == 0)
565 goto loop;
566 } else {
567 if ((statb.st_mode & 01) == 0)
568 goto loop;
569 }
570 #endif
571 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
572 INTOFF;
573 if (act & DO_ALTPATH) {
574 stalloc(strlen(fullname) + 1);
575 cmdp = &loc_cmd;
576 } else
577 cmdp = cmdlookup(name, 1);
578 cmdp->cmdtype = CMDNORMAL;
579 cmdp->param.index = idx;
580 INTON;
581 goto success;
582 }
583
584 /* We failed. If there was an entry for this command, delete it */
585 if (cmdp)
586 delete_cmd_entry();
587 if (act & DO_ERR)
588 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
589 entry->cmdtype = CMDUNKNOWN;
590 return;
591
592 builtin_success:
593 INTOFF;
594 if (act & DO_ALTPATH)
595 cmdp = &loc_cmd;
596 else
597 cmdp = cmdlookup(name, 1);
598 if (cmdp->cmdtype == CMDFUNCTION)
599 /* DO_NOFUNC must have been set */
600 cmdp = &loc_cmd;
601 cmdp->cmdtype = CMDBUILTIN;
602 cmdp->param.bltin = bltin;
603 INTON;
604 success:
605 cmdp->rehash = 0;
606 entry->cmdtype = cmdp->cmdtype;
607 entry->u = cmdp->param;
608 }
609
610
611
612 /*
613 * Search the table of builtin commands.
614 */
615
616 int
617 (*find_builtin(name))(int, char **)
618 char *name;
619 {
620 const struct builtincmd *bp;
621
622 for (bp = builtincmd ; bp->name ; bp++) {
623 if (*bp->name == *name && equal(bp->name, name))
624 return bp->builtin;
625 }
626 return 0;
627 }
628
629 int
630 (*find_splbltin(name))(int, char **)
631 char *name;
632 {
633 const struct builtincmd *bp;
634
635 for (bp = splbltincmd ; bp->name ; bp++) {
636 if (*bp->name == *name && equal(bp->name, name))
637 return bp->builtin;
638 }
639 return 0;
640 }
641
642 /*
643 * At shell startup put special builtins into hash table.
644 * ensures they are executed first (see posix).
645 * We stop functions being added with the same name
646 * (as they are impossible to call)
647 */
648
649 void
hash_special_builtins(void)650 hash_special_builtins(void)
651 {
652 const struct builtincmd *bp;
653 struct tblentry *cmdp;
654
655 for (bp = splbltincmd ; bp->name ; bp++) {
656 cmdp = cmdlookup(bp->name, 1);
657 cmdp->cmdtype = CMDSPLBLTIN;
658 cmdp->param.bltin = bp->builtin;
659 }
660 }
661
662
663
664 /*
665 * Called when a cd is done. Marks all commands so the next time they
666 * are executed they will be rehashed.
667 */
668
669 void
hashcd(void)670 hashcd(void)
671 {
672 struct tblentry **pp;
673 struct tblentry *cmdp;
674
675 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
676 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
677 if (cmdp->cmdtype == CMDNORMAL
678 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
679 cmdp->rehash = 1;
680 }
681 }
682 }
683
684
685
686 /*
687 * Fix command hash table when PATH changed.
688 * Called before PATH is changed. The argument is the new value of PATH;
689 * pathval() still returns the old value at this point.
690 * Called with interrupts off.
691 */
692
693 void
changepath(const char * newval)694 changepath(const char *newval)
695 {
696 const char *old, *new;
697 int idx;
698 int firstchange;
699 int bltin;
700
701 old = pathval();
702 new = newval;
703 firstchange = 9999; /* assume no change */
704 idx = 0;
705 bltin = -1;
706 for (;;) {
707 if (*old != *new) {
708 firstchange = idx;
709 if ((*old == '\0' && *new == ':')
710 || (*old == ':' && *new == '\0'))
711 firstchange++;
712 old = new; /* ignore subsequent differences */
713 }
714 if (*new == '\0')
715 break;
716 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
717 bltin = idx;
718 if (*new == ':') {
719 idx++;
720 }
721 new++, old++;
722 }
723 if (builtinloc < 0 && bltin >= 0)
724 builtinloc = bltin; /* zap builtins */
725 if (builtinloc >= 0 && bltin < 0)
726 firstchange = 0;
727 clearcmdentry(firstchange);
728 builtinloc = bltin;
729 }
730
731
732 /*
733 * Clear out command entries. The argument specifies the first entry in
734 * PATH which has changed.
735 */
736
737 STATIC void
clearcmdentry(int firstchange)738 clearcmdentry(int firstchange)
739 {
740 struct tblentry **tblp;
741 struct tblentry **pp;
742 struct tblentry *cmdp;
743
744 INTOFF;
745 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
746 pp = tblp;
747 while ((cmdp = *pp) != NULL) {
748 if ((cmdp->cmdtype == CMDNORMAL &&
749 cmdp->param.index >= firstchange)
750 || (cmdp->cmdtype == CMDBUILTIN &&
751 builtinloc >= firstchange)) {
752 *pp = cmdp->next;
753 ckfree(cmdp);
754 } else {
755 pp = &cmdp->next;
756 }
757 }
758 }
759 INTON;
760 }
761
762
763 /*
764 * Delete all functions.
765 */
766
767 #ifdef mkinit
768 MKINIT void deletefuncs(void);
769 MKINIT void hash_special_builtins(void);
770
771 INIT {
772 hash_special_builtins();
773 }
774
775 SHELLPROC {
776 deletefuncs();
777 }
778 #endif
779
780 void
deletefuncs(void)781 deletefuncs(void)
782 {
783 struct tblentry **tblp;
784 struct tblentry **pp;
785 struct tblentry *cmdp;
786
787 INTOFF;
788 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
789 pp = tblp;
790 while ((cmdp = *pp) != NULL) {
791 if (cmdp->cmdtype == CMDFUNCTION) {
792 *pp = cmdp->next;
793 freefunc(cmdp->param.func);
794 ckfree(cmdp);
795 } else {
796 pp = &cmdp->next;
797 }
798 }
799 }
800 INTON;
801 }
802
803
804
805 /*
806 * Locate a command in the command hash table. If "add" is nonzero,
807 * add the command to the table if it is not already present. The
808 * variable "lastcmdentry" is set to point to the address of the link
809 * pointing to the entry, so that delete_cmd_entry can delete the
810 * entry.
811 */
812
813 struct tblentry **lastcmdentry;
814
815
816 STATIC struct tblentry *
cmdlookup(const char * name,int add)817 cmdlookup(const char *name, int add)
818 {
819 int hashval;
820 const char *p;
821 struct tblentry *cmdp;
822 struct tblentry **pp;
823
824 p = name;
825 hashval = *p << 4;
826 while (*p)
827 hashval += *p++;
828 hashval &= 0x7FFF;
829 pp = &cmdtable[hashval % CMDTABLESIZE];
830 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
831 if (equal(cmdp->cmdname, name))
832 break;
833 pp = &cmdp->next;
834 }
835 if (add && cmdp == NULL) {
836 INTOFF;
837 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
838 + strlen(name) + 1);
839 cmdp->next = NULL;
840 cmdp->cmdtype = CMDUNKNOWN;
841 cmdp->rehash = 0;
842 strcpy(cmdp->cmdname, name);
843 INTON;
844 }
845 lastcmdentry = pp;
846 return cmdp;
847 }
848
849 /*
850 * Delete the command entry returned on the last lookup.
851 */
852
853 STATIC void
delete_cmd_entry(void)854 delete_cmd_entry(void)
855 {
856 struct tblentry *cmdp;
857
858 INTOFF;
859 cmdp = *lastcmdentry;
860 *lastcmdentry = cmdp->next;
861 ckfree(cmdp);
862 INTON;
863 }
864
865
866
867 #ifdef notdef
868 void
getcmdentry(char * name,struct cmdentry * entry)869 getcmdentry(char *name, struct cmdentry *entry)
870 {
871 struct tblentry *cmdp = cmdlookup(name, 0);
872
873 if (cmdp) {
874 entry->u = cmdp->param;
875 entry->cmdtype = cmdp->cmdtype;
876 } else {
877 entry->cmdtype = CMDUNKNOWN;
878 entry->u.index = 0;
879 }
880 }
881 #endif
882
883
884 /*
885 * Add a new command entry, replacing any existing command entry for
886 * the same name - except special builtins.
887 */
888
889 STATIC void
addcmdentry(char * name,struct cmdentry * entry)890 addcmdentry(char *name, struct cmdentry *entry)
891 {
892 struct tblentry *cmdp;
893
894 INTOFF;
895 cmdp = cmdlookup(name, 1);
896 if (cmdp->cmdtype != CMDSPLBLTIN) {
897 if (cmdp->cmdtype == CMDFUNCTION) {
898 freefunc(cmdp->param.func);
899 }
900 cmdp->cmdtype = entry->cmdtype;
901 cmdp->param = entry->u;
902 }
903 INTON;
904 }
905
906
907 /*
908 * Define a shell function.
909 */
910
911 void
defun(char * name,union node * func)912 defun(char *name, union node *func)
913 {
914 struct cmdentry entry;
915
916 INTOFF;
917 entry.cmdtype = CMDFUNCTION;
918 entry.u.func = copyfunc(func);
919 addcmdentry(name, &entry);
920 INTON;
921 }
922
923
924 /*
925 * Delete a function if it exists.
926 */
927
928 int
unsetfunc(char * name)929 unsetfunc(char *name)
930 {
931 struct tblentry *cmdp;
932
933 if ((cmdp = cmdlookup(name, 0)) != NULL &&
934 cmdp->cmdtype == CMDFUNCTION) {
935 freefunc(cmdp->param.func);
936 delete_cmd_entry();
937 return (0);
938 }
939 return (1);
940 }
941
942 /*
943 * Locate and print what a word is...
944 * also used for 'command -[v|V]'
945 */
946
947 int
typecmd(int argc,char ** argv)948 typecmd(int argc, char **argv)
949 {
950 struct cmdentry entry;
951 struct tblentry *cmdp;
952 char * const *pp;
953 struct alias *ap;
954 int err = 0;
955 char *arg;
956 int c;
957 int V_flag = 0;
958 int v_flag = 0;
959 int p_flag = 0;
960
961 while ((c = nextopt("vVp")) != 0) {
962 switch (c) {
963 case 'v': v_flag = 1; break;
964 case 'V': V_flag = 1; break;
965 case 'p': p_flag = 1; break;
966 }
967 }
968
969 if (p_flag && (v_flag || V_flag))
970 error("cannot specify -p with -v or -V");
971
972 while ((arg = *argptr++)) {
973 if (!v_flag)
974 out1str(arg);
975 /* First look at the keywords */
976 for (pp = parsekwd; *pp; pp++)
977 if (**pp == *arg && equal(*pp, arg))
978 break;
979
980 if (*pp) {
981 if (v_flag)
982 err = 1;
983 else
984 out1str(" is a shell keyword\n");
985 continue;
986 }
987
988 /* Then look at the aliases */
989 if ((ap = lookupalias(arg, 1)) != NULL) {
990 if (!v_flag)
991 out1fmt(" is an alias for \n");
992 out1fmt("%s\n", ap->val);
993 continue;
994 }
995
996 /* Then check if it is a tracked alias */
997 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
998 entry.cmdtype = cmdp->cmdtype;
999 entry.u = cmdp->param;
1000 } else {
1001 /* Finally use brute force */
1002 find_command(arg, &entry, DO_ABS, pathval());
1003 }
1004
1005 switch (entry.cmdtype) {
1006 case CMDNORMAL: {
1007 if (strchr(arg, '/') == NULL) {
1008 const char *path = pathval();
1009 char *name;
1010 int j = entry.u.index;
1011 do {
1012 name = padvance(&path, arg);
1013 stunalloc(name);
1014 } while (--j >= 0);
1015 if (!v_flag)
1016 out1fmt(" is%s ",
1017 cmdp ? " a tracked alias for" : "");
1018 out1fmt("%s\n", name);
1019 } else {
1020 if (access(arg, X_OK) == 0) {
1021 if (!v_flag)
1022 out1fmt(" is ");
1023 out1fmt("%s\n", arg);
1024 } else {
1025 if (!v_flag)
1026 out1fmt(": %s\n",
1027 strerror(errno));
1028 else
1029 err = 126;
1030 }
1031 }
1032 break;
1033 }
1034 case CMDFUNCTION:
1035 if (!v_flag)
1036 out1str(" is a shell function\n");
1037 else
1038 out1fmt("%s\n", arg);
1039 break;
1040
1041 case CMDBUILTIN:
1042 if (!v_flag)
1043 out1str(" is a shell builtin\n");
1044 else
1045 out1fmt("%s\n", arg);
1046 break;
1047
1048 case CMDSPLBLTIN:
1049 if (!v_flag)
1050 out1str(" is a special shell builtin\n");
1051 else
1052 out1fmt("%s\n", arg);
1053 break;
1054
1055 default:
1056 if (!v_flag)
1057 out1str(": not found\n");
1058 err = 127;
1059 break;
1060 }
1061 }
1062 return err;
1063 }
1064