kmkbuiltin/expr.c: file revision 1.17 from OpenBSD
[kbuild-mirror.git] / src / ash / exec.c
blob31be37fd1ed054d9ccbcccd52b6800b76767d55c
1 /* $NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $ */
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
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.
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.
35 #ifdef HAVE_SYS_CDEFS_H
36 #include <sys/cdefs.h>
37 #endif
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
41 #else
42 __RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $");
43 #endif
44 #endif /* not lint */
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/wait.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <stdio.h>
53 #include <stdlib.h>
56 * When commands are first encountered, they are entered in a hash table.
57 * This ensures that a full path search will not have to be done for them
58 * on each invocation.
60 * We should investigate converting to a linear search, even though that
61 * would make the command name "hash" a misnomer.
64 #include "shell.h"
65 #include "main.h"
66 #include "nodes.h"
67 #include "parser.h"
68 #include "redir.h"
69 #include "eval.h"
70 #include "exec.h"
71 #include "builtins.h"
72 #include "var.h"
73 #include "options.h"
74 #include "input.h"
75 #include "output.h"
76 #include "syntax.h"
77 #include "memalloc.h"
78 #include "error.h"
79 #include "init.h"
80 #include "mystring.h"
81 #include "show.h"
82 #include "jobs.h"
83 #include "alias.h"
84 #ifdef __INNOTEK_LIBC__
85 #include <InnoTekLIBC/backend.h>
86 #endif
89 #define CMDTABLESIZE 31 /* should be prime */
90 #define ARB 1 /* actual size determined at run time */
94 struct tblentry {
95 struct tblentry *next; /* next entry in hash chain */
96 union param param; /* definition of builtin function */
97 short cmdtype; /* index identifying command */
98 char rehash; /* if set, cd done since entry created */
99 char cmdname[ARB]; /* name of command */
103 STATIC struct tblentry *cmdtable[CMDTABLESIZE];
104 STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
105 int exerrno = 0; /* Last exec error */
108 STATIC void tryexec(char *, char **, char **, int, int);
109 STATIC void execinterp(char **, char **);
110 STATIC void printentry(struct tblentry *, int);
111 STATIC void clearcmdentry(int);
112 STATIC struct tblentry *cmdlookup(const char *, int);
113 STATIC void delete_cmd_entry(void);
114 #ifdef PC_EXE_EXTS
115 STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext);
116 #endif
119 extern char *const parsekwd[];
122 * Exec a program. Never returns. If you change this routine, you may
123 * have to change the find_command routine as well.
126 void
127 shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
129 char *cmdname;
130 int e;
131 #ifdef PC_EXE_EXTS
132 int has_ext = strlen(argv[0]) - 4;
133 has_ext = has_ext > 0
134 && argv[0][has_ext] == '.'
135 /* use strstr and upper/lower permuated extensions to avoid multiple strcasecmp calls. */
136 && strstr("exe;" "Exe;" "EXe;" "EXE;" "ExE;" "eXe;" "eXE;" "exE;"
137 "cmd;" "Cmd;" "CMd;" "CMD;" "CmD;" "cMd;" "cMD;" "cmD;"
138 "com;" "Com;" "COm;" "COM;" "CoM;" "cOm;" "cOM;" "coM;"
139 "bat;" "Bat;" "BAt;" "BAT;" "BaT;" "bAt;" "bAT;" "baT;"
140 "btm;" "Btm;" "BTm;" "BTM;" "BtM;" "bTm;" "bTM;" "btM;",
141 argv[0] + has_ext + 1)
142 != NULL;
143 #else
144 const int has_ext = 1;
145 #endif
146 TRACE(("shellexec: argv[0]=%s idx=%d\n", argv[0], idx));
147 if (strchr(argv[0], '/') != NULL) {
148 cmdname = stalloc(strlen(argv[0]) + 5);
149 strcpy(cmdname, argv[0]);
150 tryexec(cmdname, argv, envp, vforked, has_ext);
151 TRACE(("shellexec: cmdname=%s\n", cmdname));
152 stunalloc(cmdname);
153 e = errno;
154 } else {
155 e = ENOENT;
156 while ((cmdname = padvance(&path, argv[0])) != NULL) {
157 if (--idx < 0 && pathopt == NULL) {
158 tryexec(cmdname, argv, envp, vforked, has_ext);
159 if (errno != ENOENT && errno != ENOTDIR)
160 e = errno;
162 stunalloc(cmdname);
166 /* Map to POSIX errors */
167 switch (e) {
168 case EACCES:
169 exerrno = 126;
170 break;
171 case ENOENT:
172 exerrno = 127;
173 break;
174 default:
175 exerrno = 2;
176 break;
178 TRACE(("shellexec failed for '%s', errno %d, vforked %d, suppressint %d\n",
179 argv[0], e, vforked, suppressint ));
180 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
181 /* NOTREACHED */
185 STATIC void
186 tryexec(char *cmd, char **argv, char **envp, int vforked, int has_ext)
188 int e;
189 #ifdef EXEC_HASH_BANG_SCRIPT
190 char *p;
191 #endif
192 #ifdef PC_EXE_EXTS
193 /* exploit the effect of stat_pc_exec_exts which adds the
194 * correct extentions to the file.
196 struct stat st;
197 if (!has_ext)
198 stat_pc_exec_exts(cmd, &st, 0);
199 #endif
200 #if defined __INNOTEK_LIBC__ && defined EXEC_HASH_BANG_SCRIPT
201 __libc_Back_gfProcessHandleHashBangScripts = 0;
202 #endif
204 #ifdef SYSV
205 do {
206 execve(cmd, argv, envp);
207 } while (errno == EINTR);
208 #else
209 execve(cmd, argv, envp);
210 #endif
211 e = errno;
212 if (e == ENOEXEC) {
213 if (vforked) {
214 /* We are currently vfork(2)ed, so raise an
215 * exception, and evalcommand will try again
216 * with a normal fork(2).
218 exraise(EXSHELLPROC);
220 initshellproc();
221 setinputfile(cmd, 0);
222 commandname = arg0 = savestr(argv[0]);
223 #ifdef EXEC_HASH_BANG_SCRIPT
224 pgetc(); pungetc(); /* fill up input buffer */
225 p = parsenextc;
226 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
227 argv[0] = cmd;
228 execinterp(argv, envp);
230 #endif
231 setparam(argv + 1);
232 exraise(EXSHELLPROC);
234 errno = e;
238 #ifdef EXEC_HASH_BANG_SCRIPT
240 * Execute an interpreter introduced by "#!", for systems where this
241 * feature has not been built into the kernel. If the interpreter is
242 * the shell, return (effectively ignoring the "#!"). If the execution
243 * of the interpreter fails, exit.
245 * This code peeks inside the input buffer in order to avoid actually
246 * reading any input. It would benefit from a rewrite.
249 #define NEWARGS 5
251 STATIC void
252 execinterp(char **argv, char **envp)
254 int n;
255 char *inp;
256 char *outp;
257 char c;
258 char *p;
259 char **ap;
260 char *newargs[NEWARGS];
261 int i;
262 char **ap2;
263 char **new;
265 n = parsenleft - 2;
266 inp = parsenextc + 2;
267 ap = newargs;
268 for (;;) {
269 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
270 inp++;
271 if (n < 0)
272 goto bad;
273 if ((c = *inp++) == '\n')
274 break;
275 if (ap == &newargs[NEWARGS])
276 bad: error("Bad #! line");
277 STARTSTACKSTR(outp);
278 do {
279 STPUTC(c, outp);
280 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
281 STPUTC('\0', outp);
282 n++, inp--;
283 *ap++ = grabstackstr(outp);
285 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
286 p = newargs[0];
287 for (;;) {
288 if (equal(p, "sh") || equal(p, "ash")) {
289 TRACE(("hash bang self\n"));
290 return;
292 while (*p != '/') {
293 if (*p == '\0')
294 goto break2;
295 p++;
297 p++;
299 break2:;
301 i = (char *)ap - (char *)newargs; /* size in bytes */
302 if (i == 0)
303 error("Bad #! line");
304 for (ap2 = argv ; *ap2++ != NULL ; );
305 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
306 ap = newargs, ap2 = new;
307 while ((i -= sizeof (char **)) >= 0)
308 *ap2++ = *ap++;
309 ap = argv;
310 while (*ap2++ = *ap++);
311 TRACE(("hash bang '%s'\n", new[0]));
312 shellexec(new, envp, pathval(), 0, 0);
313 /* NOTREACHED */
315 #endif
320 * Do a path search. The variable path (passed by reference) should be
321 * set to the start of the path before the first call; padvance will update
322 * this value as it proceeds. Successive calls to padvance will return
323 * the possible path expansions in sequence. If an option (indicated by
324 * a percent sign) appears in the path entry then the global variable
325 * pathopt will be set to point to it; otherwise pathopt will be set to
326 * NULL.
329 const char *pathopt;
331 char *
332 padvance(const char **path, const char *name)
334 const char *p;
335 char *q;
336 #ifdef PC_SLASHES
337 char *s;
338 #endif
339 const char *start;
340 int len;
342 if (*path == NULL)
343 return NULL;
344 start = *path;
345 #ifdef PC_PATH_SEP
346 for (p = start ; *p && *p != ';' && *p != '%' ; p++);
347 #else
348 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
349 #endif
350 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
351 #ifdef PC_EXE_EXTS
352 len += 4; /* "4" is for .exe/.com/.cmd/.bat/.btm */
353 #endif
354 while (stackblocksize() < len)
355 growstackblock();
356 #ifdef PC_SLASHES
358 #endif
359 q = stackblock();
360 if (p != start) {
361 memcpy(q, start, p - start);
362 q += p - start;
363 *q++ = '/';
365 strcpy(q, name);
366 #ifdef PC_SLASHES
367 while ((s = strchr(s, '\\')) != NULL)
368 *s++ = '/';
369 #endif
370 pathopt = NULL;
371 if (*p == '%') {
372 pathopt = ++p;
373 #ifdef PC_PATH_SEP
374 while (*p && *p != ';') p++;
375 #else
376 while (*p && *p != ':') p++;
377 #endif
379 #ifdef PC_PATH_SEP
380 if (*p == ';')
381 #else
382 if (*p == ':')
383 #endif
384 *path = p + 1;
385 else
386 *path = NULL;
387 return stalloc(len);
391 #ifdef PC_EXE_EXTS
392 STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext)
394 /* skip the SYSV crap */
395 if (stat(fullname, st) >= 0)
396 return 0;
397 if (!has_ext && errno == ENOENT)
399 char *psz = strchr(fullname, '\0');
400 memcpy(psz, ".exe", 5);
401 if (stat(fullname, st) >= 0)
402 return 0;
403 if (errno != ENOENT && errno != ENOTDIR)
404 return -1;
406 memcpy(psz, ".cmd", 5);
407 if (stat(fullname, st) >= 0)
408 return 0;
409 if (errno != ENOENT && errno != ENOTDIR)
410 return -1;
412 memcpy(psz, ".bat", 5);
413 if (stat(fullname, st) >= 0)
414 return 0;
415 if (errno != ENOENT && errno != ENOTDIR)
416 return -1;
418 memcpy(psz, ".com", 5);
419 if (stat(fullname, st) >= 0)
420 return 0;
421 if (errno != ENOENT && errno != ENOTDIR)
422 return -1;
424 memcpy(psz, ".btm", 5);
425 if (stat(fullname, st) >= 0)
426 return 0;
427 *psz = '\0';
429 return -1;
431 #endif /* PC_EXE_EXTS */
435 /*** Command hashing code ***/
439 hashcmd(int argc, char **argv)
441 struct tblentry **pp;
442 struct tblentry *cmdp;
443 int c;
444 int verbose;
445 struct cmdentry entry;
446 char *name;
448 verbose = 0;
449 while ((c = nextopt("rv")) != '\0') {
450 if (c == 'r') {
451 clearcmdentry(0);
452 } else if (c == 'v') {
453 verbose++;
456 if (*argptr == NULL) {
457 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
458 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
459 if (verbose || cmdp->cmdtype == CMDNORMAL)
460 printentry(cmdp, verbose);
463 return 0;
465 while ((name = *argptr) != NULL) {
466 if ((cmdp = cmdlookup(name, 0)) != NULL
467 && (cmdp->cmdtype == CMDNORMAL
468 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
469 delete_cmd_entry();
470 find_command(name, &entry, DO_ERR, pathval());
471 if (verbose) {
472 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
473 cmdp = cmdlookup(name, 0);
474 printentry(cmdp, verbose);
476 output_flushall();
478 argptr++;
480 return 0;
484 STATIC void
485 printentry(struct tblentry *cmdp, int verbose)
487 int idx;
488 const char *path;
489 char *name;
491 switch (cmdp->cmdtype) {
492 case CMDNORMAL:
493 idx = cmdp->param.index;
494 path = pathval();
495 do {
496 name = padvance(&path, cmdp->cmdname);
497 stunalloc(name);
498 } while (--idx >= 0);
499 out1str(name);
500 break;
501 case CMDSPLBLTIN:
502 out1fmt("special builtin %s", cmdp->cmdname);
503 break;
504 case CMDBUILTIN:
505 out1fmt("builtin %s", cmdp->cmdname);
506 break;
507 case CMDFUNCTION:
508 out1fmt("function %s", cmdp->cmdname);
509 if (verbose) {
510 struct procstat ps;
511 INTOFF;
512 commandtext(&ps, cmdp->param.func);
513 INTON;
514 out1str("() { ");
515 out1str(ps.cmd);
516 out1str("; }");
518 break;
519 default:
520 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
522 if (cmdp->rehash)
523 out1c('*');
524 out1c('\n');
530 * Resolve a command name. If you change this routine, you may have to
531 * change the shellexec routine as well.
534 void
535 find_command(char *name, struct cmdentry *entry, int act, const char *path)
537 struct tblentry *cmdp, loc_cmd;
538 int idx;
539 int prev;
540 char *fullname;
541 struct stat statb;
542 int e;
543 int (*bltin)(int,char **);
545 #ifdef PC_EXE_EXTS
546 int has_ext = strlen(name) - 4;
547 has_ext = has_ext > 0
548 && name[has_ext] == '.'
549 /* use strstr and upper/lower permuated extensions to avoid multiple strcasecmp calls. */
550 && strstr("exe;" "Exe;" "EXe;" "EXE;" "ExE;" "eXe;" "eXE;" "exE;"
551 "cmd;" "Cmd;" "CMd;" "CMD;" "CmD;" "cMd;" "cMD;" "cmD;"
552 "com;" "Com;" "COm;" "COM;" "CoM;" "cOm;" "cOM;" "coM;"
553 "bat;" "Bat;" "BAt;" "BAT;" "BaT;" "bAt;" "bAT;" "baT;"
554 "btm;" "Btm;" "BTm;" "BTM;" "BtM;" "bTm;" "bTM;" "btM;",
555 name + has_ext + 1)
556 != NULL;
557 #endif
559 /* If name contains a slash, don't use PATH or hash table */
560 if (strchr(name, '/') != NULL) {
561 if (act & DO_ABS) {
562 while (stat(name, &statb) < 0) {
563 #ifdef SYSV
564 if (errno == EINTR)
565 continue;
566 #endif
567 if (errno != ENOENT && errno != ENOTDIR)
568 e = errno;
569 entry->cmdtype = CMDUNKNOWN;
570 entry->u.index = -1;
571 return;
573 entry->cmdtype = CMDNORMAL;
574 entry->u.index = -1;
575 return;
577 entry->cmdtype = CMDNORMAL;
578 entry->u.index = 0;
579 return;
582 if (path != pathval())
583 act |= DO_ALTPATH;
585 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
586 act |= DO_ALTBLTIN;
588 /* If name is in the table, check answer will be ok */
589 if ((cmdp = cmdlookup(name, 0)) != NULL) {
590 do {
591 switch (cmdp->cmdtype) {
592 case CMDNORMAL:
593 if (act & DO_ALTPATH) {
594 cmdp = NULL;
595 continue;
597 break;
598 case CMDFUNCTION:
599 if (act & DO_NOFUNC) {
600 cmdp = NULL;
601 continue;
603 break;
604 case CMDBUILTIN:
605 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
606 cmdp = NULL;
607 continue;
609 break;
611 /* if not invalidated by cd, we're done */
612 if (cmdp->rehash == 0)
613 goto success;
614 } while (0);
617 /* If %builtin not in path, check for builtin next */
618 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
619 (bltin = find_builtin(name)) != 0)
620 goto builtin_success;
622 /* We have to search path. */
623 prev = -1; /* where to start */
624 if (cmdp) { /* doing a rehash */
625 if (cmdp->cmdtype == CMDBUILTIN)
626 prev = builtinloc;
627 else
628 prev = cmdp->param.index;
631 e = ENOENT;
632 idx = -1;
633 loop:
634 while ((fullname = padvance(&path, name)) != NULL) {
635 stunalloc(fullname);
636 idx++;
637 if (pathopt) {
638 if (prefix("builtin", pathopt)) {
639 if ((bltin = find_builtin(name)) == 0)
640 goto loop;
641 goto builtin_success;
642 } else if (prefix("func", pathopt)) {
643 /* handled below */
644 } else {
645 /* ignore unimplemented options */
646 goto loop;
649 /* if rehash, don't redo absolute path names */
650 if (fullname[0] == '/' && idx <= prev) {
651 if (idx < prev)
652 goto loop;
653 TRACE(("searchexec \"%s\": no change\n", name));
654 goto success;
656 #ifdef PC_EXE_EXTS
657 while (stat_pc_exec_exts(fullname, &statb, has_ext) < 0) {
658 #else
659 while (stat(fullname, &statb) < 0) {
660 #endif
661 #ifdef SYSV
662 if (errno == EINTR)
663 continue;
664 #endif
665 if (errno != ENOENT && errno != ENOTDIR)
666 e = errno;
668 goto loop;
670 e = EACCES; /* if we fail, this will be the error */
671 if (!S_ISREG(statb.st_mode))
672 goto loop;
673 if (pathopt) { /* this is a %func directory */
674 if (act & DO_NOFUNC)
675 goto loop;
676 stalloc(strlen(fullname) + 1);
677 readcmdfile(fullname);
678 if ((cmdp = cmdlookup(name, 0)) == NULL ||
679 cmdp->cmdtype != CMDFUNCTION)
680 error("%s not defined in %s", name, fullname);
681 stunalloc(fullname);
682 goto success;
684 #ifdef notdef
685 /* XXX this code stops root executing stuff, and is buggy
686 if you need a group from the group list. */
687 if (statb.st_uid == geteuid()) {
688 if ((statb.st_mode & 0100) == 0)
689 goto loop;
690 } else if (statb.st_gid == getegid()) {
691 if ((statb.st_mode & 010) == 0)
692 goto loop;
693 } else {
694 if ((statb.st_mode & 01) == 0)
695 goto loop;
697 #endif
698 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
699 INTOFF;
700 if (act & DO_ALTPATH) {
701 stalloc(strlen(fullname) + 1);
702 cmdp = &loc_cmd;
703 } else
704 cmdp = cmdlookup(name, 1);
705 cmdp->cmdtype = CMDNORMAL;
706 cmdp->param.index = idx;
707 INTON;
708 goto success;
711 /* We failed. If there was an entry for this command, delete it */
712 if (cmdp)
713 delete_cmd_entry();
714 if (act & DO_ERR)
715 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
716 entry->cmdtype = CMDUNKNOWN;
717 return;
719 builtin_success:
720 INTOFF;
721 if (act & DO_ALTPATH)
722 cmdp = &loc_cmd;
723 else
724 cmdp = cmdlookup(name, 1);
725 if (cmdp->cmdtype == CMDFUNCTION)
726 /* DO_NOFUNC must have been set */
727 cmdp = &loc_cmd;
728 cmdp->cmdtype = CMDBUILTIN;
729 cmdp->param.bltin = bltin;
730 INTON;
731 success:
732 cmdp->rehash = 0;
733 entry->cmdtype = cmdp->cmdtype;
734 entry->u = cmdp->param;
740 * Search the table of builtin commands.
744 (*find_builtin(name))(int, char **)
745 char *name;
747 const struct builtincmd *bp;
749 for (bp = builtincmd ; bp->name ; bp++) {
750 if (*bp->name == *name && equal(bp->name, name))
751 return bp->builtin;
753 return 0;
757 (*find_splbltin(name))(int, char **)
758 char *name;
760 const struct builtincmd *bp;
762 for (bp = splbltincmd ; bp->name ; bp++) {
763 if (*bp->name == *name && equal(bp->name, name))
764 return bp->builtin;
766 return 0;
770 * At shell startup put special builtins into hash table.
771 * ensures they are executed first (see posix).
772 * We stop functions being added with the same name
773 * (as they are impossible to call)
776 void
777 hash_special_builtins(void)
779 const struct builtincmd *bp;
780 struct tblentry *cmdp;
782 for (bp = splbltincmd ; bp->name ; bp++) {
783 cmdp = cmdlookup(bp->name, 1);
784 cmdp->cmdtype = CMDSPLBLTIN;
785 cmdp->param.bltin = bp->builtin;
792 * Called when a cd is done. Marks all commands so the next time they
793 * are executed they will be rehashed.
796 void
797 hashcd(void)
799 struct tblentry **pp;
800 struct tblentry *cmdp;
802 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
803 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
804 if (cmdp->cmdtype == CMDNORMAL
805 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
806 cmdp->rehash = 1;
814 * Fix command hash table when PATH changed.
815 * Called before PATH is changed. The argument is the new value of PATH;
816 * pathval() still returns the old value at this point.
817 * Called with interrupts off.
820 void
821 changepath(const char *newval)
823 const char *old, *new;
824 int idx;
825 int firstchange;
826 int bltin;
828 old = pathval();
829 new = newval;
830 firstchange = 9999; /* assume no change */
831 idx = 0;
832 bltin = -1;
833 for (;;) {
834 if (*old != *new) {
835 firstchange = idx;
836 #ifdef PC_PATH_SEP
837 if ((*old == '\0' && *new == ';')
838 || (*old == ';' && *new == '\0'))
839 #else
840 if ((*old == '\0' && *new == ':')
841 || (*old == ':' && *new == '\0'))
842 #endif
843 firstchange++;
844 old = new; /* ignore subsequent differences */
846 if (*new == '\0')
847 break;
848 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
849 bltin = idx;
850 #ifdef PC_PATH_SEP
851 if (*new == ';') {
852 #else
853 if (*new == ':') {
854 #endif
855 idx++;
857 new++, old++;
859 if (builtinloc < 0 && bltin >= 0)
860 builtinloc = bltin; /* zap builtins */
861 if (builtinloc >= 0 && bltin < 0)
862 firstchange = 0;
863 clearcmdentry(firstchange);
864 builtinloc = bltin;
869 * Clear out command entries. The argument specifies the first entry in
870 * PATH which has changed.
873 STATIC void
874 clearcmdentry(int firstchange)
876 struct tblentry **tblp;
877 struct tblentry **pp;
878 struct tblentry *cmdp;
880 INTOFF;
881 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
882 pp = tblp;
883 while ((cmdp = *pp) != NULL) {
884 if ((cmdp->cmdtype == CMDNORMAL &&
885 cmdp->param.index >= firstchange)
886 || (cmdp->cmdtype == CMDBUILTIN &&
887 builtinloc >= firstchange)) {
888 *pp = cmdp->next;
889 ckfree(cmdp);
890 } else {
891 pp = &cmdp->next;
895 INTON;
900 * Delete all functions.
903 #ifdef mkinit
904 MKINIT void deletefuncs(void);
905 MKINIT void hash_special_builtins(void);
907 INIT {
908 hash_special_builtins();
911 SHELLPROC {
912 deletefuncs();
914 #endif
916 void
917 deletefuncs(void)
919 struct tblentry **tblp;
920 struct tblentry **pp;
921 struct tblentry *cmdp;
923 INTOFF;
924 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
925 pp = tblp;
926 while ((cmdp = *pp) != NULL) {
927 if (cmdp->cmdtype == CMDFUNCTION) {
928 *pp = cmdp->next;
929 freefunc(cmdp->param.func);
930 ckfree(cmdp);
931 } else {
932 pp = &cmdp->next;
936 INTON;
942 * Locate a command in the command hash table. If "add" is nonzero,
943 * add the command to the table if it is not already present. The
944 * variable "lastcmdentry" is set to point to the address of the link
945 * pointing to the entry, so that delete_cmd_entry can delete the
946 * entry.
949 struct tblentry **lastcmdentry;
952 STATIC struct tblentry *
953 cmdlookup(const char *name, int add)
955 int hashval;
956 const char *p;
957 struct tblentry *cmdp;
958 struct tblentry **pp;
960 p = name;
961 hashval = *p << 4;
962 while (*p)
963 hashval += *p++;
964 hashval &= 0x7FFF;
965 pp = &cmdtable[hashval % CMDTABLESIZE];
966 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
967 if (equal(cmdp->cmdname, name))
968 break;
969 pp = &cmdp->next;
971 if (add && cmdp == NULL) {
972 INTOFF;
973 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
974 + strlen(name) + 1);
975 cmdp->next = NULL;
976 cmdp->cmdtype = CMDUNKNOWN;
977 cmdp->rehash = 0;
978 strcpy(cmdp->cmdname, name);
979 INTON;
981 lastcmdentry = pp;
982 return cmdp;
986 * Delete the command entry returned on the last lookup.
989 STATIC void
990 delete_cmd_entry(void)
992 struct tblentry *cmdp;
994 INTOFF;
995 cmdp = *lastcmdentry;
996 *lastcmdentry = cmdp->next;
997 ckfree(cmdp);
998 INTON;
1003 #ifdef notdef
1004 void
1005 getcmdentry(char *name, struct cmdentry *entry)
1007 struct tblentry *cmdp = cmdlookup(name, 0);
1009 if (cmdp) {
1010 entry->u = cmdp->param;
1011 entry->cmdtype = cmdp->cmdtype;
1012 } else {
1013 entry->cmdtype = CMDUNKNOWN;
1014 entry->u.index = 0;
1017 #endif
1021 * Add a new command entry, replacing any existing command entry for
1022 * the same name - except special builtins.
1025 STATIC void
1026 addcmdentry(char *name, struct cmdentry *entry)
1028 struct tblentry *cmdp;
1030 INTOFF;
1031 cmdp = cmdlookup(name, 1);
1032 if (cmdp->cmdtype != CMDSPLBLTIN) {
1033 if (cmdp->cmdtype == CMDFUNCTION) {
1034 freefunc(cmdp->param.func);
1036 cmdp->cmdtype = entry->cmdtype;
1037 cmdp->param = entry->u;
1039 INTON;
1044 * Define a shell function.
1047 void
1048 defun(char *name, union node *func)
1050 struct cmdentry entry;
1052 INTOFF;
1053 entry.cmdtype = CMDFUNCTION;
1054 entry.u.func = copyfunc(func);
1055 addcmdentry(name, &entry);
1056 INTON;
1061 * Delete a function if it exists.
1065 unsetfunc(char *name)
1067 struct tblentry *cmdp;
1069 if ((cmdp = cmdlookup(name, 0)) != NULL &&
1070 cmdp->cmdtype == CMDFUNCTION) {
1071 freefunc(cmdp->param.func);
1072 delete_cmd_entry();
1073 return (0);
1075 return (1);
1079 * Locate and print what a word is...
1080 * also used for 'command -[v|V]'
1084 typecmd(int argc, char **argv)
1086 struct cmdentry entry;
1087 struct tblentry *cmdp;
1088 char * const *pp;
1089 struct alias *ap;
1090 int err = 0;
1091 char *arg;
1092 int c;
1093 int V_flag = 0;
1094 int v_flag = 0;
1095 int p_flag = 0;
1097 while ((c = nextopt("vVp")) != 0) {
1098 switch (c) {
1099 case 'v': v_flag = 1; break;
1100 case 'V': V_flag = 1; break;
1101 case 'p': p_flag = 1; break;
1105 if (p_flag && (v_flag || V_flag))
1106 error("cannot specify -p with -v or -V");
1108 while ((arg = *argptr++)) {
1109 if (!v_flag)
1110 out1str(arg);
1111 /* First look at the keywords */
1112 for (pp = parsekwd; *pp; pp++)
1113 if (**pp == *arg && equal(*pp, arg))
1114 break;
1116 if (*pp) {
1117 if (v_flag)
1118 err = 1;
1119 else
1120 out1str(" is a shell keyword\n");
1121 continue;
1124 /* Then look at the aliases */
1125 if ((ap = lookupalias(arg, 1)) != NULL) {
1126 if (!v_flag)
1127 out1fmt(" is an alias for \n");
1128 out1fmt("%s\n", ap->val);
1129 continue;
1132 /* Then check if it is a tracked alias */
1133 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
1134 entry.cmdtype = cmdp->cmdtype;
1135 entry.u = cmdp->param;
1136 } else {
1137 /* Finally use brute force */
1138 find_command(arg, &entry, DO_ABS, pathval());
1141 switch (entry.cmdtype) {
1142 case CMDNORMAL: {
1143 if (strchr(arg, '/') == NULL) {
1144 const char *path = pathval();
1145 char *name;
1146 int j = entry.u.index;
1147 do {
1148 name = padvance(&path, arg);
1149 stunalloc(name);
1150 } while (--j >= 0);
1151 if (!v_flag)
1152 out1fmt(" is%s ",
1153 cmdp ? " a tracked alias for" : "");
1154 out1fmt("%s\n", name);
1155 } else {
1156 if (access(arg, X_OK) == 0) {
1157 if (!v_flag)
1158 out1fmt(" is ");
1159 out1fmt("%s\n", arg);
1160 } else {
1161 if (!v_flag)
1162 out1fmt(": %s\n",
1163 strerror(errno));
1164 else
1165 err = 126;
1168 break;
1170 case CMDFUNCTION:
1171 if (!v_flag)
1172 out1str(" is a shell function\n");
1173 else
1174 out1fmt("%s\n", arg);
1175 break;
1177 case CMDBUILTIN:
1178 if (!v_flag)
1179 out1str(" is a shell builtin\n");
1180 else
1181 out1fmt("%s\n", arg);
1182 break;
1184 case CMDSPLBLTIN:
1185 if (!v_flag)
1186 out1str(" is a special shell builtin\n");
1187 else
1188 out1fmt("%s\n", arg);
1189 break;
1191 default:
1192 if (!v_flag)
1193 out1str(": not found\n");
1194 err = 127;
1195 break;
1198 return err;