VirtualBox

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

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

PC_SLASHES

  • Property svn:eol-style set to native
File size: 25.9 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#ifdef PC_SLASHES
337 char *s;
338#endif
339 const char *start;
340 int len;
341
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
357 s =
358#endif
359 q = stackblock();
360 if (p != start) {
361 memcpy(q, start, p - start);
362 q += p - start;
363 *q++ = '/';
364 }
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
378 }
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);
388}
389
390
391#ifdef PC_EXE_EXTS
392STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext)
393{
394 /* skip the SYSV crap */
395 if (stat(fullname, st) >= 0)
396 return 0;
397 if (!has_ext && errno == ENOENT)
398 {
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;
405
406 memcpy(psz, ".cmd", 5);
407 if (stat(fullname, st) >= 0)
408 return 0;
409 if (errno != ENOENT && errno != ENOTDIR)
410 return -1;
411
412 memcpy(psz, ".bat", 5);
413 if (stat(fullname, st) >= 0)
414 return 0;
415 if (errno != ENOENT && errno != ENOTDIR)
416 return -1;
417
418 memcpy(psz, ".com", 5);
419 if (stat(fullname, st) >= 0)
420 return 0;
421 if (errno != ENOENT && errno != ENOTDIR)
422 return -1;
423
424 memcpy(psz, ".btm", 5);
425 if (stat(fullname, st) >= 0)
426 return 0;
427 *psz = '\0';
428 }
429 return -1;
430}
431#endif /* PC_EXE_EXTS */
432
433
434
435/*** Command hashing code ***/
436
437
438int
439hashcmd(int argc, char **argv)
440{
441 struct tblentry **pp;
442 struct tblentry *cmdp;
443 int c;
444 int verbose;
445 struct cmdentry entry;
446 char *name;
447
448 verbose = 0;
449 while ((c = nextopt("rv")) != '\0') {
450 if (c == 'r') {
451 clearcmdentry(0);
452 } else if (c == 'v') {
453 verbose++;
454 }
455 }
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);
461 }
462 }
463 return 0;
464 }
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);
475 }
476 output_flushall();
477 }
478 argptr++;
479 }
480 return 0;
481}
482
483
484STATIC void
485printentry(struct tblentry *cmdp, int verbose)
486{
487 int idx;
488 const char *path;
489 char *name;
490
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("; }");
517 }
518 break;
519 default:
520 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
521 }
522 if (cmdp->rehash)
523 out1c('*');
524 out1c('\n');
525}
526
527
528
529/*
530 * Resolve a command name. If you change this routine, you may have to
531 * change the shellexec routine as well.
532 */
533
534void
535find_command(char *name, struct cmdentry *entry, int act, const char *path)
536{
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 **);
544
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
558
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;
572 }
573 entry->cmdtype = CMDNORMAL;
574 entry->u.index = -1;
575 return;
576 }
577 entry->cmdtype = CMDNORMAL;
578 entry->u.index = 0;
579 return;
580 }
581
582 if (path != pathval())
583 act |= DO_ALTPATH;
584
585 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
586 act |= DO_ALTBLTIN;
587
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;
596 }
597 break;
598 case CMDFUNCTION:
599 if (act & DO_NOFUNC) {
600 cmdp = NULL;
601 continue;
602 }
603 break;
604 case CMDBUILTIN:
605 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
606 cmdp = NULL;
607 continue;
608 }
609 break;
610 }
611 /* if not invalidated by cd, we're done */
612 if (cmdp->rehash == 0)
613 goto success;
614 } while (0);
615 }
616
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;
621
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;
629 }
630
631 e = ENOENT;
632 idx = -1;
633loop:
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;
647 }
648 }
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;
655 }
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;
667
668 goto loop;
669 }
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;
683 }
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;
696 }
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;
709 }
710
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;
718
719builtin_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;
731success:
732 cmdp->rehash = 0;
733 entry->cmdtype = cmdp->cmdtype;
734 entry->u = cmdp->param;
735}
736
737
738
739/*
740 * Search the table of builtin commands.
741 */
742
743int
744(*find_builtin(name))(int, char **)
745 char *name;
746{
747 const struct builtincmd *bp;
748
749 for (bp = builtincmd ; bp->name ; bp++) {
750 if (*bp->name == *name && equal(bp->name, name))
751 return bp->builtin;
752 }
753 return 0;
754}
755
756int
757(*find_splbltin(name))(int, char **)
758 char *name;
759{
760 const struct builtincmd *bp;
761
762 for (bp = splbltincmd ; bp->name ; bp++) {
763 if (*bp->name == *name && equal(bp->name, name))
764 return bp->builtin;
765 }
766 return 0;
767}
768
769/*
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)
774 */
775
776void
777hash_special_builtins(void)
778{
779 const struct builtincmd *bp;
780 struct tblentry *cmdp;
781
782 for (bp = splbltincmd ; bp->name ; bp++) {
783 cmdp = cmdlookup(bp->name, 1);
784 cmdp->cmdtype = CMDSPLBLTIN;
785 cmdp->param.bltin = bp->builtin;
786 }
787}
788
789
790
791/*
792 * Called when a cd is done. Marks all commands so the next time they
793 * are executed they will be rehashed.
794 */
795
796void
797hashcd(void)
798{
799 struct tblentry **pp;
800 struct tblentry *cmdp;
801
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;
807 }
808 }
809}
810
811
812
813/*
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.
818 */
819
820void
821changepath(const char *newval)
822{
823 const char *old, *new;
824 int idx;
825 int firstchange;
826 int bltin;
827
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 */
845 }
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++;
856 }
857 new++, old++;
858 }
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;
865}
866
867
868/*
869 * Clear out command entries. The argument specifies the first entry in
870 * PATH which has changed.
871 */
872
873STATIC void
874clearcmdentry(int firstchange)
875{
876 struct tblentry **tblp;
877 struct tblentry **pp;
878 struct tblentry *cmdp;
879
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;
892 }
893 }
894 }
895 INTON;
896}
897
898
899/*
900 * Delete all functions.
901 */
902
903#ifdef mkinit
904MKINIT void deletefuncs(void);
905MKINIT void hash_special_builtins(void);
906
907INIT {
908 hash_special_builtins();
909}
910
911SHELLPROC {
912 deletefuncs();
913}
914#endif
915
916void
917deletefuncs(void)
918{
919 struct tblentry **tblp;
920 struct tblentry **pp;
921 struct tblentry *cmdp;
922
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;
933 }
934 }
935 }
936 INTON;
937}
938
939
940
941/*
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.
947 */
948
949struct tblentry **lastcmdentry;
950
951
952STATIC struct tblentry *
953cmdlookup(const char *name, int add)
954{
955 int hashval;
956 const char *p;
957 struct tblentry *cmdp;
958 struct tblentry **pp;
959
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;
970 }
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;
980 }
981 lastcmdentry = pp;
982 return cmdp;
983}
984
985/*
986 * Delete the command entry returned on the last lookup.
987 */
988
989STATIC void
990delete_cmd_entry(void)
991{
992 struct tblentry *cmdp;
993
994 INTOFF;
995 cmdp = *lastcmdentry;
996 *lastcmdentry = cmdp->next;
997 ckfree(cmdp);
998 INTON;
999}
1000
1001
1002
1003#ifdef notdef
1004void
1005getcmdentry(char *name, struct cmdentry *entry)
1006{
1007 struct tblentry *cmdp = cmdlookup(name, 0);
1008
1009 if (cmdp) {
1010 entry->u = cmdp->param;
1011 entry->cmdtype = cmdp->cmdtype;
1012 } else {
1013 entry->cmdtype = CMDUNKNOWN;
1014 entry->u.index = 0;
1015 }
1016}
1017#endif
1018
1019
1020/*
1021 * Add a new command entry, replacing any existing command entry for
1022 * the same name - except special builtins.
1023 */
1024
1025STATIC void
1026addcmdentry(char *name, struct cmdentry *entry)
1027{
1028 struct tblentry *cmdp;
1029
1030 INTOFF;
1031 cmdp = cmdlookup(name, 1);
1032 if (cmdp->cmdtype != CMDSPLBLTIN) {
1033 if (cmdp->cmdtype == CMDFUNCTION) {
1034 freefunc(cmdp->param.func);
1035 }
1036 cmdp->cmdtype = entry->cmdtype;
1037 cmdp->param = entry->u;
1038 }
1039 INTON;
1040}
1041
1042
1043/*
1044 * Define a shell function.
1045 */
1046
1047void
1048defun(char *name, union node *func)
1049{
1050 struct cmdentry entry;
1051
1052 INTOFF;
1053 entry.cmdtype = CMDFUNCTION;
1054 entry.u.func = copyfunc(func);
1055 addcmdentry(name, &entry);
1056 INTON;
1057}
1058
1059
1060/*
1061 * Delete a function if it exists.
1062 */
1063
1064int
1065unsetfunc(char *name)
1066{
1067 struct tblentry *cmdp;
1068
1069 if ((cmdp = cmdlookup(name, 0)) != NULL &&
1070 cmdp->cmdtype == CMDFUNCTION) {
1071 freefunc(cmdp->param.func);
1072 delete_cmd_entry();
1073 return (0);
1074 }
1075 return (1);
1076}
1077
1078/*
1079 * Locate and print what a word is...
1080 * also used for 'command -[v|V]'
1081 */
1082
1083int
1084typecmd(int argc, char **argv)
1085{
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;
1096
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;
1102 }
1103 }
1104
1105 if (p_flag && (v_flag || V_flag))
1106 error("cannot specify -p with -v or -V");
1107
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;
1115
1116 if (*pp) {
1117 if (v_flag)
1118 err = 1;
1119 else
1120 out1str(" is a shell keyword\n");
1121 continue;
1122 }
1123
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;
1130 }
1131
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());
1139 }
1140
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;
1166 }
1167 }
1168 break;
1169 }
1170 case CMDFUNCTION:
1171 if (!v_flag)
1172 out1str(" is a shell function\n");
1173 else
1174 out1fmt("%s\n", arg);
1175 break;
1176
1177 case CMDBUILTIN:
1178 if (!v_flag)
1179 out1str(" is a shell builtin\n");
1180 else
1181 out1fmt("%s\n", arg);
1182 break;
1183
1184 case CMDSPLBLTIN:
1185 if (!v_flag)
1186 out1str(" is a special shell builtin\n");
1187 else
1188 out1fmt("%s\n", arg);
1189 break;
1190
1191 default:
1192 if (!v_flag)
1193 out1str(": not found\n");
1194 err = 127;
1195 break;
1196 }
1197 }
1198 return err;
1199}
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