exec.c revision 1.34 1 /* $NetBSD: exec.c,v 1.34 2002/11/24 22:35:39 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. 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.34 2002/11/24 22:35:39 christos 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;
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 the 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 name is in the table, and not invalidated by cd, we're done */
463 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
464 goto success;
465
466 /* If %builtin not in path, check for builtin next */
467 if (builtinloc < 0 && (bltin = find_builtin(name)) != 0) {
468 INTOFF;
469 cmdp = cmdlookup(name, 1);
470 cmdp->cmdtype = CMDBUILTIN;
471 cmdp->param.bltin = bltin;
472 INTON;
473 goto success;
474 }
475
476 /* We have to search path. */
477 prev = -1; /* where to start */
478 if (cmdp) { /* doing a rehash */
479 if (cmdp->cmdtype == CMDBUILTIN)
480 prev = builtinloc;
481 else
482 prev = cmdp->param.index;
483 }
484
485 e = ENOENT;
486 idx = -1;
487 loop:
488 while ((fullname = padvance(&path, name)) != NULL) {
489 stunalloc(fullname);
490 idx++;
491 if (pathopt) {
492 if (prefix("builtin", pathopt)) {
493 if ((bltin = find_builtin(name)) == 0)
494 goto loop;
495 INTOFF;
496 cmdp = cmdlookup(name, 1);
497 cmdp->cmdtype = CMDBUILTIN;
498 cmdp->param.bltin = bltin;
499 INTON;
500 goto success;
501 } else if (prefix("func", pathopt)) {
502 /* handled below */
503 } else {
504 goto loop; /* ignore unimplemented options */
505 }
506 }
507 /* if rehash, don't redo absolute path names */
508 if (fullname[0] == '/' && idx <= prev) {
509 if (idx < prev)
510 goto loop;
511 TRACE(("searchexec \"%s\": no change\n", name));
512 goto success;
513 }
514 while (stat(fullname, &statb) < 0) {
515 #ifdef SYSV
516 if (errno == EINTR)
517 continue;
518 #endif
519 if (errno != ENOENT && errno != ENOTDIR)
520 e = errno;
521 goto loop;
522 }
523 e = EACCES; /* if we fail, this will be the error */
524 if (!S_ISREG(statb.st_mode))
525 goto loop;
526 if (pathopt) { /* this is a %func directory */
527 stalloc(strlen(fullname) + 1);
528 readcmdfile(fullname);
529 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
530 error("%s not defined in %s", name, fullname);
531 stunalloc(fullname);
532 goto success;
533 }
534 #ifdef notdef
535 if (statb.st_uid == geteuid()) {
536 if ((statb.st_mode & 0100) == 0)
537 goto loop;
538 } else if (statb.st_gid == getegid()) {
539 if ((statb.st_mode & 010) == 0)
540 goto loop;
541 } else {
542 if ((statb.st_mode & 01) == 0)
543 goto loop;
544 }
545 #endif
546 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
547 INTOFF;
548 cmdp = cmdlookup(name, 1);
549 cmdp->cmdtype = CMDNORMAL;
550 cmdp->param.index = idx;
551 INTON;
552 goto success;
553 }
554
555 /* We failed. If there was an entry for this command, delete it */
556 if (cmdp)
557 delete_cmd_entry();
558 if (act & DO_ERR)
559 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
560 entry->cmdtype = CMDUNKNOWN;
561 return;
562
563 success:
564 cmdp->rehash = 0;
565 entry->cmdtype = cmdp->cmdtype;
566 entry->u = cmdp->param;
567 }
568
569
570
571 /*
572 * Search the table of builtin commands.
573 */
574
575 int
576 (*find_builtin(name))(int, char **)
577 char *name;
578 {
579 const struct builtincmd *bp;
580
581 for (bp = builtincmd ; bp->name ; bp++) {
582 if (*bp->name == *name && equal(bp->name, name))
583 return bp->builtin;
584 }
585 return 0;
586 }
587
588 int
589 (*find_splbltin(name))(int, char **)
590 char *name;
591 {
592 const struct builtincmd *bp;
593
594 for (bp = splbltincmd ; bp->name ; bp++) {
595 if (*bp->name == *name && equal(bp->name, name))
596 return bp->builtin;
597 }
598 return 0;
599 }
600
601 /*
602 * At shell startup put special builtins into hash table.
603 * ensures they are executed first (see posix).
604 * We stop functions being added with the same name
605 * (as they are impossible to call)
606 */
607
608 void
609 hash_special_builtins(void)
610 {
611 const struct builtincmd *bp;
612 struct tblentry *cmdp;
613
614 for (bp = splbltincmd ; bp->name ; bp++) {
615 cmdp = cmdlookup(bp->name, 1);
616 cmdp->cmdtype = CMDSPLBLTIN;
617 cmdp->param.bltin = bp->builtin;
618 }
619 }
620
621
622
623 /*
624 * Called when a cd is done. Marks all commands so the next time they
625 * are executed they will be rehashed.
626 */
627
628 void
629 hashcd(void)
630 {
631 struct tblentry **pp;
632 struct tblentry *cmdp;
633
634 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
635 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
636 if (cmdp->cmdtype == CMDNORMAL
637 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
638 cmdp->rehash = 1;
639 }
640 }
641 }
642
643
644
645 /*
646 * Called before PATH is changed. The argument is the new value of PATH;
647 * pathval() still returns the old value at this point. Called with
648 * interrupts off.
649 */
650
651 void
652 changepath(const char *newval)
653 {
654 const char *old, *new;
655 int idx;
656 int firstchange;
657 int bltin;
658
659 old = pathval();
660 new = newval;
661 firstchange = 9999; /* assume no change */
662 idx = 0;
663 bltin = -1;
664 for (;;) {
665 if (*old != *new) {
666 firstchange = idx;
667 if ((*old == '\0' && *new == ':')
668 || (*old == ':' && *new == '\0'))
669 firstchange++;
670 old = new; /* ignore subsequent differences */
671 }
672 if (*new == '\0')
673 break;
674 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
675 bltin = idx;
676 if (*new == ':') {
677 idx++;
678 }
679 new++, old++;
680 }
681 if (builtinloc < 0 && bltin >= 0)
682 builtinloc = bltin; /* zap builtins */
683 if (builtinloc >= 0 && bltin < 0)
684 firstchange = 0;
685 clearcmdentry(firstchange);
686 builtinloc = bltin;
687 }
688
689
690 /*
691 * Clear out command entries. The argument specifies the first entry in
692 * PATH which has changed.
693 */
694
695 STATIC void
696 clearcmdentry(int firstchange)
697 {
698 struct tblentry **tblp;
699 struct tblentry **pp;
700 struct tblentry *cmdp;
701
702 INTOFF;
703 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
704 pp = tblp;
705 while ((cmdp = *pp) != NULL) {
706 if ((cmdp->cmdtype == CMDNORMAL &&
707 cmdp->param.index >= firstchange)
708 || (cmdp->cmdtype == CMDBUILTIN &&
709 builtinloc >= firstchange)) {
710 *pp = cmdp->next;
711 ckfree(cmdp);
712 } else {
713 pp = &cmdp->next;
714 }
715 }
716 }
717 INTON;
718 }
719
720
721 /*
722 * Delete all functions.
723 */
724
725 #ifdef mkinit
726 MKINIT void deletefuncs(void);
727 MKINIT void hash_special_builtins(void);
728
729 INIT {
730 hash_special_builtins();
731 }
732
733 SHELLPROC {
734 deletefuncs();
735 }
736 #endif
737
738 void
739 deletefuncs(void)
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 == CMDFUNCTION) {
750 *pp = cmdp->next;
751 freefunc(cmdp->param.func);
752 ckfree(cmdp);
753 } else {
754 pp = &cmdp->next;
755 }
756 }
757 }
758 INTON;
759 }
760
761
762
763 /*
764 * Locate a command in the command hash table. If "add" is nonzero,
765 * add the command to the table if it is not already present. The
766 * variable "lastcmdentry" is set to point to the address of the link
767 * pointing to the entry, so that delete_cmd_entry can delete the
768 * entry.
769 */
770
771 struct tblentry **lastcmdentry;
772
773
774 STATIC struct tblentry *
775 cmdlookup(const char *name, int add)
776 {
777 int hashval;
778 const char *p;
779 struct tblentry *cmdp;
780 struct tblentry **pp;
781
782 p = name;
783 hashval = *p << 4;
784 while (*p)
785 hashval += *p++;
786 hashval &= 0x7FFF;
787 pp = &cmdtable[hashval % CMDTABLESIZE];
788 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
789 if (equal(cmdp->cmdname, name))
790 break;
791 pp = &cmdp->next;
792 }
793 if (add && cmdp == NULL) {
794 INTOFF;
795 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
796 + strlen(name) + 1);
797 cmdp->next = NULL;
798 cmdp->cmdtype = CMDUNKNOWN;
799 cmdp->rehash = 0;
800 strcpy(cmdp->cmdname, name);
801 INTON;
802 }
803 lastcmdentry = pp;
804 return cmdp;
805 }
806
807 /*
808 * Delete the command entry returned on the last lookup.
809 */
810
811 STATIC void
812 delete_cmd_entry(void)
813 {
814 struct tblentry *cmdp;
815
816 INTOFF;
817 cmdp = *lastcmdentry;
818 *lastcmdentry = cmdp->next;
819 ckfree(cmdp);
820 INTON;
821 }
822
823
824
825 #ifdef notdef
826 void
827 getcmdentry(char *name, struct cmdentry *entry)
828 {
829 struct tblentry *cmdp = cmdlookup(name, 0);
830
831 if (cmdp) {
832 entry->u = cmdp->param;
833 entry->cmdtype = cmdp->cmdtype;
834 } else {
835 entry->cmdtype = CMDUNKNOWN;
836 entry->u.index = 0;
837 }
838 }
839 #endif
840
841
842 /*
843 * Add a new command entry, replacing any existing command entry for
844 * the same name - except special builtins.
845 */
846
847 void
848 addcmdentry(char *name, struct cmdentry *entry)
849 {
850 struct tblentry *cmdp;
851
852 INTOFF;
853 cmdp = cmdlookup(name, 1);
854 if (cmdp->cmdtype != CMDSPLBLTIN) {
855 if (cmdp->cmdtype == CMDFUNCTION) {
856 freefunc(cmdp->param.func);
857 }
858 cmdp->cmdtype = entry->cmdtype;
859 cmdp->param = entry->u;
860 }
861 INTON;
862 }
863
864
865 /*
866 * Define a shell function.
867 */
868
869 void
870 defun(char *name, union node *func)
871 {
872 struct cmdentry entry;
873
874 INTOFF;
875 entry.cmdtype = CMDFUNCTION;
876 entry.u.func = copyfunc(func);
877 addcmdentry(name, &entry);
878 INTON;
879 }
880
881
882 /*
883 * Delete a function if it exists.
884 */
885
886 int
887 unsetfunc(char *name)
888 {
889 struct tblentry *cmdp;
890
891 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
892 freefunc(cmdp->param.func);
893 delete_cmd_entry();
894 return (0);
895 }
896 return (1);
897 }
898
899 /*
900 * Locate and print what a word is...
901 */
902
903 int
904 typecmd(int argc, char **argv)
905 {
906 struct cmdentry entry;
907 struct tblentry *cmdp;
908 char * const *pp;
909 struct alias *ap;
910 int i;
911 int err = 0;
912
913 for (i = 1; i < argc; i++) {
914 out1str(argv[i]);
915 /* First look at the keywords */
916 for (pp = parsekwd; *pp; pp++)
917 if (**pp == *argv[i] && equal(*pp, argv[i]))
918 break;
919
920 if (*pp) {
921 out1str(" is a shell keyword\n");
922 continue;
923 }
924
925 /* Then look at the aliases */
926 if ((ap = lookupalias(argv[i], 1)) != NULL) {
927 out1fmt(" is an alias for %s\n", ap->val);
928 continue;
929 }
930
931 /* Then check if it is a tracked alias */
932 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
933 entry.cmdtype = cmdp->cmdtype;
934 entry.u = cmdp->param;
935 }
936 else {
937 /* Finally use brute force */
938 find_command(argv[i], &entry, DO_ABS, pathval());
939 }
940
941 switch (entry.cmdtype) {
942 case CMDNORMAL: {
943 if (strchr(argv[i], '/') == NULL) {
944 const char *path = pathval();
945 char *name;
946 int j = entry.u.index;
947 do {
948 name = padvance(&path, argv[i]);
949 stunalloc(name);
950 } while (--j >= 0);
951 out1fmt(" is%s %s\n",
952 cmdp ? " a tracked alias for" : "", name);
953 } else {
954 if (access(argv[i], X_OK) == 0)
955 out1fmt(" is %s\n", argv[i]);
956 else
957 out1fmt(": %s\n", strerror(errno));
958 }
959 break;
960 }
961 case CMDFUNCTION:
962 out1str(" is a shell function\n");
963 break;
964
965 case CMDBUILTIN:
966 out1str(" is a shell builtin\n");
967 break;
968
969 case CMDSPLBLTIN:
970 out1str(" is a special shell builtin\n");
971 break;
972
973 default:
974 out1str(": not found\n");
975 err |= 127;
976 break;
977 }
978 }
979 return err;
980 }
981