VirtualBox

source: kBuild/trunk/src/ash/exec.c@ 809

Last change on this file since 809 was 809, checked in by bird, 18 years ago

Solaris + cleanup.

  • Property svn:eol-style set to native
File size: 25.8 KB
Line 
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#ifdef HAVE_SYS_CDEFS_H
36#include <sys/cdefs.h>
37#endif
38#ifndef lint
39#if 0
40static 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 */
45
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>
54
55/*
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.
59 *
60 * We should investigate converting to a linear search, even though that
61 * would make the command name "hash" a misnomer.
62 */
63
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
87
88
89#define CMDTABLESIZE 31 /* should be prime */
90#define ARB 1 /* actual size determined at run time */
91
92
93
94struct 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 */
100};
101
102
103STATIC struct tblentry *cmdtable[CMDTABLESIZE];
104STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
105int exerrno = 0; /* Last exec error */
106
107
108STATIC void tryexec(char *, char **, char **, int, int);
109STATIC void execinterp(char **, char **);
110STATIC void printentry(struct tblentry *, int);
111STATIC void clearcmdentry(int);
112STATIC struct tblentry *cmdlookup(const char *, int);
113STATIC void delete_cmd_entry(void);
114#ifdef PC_EXE_EXTS
115STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext);
116#endif
117
118
119extern char *const parsekwd[];
120
121/*
122 * Exec a program. Never returns. If you change this routine, you may
123 * have to change the find_command routine as well.
124 */
125
126void
127shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
128{
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;
161 }
162 stunalloc(cmdname);
163 }
164 }
165
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;
177 }
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 */
182}
183
184
185STATIC void
186tryexec(char *cmd, char **argv, char **envp, int vforked, int has_ext)
187{
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.
195 */
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
203
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).
217 */
218 exraise(EXSHELLPROC);
219 }
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);
229 }
230#endif
231 setparam(argv + 1);
232 exraise(EXSHELLPROC);
233 }
234 errno = e;
235}
236
237
238#ifdef EXEC_HASH_BANG_SCRIPT
239/*
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.
244 *
245 * This code peeks inside the input buffer in order to avoid actually
246 * reading any input. It would benefit from a rewrite.
247 */
248
249#define NEWARGS 5
250
251STATIC void
252execinterp(char **argv, char **envp)
253{
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;
264
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])
276bad: 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);
284 }
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;
291 }
292 while (*p != '/') {
293 if (*p == '\0')
294 goto break2;
295 p++;
296 }
297 p++;
298 }
299break2:;
300 }
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 */
314}
315#endif
316
317
318
319/*
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.
327 */
328
329const char *pathopt;
330
331char *
332padvance(const char **path, const char *name)
333{
334 const char *p;
335 char *q;
336 const char *start;
337 int len;
338
339 if (*path == NULL)
340 return NULL;
341 start = *path;
342#ifdef PC_PATH_SEP
343 for (p = start ; *p && *p != ';' && *p != '%' ; p++);
344#else
345 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
346#endif
347 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
348#ifdef PC_EXE_EXTS
349 len += 4; /* "4" is for .exe/.com/.cmd/.bat/.btm */
350#endif
351 while (stackblocksize() < len)
352 growstackblock();
353 q = stackblock();
354 if (p != start) {
355 memcpy(q, start, p - start);
356 q += p - start;
357 *q++ = '/';
358 }
359 strcpy(q, name);
360 pathopt = NULL;
361 if (*p == '%') {
362 pathopt = ++p;
363#ifdef PC_PATH_SEP
364 while (*p && *p != ';') p++;
365#else
366 while (*p && *p != ':') p++;
367#endif
368 }
369#ifdef PC_PATH_SEP
370 if (*p == ';')
371#else
372 if (*p == ':')
373#endif
374 *path = p + 1;
375 else
376 *path = NULL;
377 return stalloc(len);
378}
379
380
381#ifdef PC_EXE_EXTS
382STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext)
383{
384 /* skip the SYSV crap */
385 if (stat(fullname, st) >= 0)
386 return 0;
387 if (!has_ext && errno == ENOENT)
388 {
389 char *psz = strchr(fullname, '\0');
390 memcpy(psz, ".exe", 5);
391 if (stat(fullname, st) >= 0)
392 return 0;
393 if (errno != ENOENT && errno != ENOTDIR)
394 return -1;
395
396 memcpy(psz, ".cmd", 5);
397 if (stat(fullname, st) >= 0)
398 return 0;
399 if (errno != ENOENT && errno != ENOTDIR)
400 return -1;
401
402 memcpy(psz, ".bat", 5);
403 if (stat(fullname, st) >= 0)
404 return 0;
405 if (errno != ENOENT && errno != ENOTDIR)
406 return -1;
407
408 memcpy(psz, ".com", 5);
409 if (stat(fullname, st) >= 0)
410 return 0;
411 if (errno != ENOENT && errno != ENOTDIR)
412 return -1;
413
414 memcpy(psz, ".btm", 5);
415 if (stat(fullname, st) >= 0)
416 return 0;
417 *psz = '\0';
418 }
419 return -1;
420}
421#endif /* PC_EXE_EXTS */
422
423
424
425/*** Command hashing code ***/
426
427
428int
429hashcmd(int argc, char **argv)
430{
431 struct tblentry **pp;
432 struct tblentry *cmdp;
433 int c;
434 int verbose;
435 struct cmdentry entry;
436 char *name;
437
438 verbose = 0;
439 while ((c = nextopt("rv")) != '\0') {
440 if (c == 'r') {
441 clearcmdentry(0);
442 } else if (c == 'v') {
443 verbose++;
444 }
445 }
446 if (*argptr == NULL) {
447 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
448 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
449 if (verbose || cmdp->cmdtype == CMDNORMAL)
450 printentry(cmdp, verbose);
451 }
452 }
453 return 0;
454 }
455 while ((name = *argptr) != NULL) {
456 if ((cmdp = cmdlookup(name, 0)) != NULL
457 && (cmdp->cmdtype == CMDNORMAL
458 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
459 delete_cmd_entry();
460 find_command(name, &entry, DO_ERR, pathval());
461 if (verbose) {
462 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
463 cmdp = cmdlookup(name, 0);
464 printentry(cmdp, verbose);
465 }
466 output_flushall();
467 }
468 argptr++;
469 }
470 return 0;
471}
472
473
474STATIC void
475printentry(struct tblentry *cmdp, int verbose)
476{
477 int idx;
478 const char *path;
479 char *name;
480
481 switch (cmdp->cmdtype) {
482 case CMDNORMAL:
483 idx = cmdp->param.index;
484 path = pathval();
485 do {
486 name = padvance(&path, cmdp->cmdname);
487 stunalloc(name);
488 } while (--idx >= 0);
489 out1str(name);
490 break;
491 case CMDSPLBLTIN:
492 out1fmt("special builtin %s", cmdp->cmdname);
493 break;
494 case CMDBUILTIN:
495 out1fmt("builtin %s", cmdp->cmdname);
496 break;
497 case CMDFUNCTION:
498 out1fmt("function %s", cmdp->cmdname);
499 if (verbose) {
500 struct procstat ps;
501 INTOFF;
502 commandtext(&ps, cmdp->param.func);
503 INTON;
504 out1str("() { ");
505 out1str(ps.cmd);
506 out1str("; }");
507 }
508 break;
509 default:
510 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
511 }
512 if (cmdp->rehash)
513 out1c('*');
514 out1c('\n');
515}
516
517
518
519/*
520 * Resolve a command name. If you change this routine, you may have to
521 * change the shellexec routine as well.
522 */
523
524void
525find_command(char *name, struct cmdentry *entry, int act, const char *path)
526{
527 struct tblentry *cmdp, loc_cmd;
528 int idx;
529 int prev;
530 char *fullname;
531 struct stat statb;
532 int e;
533 int (*bltin)(int,char **);
534
535#ifdef PC_EXE_EXTS
536 int has_ext = strlen(name) - 4;
537 has_ext = has_ext > 0
538 && name[has_ext] == '.'
539 /* use strstr and upper/lower permuated extensions to avoid multiple strcasecmp calls. */
540 && strstr("exe;" "Exe;" "EXe;" "EXE;" "ExE;" "eXe;" "eXE;" "exE;"
541 "cmd;" "Cmd;" "CMd;" "CMD;" "CmD;" "cMd;" "cMD;" "cmD;"
542 "com;" "Com;" "COm;" "COM;" "CoM;" "cOm;" "cOM;" "coM;"
543 "bat;" "Bat;" "BAt;" "BAT;" "BaT;" "bAt;" "bAT;" "baT;"
544 "btm;" "Btm;" "BTm;" "BTM;" "BtM;" "bTm;" "bTM;" "btM;",
545 name + has_ext + 1)
546 != NULL;
547#endif
548
549 /* If name contains a slash, don't use PATH or hash table */
550 if (strchr(name, '/') != NULL) {
551 if (act & DO_ABS) {
552 while (stat(name, &statb) < 0) {
553#ifdef SYSV
554 if (errno == EINTR)
555 continue;
556#endif
557 if (errno != ENOENT && errno != ENOTDIR)
558 e = errno;
559 entry->cmdtype = CMDUNKNOWN;
560 entry->u.index = -1;
561 return;
562 }
563 entry->cmdtype = CMDNORMAL;
564 entry->u.index = -1;
565 return;
566 }
567 entry->cmdtype = CMDNORMAL;
568 entry->u.index = 0;
569 return;
570 }
571
572 if (path != pathval())
573 act |= DO_ALTPATH;
574
575 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
576 act |= DO_ALTBLTIN;
577
578 /* If name is in the table, check answer will be ok */
579 if ((cmdp = cmdlookup(name, 0)) != NULL) {
580 do {
581 switch (cmdp->cmdtype) {
582 case CMDNORMAL:
583 if (act & DO_ALTPATH) {
584 cmdp = NULL;
585 continue;
586 }
587 break;
588 case CMDFUNCTION:
589 if (act & DO_NOFUNC) {
590 cmdp = NULL;
591 continue;
592 }
593 break;
594 case CMDBUILTIN:
595 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
596 cmdp = NULL;
597 continue;
598 }
599 break;
600 }
601 /* if not invalidated by cd, we're done */
602 if (cmdp->rehash == 0)
603 goto success;
604 } while (0);
605 }
606
607 /* If %builtin not in path, check for builtin next */
608 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
609 (bltin = find_builtin(name)) != 0)
610 goto builtin_success;
611
612 /* We have to search path. */
613 prev = -1; /* where to start */
614 if (cmdp) { /* doing a rehash */
615 if (cmdp->cmdtype == CMDBUILTIN)
616 prev = builtinloc;
617 else
618 prev = cmdp->param.index;
619 }
620
621 e = ENOENT;
622 idx = -1;
623loop:
624 while ((fullname = padvance(&path, name)) != NULL) {
625 stunalloc(fullname);
626 idx++;
627 if (pathopt) {
628 if (prefix("builtin", pathopt)) {
629 if ((bltin = find_builtin(name)) == 0)
630 goto loop;
631 goto builtin_success;
632 } else if (prefix("func", pathopt)) {
633 /* handled below */
634 } else {
635 /* ignore unimplemented options */
636 goto loop;
637 }
638 }
639 /* if rehash, don't redo absolute path names */
640 if (fullname[0] == '/' && idx <= prev) {
641 if (idx < prev)
642 goto loop;
643 TRACE(("searchexec \"%s\": no change\n", name));
644 goto success;
645 }
646#ifdef PC_EXE_EXTS
647 while (stat_pc_exec_exts(fullname, &statb, has_ext) < 0) {
648#else
649 while (stat(fullname, &statb) < 0) {
650#endif
651#ifdef SYSV
652 if (errno == EINTR)
653 continue;
654#endif
655 if (errno != ENOENT && errno != ENOTDIR)
656 e = errno;
657
658 goto loop;
659 }
660 e = EACCES; /* if we fail, this will be the error */
661 if (!S_ISREG(statb.st_mode))
662 goto loop;
663 if (pathopt) { /* this is a %func directory */
664 if (act & DO_NOFUNC)
665 goto loop;
666 stalloc(strlen(fullname) + 1);
667 readcmdfile(fullname);
668 if ((cmdp = cmdlookup(name, 0)) == NULL ||
669 cmdp->cmdtype != CMDFUNCTION)
670 error("%s not defined in %s", name, fullname);
671 stunalloc(fullname);
672 goto success;
673 }
674#ifdef notdef
675 /* XXX this code stops root executing stuff, and is buggy
676 if you need a group from the group list. */
677 if (statb.st_uid == geteuid()) {
678 if ((statb.st_mode & 0100) == 0)
679 goto loop;
680 } else if (statb.st_gid == getegid()) {
681 if ((statb.st_mode & 010) == 0)
682 goto loop;
683 } else {
684 if ((statb.st_mode & 01) == 0)
685 goto loop;
686 }
687#endif
688 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
689 INTOFF;
690 if (act & DO_ALTPATH) {
691 stalloc(strlen(fullname) + 1);
692 cmdp = &loc_cmd;
693 } else
694 cmdp = cmdlookup(name, 1);
695 cmdp->cmdtype = CMDNORMAL;
696 cmdp->param.index = idx;
697 INTON;
698 goto success;
699 }
700
701 /* We failed. If there was an entry for this command, delete it */
702 if (cmdp)
703 delete_cmd_entry();
704 if (act & DO_ERR)
705 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
706 entry->cmdtype = CMDUNKNOWN;
707 return;
708
709builtin_success:
710 INTOFF;
711 if (act & DO_ALTPATH)
712 cmdp = &loc_cmd;
713 else
714 cmdp = cmdlookup(name, 1);
715 if (cmdp->cmdtype == CMDFUNCTION)
716 /* DO_NOFUNC must have been set */
717 cmdp = &loc_cmd;
718 cmdp->cmdtype = CMDBUILTIN;
719 cmdp->param.bltin = bltin;
720 INTON;
721success:
722 cmdp->rehash = 0;
723 entry->cmdtype = cmdp->cmdtype;
724 entry->u = cmdp->param;
725}
726
727
728
729/*
730 * Search the table of builtin commands.
731 */
732
733int
734(*find_builtin(name))(int, char **)
735 char *name;
736{
737 const struct builtincmd *bp;
738
739 for (bp = builtincmd ; bp->name ; bp++) {
740 if (*bp->name == *name && equal(bp->name, name))
741 return bp->builtin;
742 }
743 return 0;
744}
745
746int
747(*find_splbltin(name))(int, char **)
748 char *name;
749{
750 const struct builtincmd *bp;
751
752 for (bp = splbltincmd ; bp->name ; bp++) {
753 if (*bp->name == *name && equal(bp->name, name))
754 return bp->builtin;
755 }
756 return 0;
757}
758
759/*
760 * At shell startup put special builtins into hash table.
761 * ensures they are executed first (see posix).
762 * We stop functions being added with the same name
763 * (as they are impossible to call)
764 */
765
766void
767hash_special_builtins(void)
768{
769 const struct builtincmd *bp;
770 struct tblentry *cmdp;
771
772 for (bp = splbltincmd ; bp->name ; bp++) {
773 cmdp = cmdlookup(bp->name, 1);
774 cmdp->cmdtype = CMDSPLBLTIN;
775 cmdp->param.bltin = bp->builtin;
776 }
777}
778
779
780
781/*
782 * Called when a cd is done. Marks all commands so the next time they
783 * are executed they will be rehashed.
784 */
785
786void
787hashcd(void)
788{
789 struct tblentry **pp;
790 struct tblentry *cmdp;
791
792 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
793 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
794 if (cmdp->cmdtype == CMDNORMAL
795 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
796 cmdp->rehash = 1;
797 }
798 }
799}
800
801
802
803/*
804 * Fix command hash table when PATH changed.
805 * Called before PATH is changed. The argument is the new value of PATH;
806 * pathval() still returns the old value at this point.
807 * Called with interrupts off.
808 */
809
810void
811changepath(const char *newval)
812{
813 const char *old, *new;
814 int idx;
815 int firstchange;
816 int bltin;
817
818 old = pathval();
819 new = newval;
820 firstchange = 9999; /* assume no change */
821 idx = 0;
822 bltin = -1;
823 for (;;) {
824 if (*old != *new) {
825 firstchange = idx;
826#ifdef PC_PATH_SEP
827 if ((*old == '\0' && *new == ';')
828 || (*old == ';' && *new == '\0'))
829#else
830 if ((*old == '\0' && *new == ':')
831 || (*old == ':' && *new == '\0'))
832#endif
833 firstchange++;
834 old = new; /* ignore subsequent differences */
835 }
836 if (*new == '\0')
837 break;
838 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
839 bltin = idx;
840#ifdef PC_PATH_SEP
841 if (*new == ';') {
842#else
843 if (*new == ':') {
844#endif
845 idx++;
846 }
847 new++, old++;
848 }
849 if (builtinloc < 0 && bltin >= 0)
850 builtinloc = bltin; /* zap builtins */
851 if (builtinloc >= 0 && bltin < 0)
852 firstchange = 0;
853 clearcmdentry(firstchange);
854 builtinloc = bltin;
855}
856
857
858/*
859 * Clear out command entries. The argument specifies the first entry in
860 * PATH which has changed.
861 */
862
863STATIC void
864clearcmdentry(int firstchange)
865{
866 struct tblentry **tblp;
867 struct tblentry **pp;
868 struct tblentry *cmdp;
869
870 INTOFF;
871 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
872 pp = tblp;
873 while ((cmdp = *pp) != NULL) {
874 if ((cmdp->cmdtype == CMDNORMAL &&
875 cmdp->param.index >= firstchange)
876 || (cmdp->cmdtype == CMDBUILTIN &&
877 builtinloc >= firstchange)) {
878 *pp = cmdp->next;
879 ckfree(cmdp);
880 } else {
881 pp = &cmdp->next;
882 }
883 }
884 }
885 INTON;
886}
887
888
889/*
890 * Delete all functions.
891 */
892
893#ifdef mkinit
894MKINIT void deletefuncs(void);
895MKINIT void hash_special_builtins(void);
896
897INIT {
898 hash_special_builtins();
899}
900
901SHELLPROC {
902 deletefuncs();
903}
904#endif
905
906void
907deletefuncs(void)
908{
909 struct tblentry **tblp;
910 struct tblentry **pp;
911 struct tblentry *cmdp;
912
913 INTOFF;
914 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
915 pp = tblp;
916 while ((cmdp = *pp) != NULL) {
917 if (cmdp->cmdtype == CMDFUNCTION) {
918 *pp = cmdp->next;
919 freefunc(cmdp->param.func);
920 ckfree(cmdp);
921 } else {
922 pp = &cmdp->next;
923 }
924 }
925 }
926 INTON;
927}
928
929
930
931/*
932 * Locate a command in the command hash table. If "add" is nonzero,
933 * add the command to the table if it is not already present. The
934 * variable "lastcmdentry" is set to point to the address of the link
935 * pointing to the entry, so that delete_cmd_entry can delete the
936 * entry.
937 */
938
939struct tblentry **lastcmdentry;
940
941
942STATIC struct tblentry *
943cmdlookup(const char *name, int add)
944{
945 int hashval;
946 const char *p;
947 struct tblentry *cmdp;
948 struct tblentry **pp;
949
950 p = name;
951 hashval = *p << 4;
952 while (*p)
953 hashval += *p++;
954 hashval &= 0x7FFF;
955 pp = &cmdtable[hashval % CMDTABLESIZE];
956 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
957 if (equal(cmdp->cmdname, name))
958 break;
959 pp = &cmdp->next;
960 }
961 if (add && cmdp == NULL) {
962 INTOFF;
963 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
964 + strlen(name) + 1);
965 cmdp->next = NULL;
966 cmdp->cmdtype = CMDUNKNOWN;
967 cmdp->rehash = 0;
968 strcpy(cmdp->cmdname, name);
969 INTON;
970 }
971 lastcmdentry = pp;
972 return cmdp;
973}
974
975/*
976 * Delete the command entry returned on the last lookup.
977 */
978
979STATIC void
980delete_cmd_entry(void)
981{
982 struct tblentry *cmdp;
983
984 INTOFF;
985 cmdp = *lastcmdentry;
986 *lastcmdentry = cmdp->next;
987 ckfree(cmdp);
988 INTON;
989}
990
991
992
993#ifdef notdef
994void
995getcmdentry(char *name, struct cmdentry *entry)
996{
997 struct tblentry *cmdp = cmdlookup(name, 0);
998
999 if (cmdp) {
1000 entry->u = cmdp->param;
1001 entry->cmdtype = cmdp->cmdtype;
1002 } else {
1003 entry->cmdtype = CMDUNKNOWN;
1004 entry->u.index = 0;
1005 }
1006}
1007#endif
1008
1009
1010/*
1011 * Add a new command entry, replacing any existing command entry for
1012 * the same name - except special builtins.
1013 */
1014
1015STATIC void
1016addcmdentry(char *name, struct cmdentry *entry)
1017{
1018 struct tblentry *cmdp;
1019
1020 INTOFF;
1021 cmdp = cmdlookup(name, 1);
1022 if (cmdp->cmdtype != CMDSPLBLTIN) {
1023 if (cmdp->cmdtype == CMDFUNCTION) {
1024 freefunc(cmdp->param.func);
1025 }
1026 cmdp->cmdtype = entry->cmdtype;
1027 cmdp->param = entry->u;
1028 }
1029 INTON;
1030}
1031
1032
1033/*
1034 * Define a shell function.
1035 */
1036
1037void
1038defun(char *name, union node *func)
1039{
1040 struct cmdentry entry;
1041
1042 INTOFF;
1043 entry.cmdtype = CMDFUNCTION;
1044 entry.u.func = copyfunc(func);
1045 addcmdentry(name, &entry);
1046 INTON;
1047}
1048
1049
1050/*
1051 * Delete a function if it exists.
1052 */
1053
1054int
1055unsetfunc(char *name)
1056{
1057 struct tblentry *cmdp;
1058
1059 if ((cmdp = cmdlookup(name, 0)) != NULL &&
1060 cmdp->cmdtype == CMDFUNCTION) {
1061 freefunc(cmdp->param.func);
1062 delete_cmd_entry();
1063 return (0);
1064 }
1065 return (1);
1066}
1067
1068/*
1069 * Locate and print what a word is...
1070 * also used for 'command -[v|V]'
1071 */
1072
1073int
1074typecmd(int argc, char **argv)
1075{
1076 struct cmdentry entry;
1077 struct tblentry *cmdp;
1078 char * const *pp;
1079 struct alias *ap;
1080 int err = 0;
1081 char *arg;
1082 int c;
1083 int V_flag = 0;
1084 int v_flag = 0;
1085 int p_flag = 0;
1086
1087 while ((c = nextopt("vVp")) != 0) {
1088 switch (c) {
1089 case 'v': v_flag = 1; break;
1090 case 'V': V_flag = 1; break;
1091 case 'p': p_flag = 1; break;
1092 }
1093 }
1094
1095 if (p_flag && (v_flag || V_flag))
1096 error("cannot specify -p with -v or -V");
1097
1098 while ((arg = *argptr++)) {
1099 if (!v_flag)
1100 out1str(arg);
1101 /* First look at the keywords */
1102 for (pp = parsekwd; *pp; pp++)
1103 if (**pp == *arg && equal(*pp, arg))
1104 break;
1105
1106 if (*pp) {
1107 if (v_flag)
1108 err = 1;
1109 else
1110 out1str(" is a shell keyword\n");
1111 continue;
1112 }
1113
1114 /* Then look at the aliases */
1115 if ((ap = lookupalias(arg, 1)) != NULL) {
1116 if (!v_flag)
1117 out1fmt(" is an alias for \n");
1118 out1fmt("%s\n", ap->val);
1119 continue;
1120 }
1121
1122 /* Then check if it is a tracked alias */
1123 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
1124 entry.cmdtype = cmdp->cmdtype;
1125 entry.u = cmdp->param;
1126 } else {
1127 /* Finally use brute force */
1128 find_command(arg, &entry, DO_ABS, pathval());
1129 }
1130
1131 switch (entry.cmdtype) {
1132 case CMDNORMAL: {
1133 if (strchr(arg, '/') == NULL) {
1134 const char *path = pathval();
1135 char *name;
1136 int j = entry.u.index;
1137 do {
1138 name = padvance(&path, arg);
1139 stunalloc(name);
1140 } while (--j >= 0);
1141 if (!v_flag)
1142 out1fmt(" is%s ",
1143 cmdp ? " a tracked alias for" : "");
1144 out1fmt("%s\n", name);
1145 } else {
1146 if (access(arg, X_OK) == 0) {
1147 if (!v_flag)
1148 out1fmt(" is ");
1149 out1fmt("%s\n", arg);
1150 } else {
1151 if (!v_flag)
1152 out1fmt(": %s\n",
1153 strerror(errno));
1154 else
1155 err = 126;
1156 }
1157 }
1158 break;
1159 }
1160 case CMDFUNCTION:
1161 if (!v_flag)
1162 out1str(" is a shell function\n");
1163 else
1164 out1fmt("%s\n", arg);
1165 break;
1166
1167 case CMDBUILTIN:
1168 if (!v_flag)
1169 out1str(" is a shell builtin\n");
1170 else
1171 out1fmt("%s\n", arg);
1172 break;
1173
1174 case CMDSPLBLTIN:
1175 if (!v_flag)
1176 out1str(" is a special shell builtin\n");
1177 else
1178 out1fmt("%s\n", arg);
1179 break;
1180
1181 default:
1182 if (!v_flag)
1183 out1str(": not found\n");
1184 err = 127;
1185 break;
1186 }
1187 }
1188 return err;
1189}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette