source: trunk/ash/exec.c@ 3604

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

tabs!

File size: 28.7 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#include <sys/cdefs.h>
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
39#else
40__RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $");
41#endif
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/wait.h>
47#include <unistd.h>
48#include <fcntl.h>
49#include <errno.h>
50#include <stdio.h>
51#include <stdlib.h>
52
53/*
54 * When commands are first encountered, they are entered in a hash table.
55 * This ensures that a full path search will not have to be done for them
56 * on each invocation.
57 *
58 * We should investigate converting to a linear search, even though that
59 * would make the command name "hash" a misnomer.
60 */
61
62#include "shell.h"
63#include "main.h"
64#include "nodes.h"
65#include "parser.h"
66#include "redir.h"
67#include "eval.h"
68#include "exec.h"
69#include "builtins.h"
70#include "var.h"
71#include "options.h"
72#include "input.h"
73#include "output.h"
74#include "syntax.h"
75#include "memalloc.h"
76#include "error.h"
77#include "init.h"
78#include "mystring.h"
79#include "show.h"
80#include "jobs.h"
81#include "alias.h"
82#ifdef __INNOTEK_LIBC__
83#include <InnoTekLIBC/backend.h>
84#endif
85
86
87#define CMDTABLESIZE 31 /* should be prime */
88#define ARB 1 /* actual size determined at run time */
89
90
91
92struct tblentry {
93 struct tblentry *next; /* next entry in hash chain */
94 union param param; /* definition of builtin function */
95 short cmdtype; /* index identifying command */
96 char rehash; /* if set, cd done since entry created */
97 char cmdname[ARB]; /* name of command */
98};
99
100
101STATIC struct tblentry *cmdtable[CMDTABLESIZE];
102STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
103int exerrno = 0; /* Last exec error */
104
105
106STATIC void tryexec(char *, char **, char **, int, int);
107STATIC void execinterp(char **, char **);
108STATIC void printentry(struct tblentry *, int);
109STATIC void clearcmdentry(int);
110STATIC struct tblentry *cmdlookup(const char *, int);
111STATIC void delete_cmd_entry(void);
112#ifdef PC_EXE_EXTS
113STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext);
114#endif
115
116
117extern char *const parsekwd[];
118
119/*
120 * Exec a program. Never returns. If you change this routine, you may
121 * have to change the find_command routine as well.
122 */
123
124void
125shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
126{
127 char *cmdname;
128 int e;
129#ifdef PC_EXE_EXTS
130 int has_ext = strlen(argv[0]) - 4;
131 has_ext = has_ext > 0
132 && argv[0][has_ext] == '.'
133 /* use strstr and upper/lower permuated extensions to avoid multiple strcasecmp calls. */
134 && strstr("exe;" "Exe;" "EXe;" "EXE;" "ExE;" "eXe;" "eXE;" "exE;"
135 "cmd;" "Cmd;" "CMd;" "CMD;" "CmD;" "cMd;" "cMD;" "cmD;"
136 "com;" "Com;" "COm;" "COM;" "CoM;" "cOm;" "cOM;" "coM;"
137 "bat;" "Bat;" "BAt;" "BAT;" "BaT;" "bAt;" "bAT;" "baT;"
138 "btm;" "Btm;" "BTm;" "BTM;" "BtM;" "bTm;" "bTM;" "btM;",
139 argv[0] + has_ext + 1)
140 != NULL;
141#else
142 const int has_ext = 1;
143#endif
144 TRACE(("shellexec: argv[0]=%s idx=%d\n", argv[0], idx));
145 if (strchr(argv[0], '/') != NULL) {
146 cmdname = stalloc(strlen(argv[0]) + 5);
147 strcpy(cmdname, argv[0]);
148 tryexec(cmdname, argv, envp, vforked, has_ext);
149 TRACE(("shellexec: cmdname=%s\n", cmdname));
150 stunalloc(cmdname);
151 e = errno;
152 } else {
153 e = ENOENT;
154 while ((cmdname = padvance(&path, argv[0])) != NULL) {
155 if (--idx < 0 && pathopt == NULL) {
156 tryexec(cmdname, argv, envp, vforked, has_ext);
157 if (errno != ENOENT && errno != ENOTDIR)
158 e = errno;
159 }
160 stunalloc(cmdname);
161 }
162 }
163
164 /* Map to POSIX errors */
165 switch (e) {
166 case EACCES:
167 exerrno = 126;
168 break;
169 case ENOENT:
170 exerrno = 127;
171 break;
172 default:
173 exerrno = 2;
174 break;
175 }
176 TRACE(("shellexec failed for '%s', errno %d, vforked %d, suppressint %d\n",
177 argv[0], e, vforked, suppressint ));
178 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
179 /* NOTREACHED */
180}
181
182
183STATIC void
184tryexec(char *cmd, char **argv, char **envp, int vforked, int has_ext)
185{
186 int e;
187#ifdef EXEC_HASH_BANG_SCRIPT
188 char *p;
189#endif
190#ifdef PC_EXE_EXTS
191 /* exploit the effect of stat_pc_exec_exts which adds the
192 * correct extentions to the file.
193 */
194 struct stat st;
195 if (!has_ext)
196 stat_pc_exec_exts(cmd, &st, 0);
197#endif
198#if defined __INNOTEK_LIBC__ && defined EXEC_HASH_BANG_SCRIPT
199 __libc_Back_gfProcessHandleHashBangScripts = 0;
200#endif
201
202#ifdef SYSV
203 do {
204 execve(cmd, argv, envp);
205 } while (errno == EINTR);
206#else
207 execve(cmd, argv, envp);
208#endif
209 e = errno;
210 if (e == ENOEXEC) {
211 if (vforked) {
212 /* We are currently vfork(2)ed, so raise an
213 * exception, and evalcommand will try again
214 * with a normal fork(2).
215 */
216 exraise(EXSHELLPROC);
217 }
218 initshellproc();
219 setinputfile(cmd, 0);
220 commandname = arg0 = savestr(argv[0]);
221#ifdef EXEC_HASH_BANG_SCRIPT
222 pgetc(); pungetc(); /* fill up input buffer */
223 p = parsenextc;
224 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
225 argv[0] = cmd;
226 execinterp(argv, envp);
227 }
228#endif
229 setparam(argv + 1);
230 exraise(EXSHELLPROC);
231 }
232#ifdef __OS2__
233 /* A lot of scripts references some core /bin/ programs, like for
234 instance the bash testcases. So, to make life easier, we map
235 a small set of these to @unixroot and or the PATH to simplify
236 porting. */
237 else if (!strncmp(cmd, "/bin/", sizeof("/bin/") - 1)) {
238 char *name = cmd+ sizeof("/bin/") - 1;
239 int search_path = 1;
240 if (!strcmp(name, "sh")
241 || !strcmp(name, "sh.exe")
242 || !strcmp(name, "bash")
243 || !strcmp(name, "bash.exe")
244 || !strcmp(name, "ash")
245 || !strcmp(name, "ash.exe")
246 || !strcmp(name, "ls")
247 || !strcmp(name, "ls.exe")
248 || !strcmp(name, "cat")
249 || !strcmp(name, "cat.exe")
250 || !strcmp(name, "mkdir")
251 || !strcmp(name, "mkdir.exe")
252 || !strcmp(name, "expr")
253 || !strcmp(name, "expr.exe")
254 || !strcmp(name, "pwd")
255 || !strcmp(name, "pwd.exe")
256 || !strcmp(name, "tr")
257 || !strcmp(name, "tr.exe")
258 || !strcmp(name, "test")
259 || !strcmp(name, "test.exe")
260 || !strcmp(name, "echo")
261 || !strcmp(name, "echo.exe")
262 || (search_path = 0)
263 || !strcmp(name, "sort")
264 || !strcmp(name, "sort.exe")
265 || !strcmp(name, "cp")
266 || !strcmp(name, "cp.exe")
267 || !strcmp(name, "rm")
268 || !strcmp(name, "rm.exe")
269 || !strcmp(name, "unlink")
270 || !strcmp(name, "unlink.exe")
271 || !strcmp(name, "mv")
272 || !strcmp(name, "mv.exe")
273 || !strcmp(name, "rmdir")
274 || !strcmp(name, "rmdir.exe")
275 ) {
276 char tmp[48];
277 strcat(strcpy(tmp, "/@unixroot"), cmd);
278 execve(tmp, argv, envp);
279 if (search_path) {
280 strcpy(tmp, name);
281 if (!strchr(name, '.'))
282 strcat(tmp, ".exe");
283 execvpe(tmp, argv, envp);
284 }
285 }
286 }
287#endif
288 errno = e;
289}
290
291
292#ifdef EXEC_HASH_BANG_SCRIPT
293/*
294 * Execute an interpreter introduced by "#!", for systems where this
295 * feature has not been built into the kernel. If the interpreter is
296 * the shell, return (effectively ignoring the "#!"). If the execution
297 * of the interpreter fails, exit.
298 *
299 * This code peeks inside the input buffer in order to avoid actually
300 * reading any input. It would benefit from a rewrite.
301 */
302
303#define NEWARGS 5
304
305STATIC void
306execinterp(char **argv, char **envp)
307{
308 int n;
309 char *inp;
310 char *outp;
311 char c;
312 char *p;
313 char **ap;
314 char *newargs[NEWARGS];
315 int i;
316 char **ap2;
317 char **new;
318
319 n = parsenleft - 2;
320 inp = parsenextc + 2;
321 ap = newargs;
322 for (;;) {
323 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
324 inp++;
325 if (n < 0)
326 goto bad;
327 if ((c = *inp++) == '\n')
328 break;
329 if (ap == &newargs[NEWARGS])
330bad: error("Bad #! line");
331 STARTSTACKSTR(outp);
332 do {
333 STPUTC(c, outp);
334 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
335 STPUTC('\0', outp);
336 n++, inp--;
337 *ap++ = grabstackstr(outp);
338 }
339 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
340 p = newargs[0];
341 for (;;) {
342 if (equal(p, "sh") || equal(p, "ash")) {
343 TRACE(("hash bang self\n"));
344 return;
345 }
346 while (*p != '/') {
347 if (*p == '\0')
348 goto break2;
349 p++;
350 }
351 p++;
352 }
353break2:;
354 }
355 i = (char *)ap - (char *)newargs; /* size in bytes */
356 if (i == 0)
357 error("Bad #! line");
358 for (ap2 = argv ; *ap2++ != NULL ; );
359 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
360 ap = newargs, ap2 = new;
361 while ((i -= sizeof (char **)) >= 0)
362 *ap2++ = *ap++;
363 ap = argv;
364 while (*ap2++ = *ap++);
365 TRACE(("hash bang '%s'\n", new[0]));
366 shellexec(new, envp, pathval(), 0, 0);
367 /* NOTREACHED */
368}
369#endif
370
371
372
373/*
374 * Do a path search. The variable path (passed by reference) should be
375 * set to the start of the path before the first call; padvance will update
376 * this value as it proceeds. Successive calls to padvance will return
377 * the possible path expansions in sequence. If an option (indicated by
378 * a percent sign) appears in the path entry then the global variable
379 * pathopt will be set to point to it; otherwise pathopt will be set to
380 * NULL.
381 */
382
383const char *pathopt;
384
385char *
386padvance(const char **path, const char *name)
387{
388 const char *p;
389 char *q;
390#ifdef PC_SLASHES
391 char *s;
392#endif
393 const char *start;
394 int len;
395
396 if (*path == NULL)
397 return NULL;
398 start = *path;
399#ifdef PC_PATH_SEP
400 for (p = start ; *p && *p != ';' && *p != '%' ; p++);
401#else
402 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
403#endif
404 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
405#ifdef PC_EXE_EXTS
406 len += 4; /* "4" is for .exe/.com/.cmd/.bat/.btm */
407#endif
408 while (stackblocksize() < len)
409 growstackblock();
410#ifdef PC_SLASHES
411 s =
412#endif
413 q = stackblock();
414 if (p != start) {
415 memcpy(q, start, p - start);
416 q += p - start;
417 *q++ = '/';
418 }
419 strcpy(q, name);
420#ifdef PC_SLASHES
421 while ((s = strchr(s, '\\')) != NULL)
422 *s++ = '/';
423#endif
424 pathopt = NULL;
425 if (*p == '%') {
426 pathopt = ++p;
427#ifdef PC_PATH_SEP
428 while (*p && *p != ';') p++;
429#else
430 while (*p && *p != ':') p++;
431#endif
432 }
433#ifdef PC_PATH_SEP
434 if (*p == ';')
435#else
436 if (*p == ':')
437#endif
438 *path = p + 1;
439 else
440 *path = NULL;
441 return stalloc(len);
442}
443
444
445#ifdef PC_EXE_EXTS
446STATIC int stat_pc_exec_exts(char *fullname, struct stat *st, int has_ext)
447{
448 /* skip the SYSV crap */
449 if (stat(fullname, st) >= 0)
450 return 0;
451 if (!has_ext && errno == ENOENT)
452 {
453 char *psz = strchr(fullname, '\0');
454 memcpy(psz, ".exe", 5);
455 if (stat(fullname, st) >= 0)
456 return 0;
457 if (errno != ENOENT && errno != ENOTDIR)
458 return -1;
459
460 memcpy(psz, ".cmd", 5);
461 if (stat(fullname, st) >= 0)
462 return 0;
463 if (errno != ENOENT && errno != ENOTDIR)
464 return -1;
465
466 memcpy(psz, ".bat", 5);
467 if (stat(fullname, st) >= 0)
468 return 0;
469 if (errno != ENOENT && errno != ENOTDIR)
470 return -1;
471
472 memcpy(psz, ".com", 5);
473 if (stat(fullname, st) >= 0)
474 return 0;
475 if (errno != ENOENT && errno != ENOTDIR)
476 return -1;
477
478 memcpy(psz, ".btm", 5);
479 if (stat(fullname, st) >= 0)
480 return 0;
481 *psz = '\0';
482 }
483 return -1;
484}
485#endif /* PC_EXE_EXTS */
486
487
488
489/*** Command hashing code ***/
490
491
492int
493hashcmd(int argc, char **argv)
494{
495 struct tblentry **pp;
496 struct tblentry *cmdp;
497 int c;
498 int verbose;
499 struct cmdentry entry;
500 char *name;
501
502 verbose = 0;
503 while ((c = nextopt("rv")) != '\0') {
504 if (c == 'r') {
505 clearcmdentry(0);
506 } else if (c == 'v') {
507 verbose++;
508 }
509 }
510 if (*argptr == NULL) {
511 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
512 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
513 if (verbose || cmdp->cmdtype == CMDNORMAL)
514 printentry(cmdp, verbose);
515 }
516 }
517 return 0;
518 }
519 while ((name = *argptr) != NULL) {
520 if ((cmdp = cmdlookup(name, 0)) != NULL
521 && (cmdp->cmdtype == CMDNORMAL
522 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
523 delete_cmd_entry();
524 find_command(name, &entry, DO_ERR, pathval());
525 if (verbose) {
526 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
527 cmdp = cmdlookup(name, 0);
528 printentry(cmdp, verbose);
529 }
530 output_flushall();
531 }
532 argptr++;
533 }
534 return 0;
535}
536
537
538STATIC void
539printentry(struct tblentry *cmdp, int verbose)
540{
541 int idx;
542 const char *path;
543 char *name;
544
545 switch (cmdp->cmdtype) {
546 case CMDNORMAL:
547 idx = cmdp->param.index;
548 path = pathval();
549 do {
550 name = padvance(&path, cmdp->cmdname);
551 stunalloc(name);
552 } while (--idx >= 0);
553 out1str(name);
554 break;
555 case CMDSPLBLTIN:
556 out1fmt("special builtin %s", cmdp->cmdname);
557 break;
558 case CMDBUILTIN:
559 out1fmt("builtin %s", cmdp->cmdname);
560 break;
561 case CMDFUNCTION:
562 out1fmt("function %s", cmdp->cmdname);
563 if (verbose) {
564 struct procstat ps;
565 INTOFF;
566 commandtext(&ps, cmdp->param.func);
567 INTON;
568 out1str("() { ");
569 out1str(ps.cmd);
570 out1str("; }");
571 }
572 break;
573 default:
574 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
575 }
576 if (cmdp->rehash)
577 out1c('*');
578 out1c('\n');
579}
580
581
582
583/*
584 * Resolve a command name. If you change this routine, you may have to
585 * change the shellexec routine as well.
586 */
587
588void
589find_command(char *name, struct cmdentry *entry, int act, const char *path)
590{
591 struct tblentry *cmdp, loc_cmd;
592 int idx;
593 int prev;
594 char *fullname;
595 struct stat statb;
596 int e;
597 int (*bltin)(int,char **);
598
599#ifdef PC_EXE_EXTS
600 int has_ext = strlen(name) - 4;
601 has_ext = has_ext > 0
602 && name[has_ext] == '.'
603 /* use strstr and upper/lower permuated extensions to avoid multiple strcasecmp calls. */
604 && strstr("exe;" "Exe;" "EXe;" "EXE;" "ExE;" "eXe;" "eXE;" "exE;"
605 "cmd;" "Cmd;" "CMd;" "CMD;" "CmD;" "cMd;" "cMD;" "cmD;"
606 "com;" "Com;" "COm;" "COM;" "CoM;" "cOm;" "cOM;" "coM;"
607 "bat;" "Bat;" "BAt;" "BAT;" "BaT;" "bAt;" "bAT;" "baT;"
608 "btm;" "Btm;" "BTm;" "BTM;" "BtM;" "bTm;" "bTM;" "btM;",
609 name + has_ext + 1)
610 != NULL;
611#endif
612
613 /* If name contains a slash, don't use PATH or hash table */
614 if (strchr(name, '/') != NULL) {
615 if (act & DO_ABS) {
616 while (stat(name, &statb) < 0) {
617#ifdef SYSV
618 if (errno == EINTR)
619 continue;
620#endif
621 if (errno != ENOENT && errno != ENOTDIR)
622 e = errno;
623 entry->cmdtype = CMDUNKNOWN;
624 entry->u.index = -1;
625 return;
626 }
627 entry->cmdtype = CMDNORMAL;
628 entry->u.index = -1;
629 return;
630 }
631 entry->cmdtype = CMDNORMAL;
632 entry->u.index = 0;
633 return;
634 }
635
636 if (path != pathval())
637 act |= DO_ALTPATH;
638
639 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
640 act |= DO_ALTBLTIN;
641
642 /* If name is in the table, check answer will be ok */
643 if ((cmdp = cmdlookup(name, 0)) != NULL) {
644 do {
645 switch (cmdp->cmdtype) {
646 case CMDNORMAL:
647 if (act & DO_ALTPATH) {
648 cmdp = NULL;
649 continue;
650 }
651 break;
652 case CMDFUNCTION:
653 if (act & DO_NOFUNC) {
654 cmdp = NULL;
655 continue;
656 }
657 break;
658 case CMDBUILTIN:
659 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
660 cmdp = NULL;
661 continue;
662 }
663 break;
664 }
665 /* if not invalidated by cd, we're done */
666 if (cmdp->rehash == 0)
667 goto success;
668 } while (0);
669 }
670
671 /* If %builtin not in path, check for builtin next */
672 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
673 (bltin = find_builtin(name)) != 0)
674 goto builtin_success;
675
676 /* We have to search path. */
677 prev = -1; /* where to start */
678 if (cmdp) { /* doing a rehash */
679 if (cmdp->cmdtype == CMDBUILTIN)
680 prev = builtinloc;
681 else
682 prev = cmdp->param.index;
683 }
684
685 e = ENOENT;
686 idx = -1;
687loop:
688 while ((fullname = padvance(&path, name)) != NULL) {
689 stunalloc(fullname);
690 idx++;
691 if (pathopt) {
692 if (prefix("builtin", pathopt)) {
693 if ((bltin = find_builtin(name)) == 0)
694 goto loop;
695 goto builtin_success;
696 } else if (prefix("func", pathopt)) {
697 /* handled below */
698 } else {
699 /* ignore unimplemented options */
700 goto loop;
701 }
702 }
703 /* if rehash, don't redo absolute path names */
704 if (fullname[0] == '/' && idx <= prev) {
705 if (idx < prev)
706 goto loop;
707 TRACE(("searchexec \"%s\": no change\n", name));
708 goto success;
709 }
710#ifdef PC_EXE_EXTS
711 while (stat_pc_exec_exts(fullname, &statb, has_ext) < 0) {
712#else
713 while (stat(fullname, &statb) < 0) {
714#endif
715#ifdef SYSV
716 if (errno == EINTR)
717 continue;
718#endif
719 if (errno != ENOENT && errno != ENOTDIR)
720 e = errno;
721
722 goto loop;
723 }
724 e = EACCES; /* if we fail, this will be the error */
725 if (!S_ISREG(statb.st_mode))
726 goto loop;
727 if (pathopt) { /* this is a %func directory */
728 if (act & DO_NOFUNC)
729 goto loop;
730 stalloc(strlen(fullname) + 1);
731 readcmdfile(fullname);
732 if ((cmdp = cmdlookup(name, 0)) == NULL ||
733 cmdp->cmdtype != CMDFUNCTION)
734 error("%s not defined in %s", name, fullname);
735 stunalloc(fullname);
736 goto success;
737 }
738#ifdef notdef
739 /* XXX this code stops root executing stuff, and is buggy
740 if you need a group from the group list. */
741 if (statb.st_uid == geteuid()) {
742 if ((statb.st_mode & 0100) == 0)
743 goto loop;
744 } else if (statb.st_gid == getegid()) {
745 if ((statb.st_mode & 010) == 0)
746 goto loop;
747 } else {
748 if ((statb.st_mode & 01) == 0)
749 goto loop;
750 }
751#endif
752 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
753 INTOFF;
754 if (act & DO_ALTPATH) {
755 stalloc(strlen(fullname) + 1);
756 cmdp = &loc_cmd;
757 } else
758 cmdp = cmdlookup(name, 1);
759 cmdp->cmdtype = CMDNORMAL;
760 cmdp->param.index = idx;
761 INTON;
762 goto success;
763 }
764
765 /* We failed. If there was an entry for this command, delete it */
766 if (cmdp)
767 delete_cmd_entry();
768 if (act & DO_ERR)
769 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
770 entry->cmdtype = CMDUNKNOWN;
771 return;
772
773builtin_success:
774 INTOFF;
775 if (act & DO_ALTPATH)
776 cmdp = &loc_cmd;
777 else
778 cmdp = cmdlookup(name, 1);
779 if (cmdp->cmdtype == CMDFUNCTION)
780 /* DO_NOFUNC must have been set */
781 cmdp = &loc_cmd;
782 cmdp->cmdtype = CMDBUILTIN;
783 cmdp->param.bltin = bltin;
784 INTON;
785success:
786 cmdp->rehash = 0;
787 entry->cmdtype = cmdp->cmdtype;
788 entry->u = cmdp->param;
789}
790
791
792
793/*
794 * Search the table of builtin commands.
795 */
796
797int
798(*find_builtin(name))(int, char **)
799 char *name;
800{
801 const struct builtincmd *bp;
802
803 for (bp = builtincmd ; bp->name ; bp++) {
804 if (*bp->name == *name && equal(bp->name, name))
805 return bp->builtin;
806 }
807 return 0;
808}
809
810int
811(*find_splbltin(name))(int, char **)
812 char *name;
813{
814 const struct builtincmd *bp;
815
816 for (bp = splbltincmd ; bp->name ; bp++) {
817 if (*bp->name == *name && equal(bp->name, name))
818 return bp->builtin;
819 }
820 return 0;
821}
822
823/*
824 * At shell startup put special builtins into hash table.
825 * ensures they are executed first (see posix).
826 * We stop functions being added with the same name
827 * (as they are impossible to call)
828 */
829
830void
831hash_special_builtins(void)
832{
833 const struct builtincmd *bp;
834 struct tblentry *cmdp;
835
836 for (bp = splbltincmd ; bp->name ; bp++) {
837 cmdp = cmdlookup(bp->name, 1);
838 cmdp->cmdtype = CMDSPLBLTIN;
839 cmdp->param.bltin = bp->builtin;
840 }
841}
842
843
844
845/*
846 * Called when a cd is done. Marks all commands so the next time they
847 * are executed they will be rehashed.
848 */
849
850void
851hashcd(void)
852{
853 struct tblentry **pp;
854 struct tblentry *cmdp;
855
856 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
857 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
858 if (cmdp->cmdtype == CMDNORMAL
859 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
860 cmdp->rehash = 1;
861 }
862 }
863}
864
865
866
867/*
868 * Fix command hash table when PATH changed.
869 * Called before PATH is changed. The argument is the new value of PATH;
870 * pathval() still returns the old value at this point.
871 * Called with interrupts off.
872 */
873
874void
875changepath(const char *newval)
876{
877 const char *old, *new;
878 int idx;
879 int firstchange;
880 int bltin;
881
882 old = pathval();
883 new = newval;
884 firstchange = 9999; /* assume no change */
885 idx = 0;
886 bltin = -1;
887 for (;;) {
888 if (*old != *new) {
889 firstchange = idx;
890#ifdef PC_PATH_SEP
891 if ((*old == '\0' && *new == ';')
892 || (*old == ';' && *new == '\0'))
893#else
894 if ((*old == '\0' && *new == ':')
895 || (*old == ':' && *new == '\0'))
896#endif
897 firstchange++;
898 old = new; /* ignore subsequent differences */
899 }
900 if (*new == '\0')
901 break;
902 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
903 bltin = idx;
904#ifdef PC_PATH_SEP
905 if (*new == ';') {
906#else
907 if (*new == ':') {
908#endif
909 idx++;
910 }
911 new++, old++;
912 }
913 if (builtinloc < 0 && bltin >= 0)
914 builtinloc = bltin; /* zap builtins */
915 if (builtinloc >= 0 && bltin < 0)
916 firstchange = 0;
917 clearcmdentry(firstchange);
918 builtinloc = bltin;
919}
920
921
922/*
923 * Clear out command entries. The argument specifies the first entry in
924 * PATH which has changed.
925 */
926
927STATIC void
928clearcmdentry(int firstchange)
929{
930 struct tblentry **tblp;
931 struct tblentry **pp;
932 struct tblentry *cmdp;
933
934 INTOFF;
935 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
936 pp = tblp;
937 while ((cmdp = *pp) != NULL) {
938 if ((cmdp->cmdtype == CMDNORMAL &&
939 cmdp->param.index >= firstchange)
940 || (cmdp->cmdtype == CMDBUILTIN &&
941 builtinloc >= firstchange)) {
942 *pp = cmdp->next;
943 ckfree(cmdp);
944 } else {
945 pp = &cmdp->next;
946 }
947 }
948 }
949 INTON;
950}
951
952
953/*
954 * Delete all functions.
955 */
956
957#ifdef mkinit
958MKINIT void deletefuncs(void);
959MKINIT void hash_special_builtins(void);
960
961INIT {
962 hash_special_builtins();
963}
964
965SHELLPROC {
966 deletefuncs();
967}
968#endif
969
970void
971deletefuncs(void)
972{
973 struct tblentry **tblp;
974 struct tblentry **pp;
975 struct tblentry *cmdp;
976
977 INTOFF;
978 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
979 pp = tblp;
980 while ((cmdp = *pp) != NULL) {
981 if (cmdp->cmdtype == CMDFUNCTION) {
982 *pp = cmdp->next;
983 freefunc(cmdp->param.func);
984 ckfree(cmdp);
985 } else {
986 pp = &cmdp->next;
987 }
988 }
989 }
990 INTON;
991}
992
993
994
995/*
996 * Locate a command in the command hash table. If "add" is nonzero,
997 * add the command to the table if it is not already present. The
998 * variable "lastcmdentry" is set to point to the address of the link
999 * pointing to the entry, so that delete_cmd_entry can delete the
1000 * entry.
1001 */
1002
1003struct tblentry **lastcmdentry;
1004
1005
1006STATIC struct tblentry *
1007cmdlookup(const char *name, int add)
1008{
1009 int hashval;
1010 const char *p;
1011 struct tblentry *cmdp;
1012 struct tblentry **pp;
1013
1014 p = name;
1015 hashval = *p << 4;
1016 while (*p)
1017 hashval += *p++;
1018 hashval &= 0x7FFF;
1019 pp = &cmdtable[hashval % CMDTABLESIZE];
1020 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
1021 if (equal(cmdp->cmdname, name))
1022 break;
1023 pp = &cmdp->next;
1024 }
1025 if (add && cmdp == NULL) {
1026 INTOFF;
1027 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
1028 + strlen(name) + 1);
1029 cmdp->next = NULL;
1030 cmdp->cmdtype = CMDUNKNOWN;
1031 cmdp->rehash = 0;
1032 strcpy(cmdp->cmdname, name);
1033 INTON;
1034 }
1035 lastcmdentry = pp;
1036 return cmdp;
1037}
1038
1039/*
1040 * Delete the command entry returned on the last lookup.
1041 */
1042
1043STATIC void
1044delete_cmd_entry(void)
1045{
1046 struct tblentry *cmdp;
1047
1048 INTOFF;
1049 cmdp = *lastcmdentry;
1050 *lastcmdentry = cmdp->next;
1051 ckfree(cmdp);
1052 INTON;
1053}
1054
1055
1056
1057#ifdef notdef
1058void
1059getcmdentry(char *name, struct cmdentry *entry)
1060{
1061 struct tblentry *cmdp = cmdlookup(name, 0);
1062
1063 if (cmdp) {
1064 entry->u = cmdp->param;
1065 entry->cmdtype = cmdp->cmdtype;
1066 } else {
1067 entry->cmdtype = CMDUNKNOWN;
1068 entry->u.index = 0;
1069 }
1070}
1071#endif
1072
1073
1074/*
1075 * Add a new command entry, replacing any existing command entry for
1076 * the same name - except special builtins.
1077 */
1078
1079STATIC void
1080addcmdentry(char *name, struct cmdentry *entry)
1081{
1082 struct tblentry *cmdp;
1083
1084 INTOFF;
1085 cmdp = cmdlookup(name, 1);
1086 if (cmdp->cmdtype != CMDSPLBLTIN) {
1087 if (cmdp->cmdtype == CMDFUNCTION) {
1088 freefunc(cmdp->param.func);
1089 }
1090 cmdp->cmdtype = entry->cmdtype;
1091 cmdp->param = entry->u;
1092 }
1093 INTON;
1094}
1095
1096
1097/*
1098 * Define a shell function.
1099 */
1100
1101void
1102defun(char *name, union node *func)
1103{
1104 struct cmdentry entry;
1105
1106 INTOFF;
1107 entry.cmdtype = CMDFUNCTION;
1108 entry.u.func = copyfunc(func);
1109 addcmdentry(name, &entry);
1110 INTON;
1111}
1112
1113
1114/*
1115 * Delete a function if it exists.
1116 */
1117
1118int
1119unsetfunc(char *name)
1120{
1121 struct tblentry *cmdp;
1122
1123 if ((cmdp = cmdlookup(name, 0)) != NULL &&
1124 cmdp->cmdtype == CMDFUNCTION) {
1125 freefunc(cmdp->param.func);
1126 delete_cmd_entry();
1127 return (0);
1128 }
1129 return (1);
1130}
1131
1132/*
1133 * Locate and print what a word is...
1134 * also used for 'command -[v|V]'
1135 */
1136
1137int
1138typecmd(int argc, char **argv)
1139{
1140 struct cmdentry entry;
1141 struct tblentry *cmdp;
1142 char * const *pp;
1143 struct alias *ap;
1144 int err = 0;
1145 char *arg;
1146 int c;
1147 int V_flag = 0;
1148 int v_flag = 0;
1149 int p_flag = 0;
1150
1151 while ((c = nextopt("vVp")) != 0) {
1152 switch (c) {
1153 case 'v': v_flag = 1; break;
1154 case 'V': V_flag = 1; break;
1155 case 'p': p_flag = 1; break;
1156 }
1157 }
1158
1159 if (p_flag && (v_flag || V_flag))
1160 error("cannot specify -p with -v or -V");
1161
1162 while ((arg = *argptr++)) {
1163 if (!v_flag)
1164 out1str(arg);
1165 /* First look at the keywords */
1166 for (pp = parsekwd; *pp; pp++)
1167 if (**pp == *arg && equal(*pp, arg))
1168 break;
1169
1170 if (*pp) {
1171 if (v_flag)
1172 err = 1;
1173 else
1174 out1str(" is a shell keyword\n");
1175 continue;
1176 }
1177
1178 /* Then look at the aliases */
1179 if ((ap = lookupalias(arg, 1)) != NULL) {
1180 if (!v_flag)
1181 out1fmt(" is an alias for \n");
1182 out1fmt("%s\n", ap->val);
1183 continue;
1184 }
1185
1186 /* Then check if it is a tracked alias */
1187 if ((cmdp = cmdlookup(arg, 0)) != NULL) {
1188 entry.cmdtype = cmdp->cmdtype;
1189 entry.u = cmdp->param;
1190 } else {
1191 /* Finally use brute force */
1192 find_command(arg, &entry, DO_ABS, pathval());
1193 }
1194
1195 switch (entry.cmdtype) {
1196 case CMDNORMAL: {
1197 if (strchr(arg, '/') == NULL) {
1198 const char *path = pathval();
1199 char *name;
1200 int j = entry.u.index;
1201 do {
1202 name = padvance(&path, arg);
1203 stunalloc(name);
1204 } while (--j >= 0);
1205 if (!v_flag)
1206 out1fmt(" is%s ",
1207 cmdp ? " a tracked alias for" : "");
1208 out1fmt("%s\n", name);
1209 } else {
1210 if (access(arg, X_OK) == 0) {
1211 if (!v_flag)
1212 out1fmt(" is ");
1213 out1fmt("%s\n", arg);
1214 } else {
1215 if (!v_flag)
1216 out1fmt(": %s\n",
1217 strerror(errno));
1218 else
1219 err = 126;
1220 }
1221 }
1222 break;
1223 }
1224 case CMDFUNCTION:
1225 if (!v_flag)
1226 out1str(" is a shell function\n");
1227 else
1228 out1fmt("%s\n", arg);
1229 break;
1230
1231 case CMDBUILTIN:
1232 if (!v_flag)
1233 out1str(" is a shell builtin\n");
1234 else
1235 out1fmt("%s\n", arg);
1236 break;
1237
1238 case CMDSPLBLTIN:
1239 if (!v_flag)
1240 out1str(" is a special shell builtin\n");
1241 else
1242 out1fmt("%s\n", arg);
1243 break;
1244
1245 default:
1246 if (!v_flag)
1247 out1str(": not found\n");
1248 err = 127;
1249 break;
1250 }
1251 }
1252 return err;
1253}
Note: See TracBrowser for help on using the repository browser.