source: trunk/ash/exec.c@ 3164

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

convert dos slashes to unix slashes found in the PATH as to not upset any shell scripts using $0.

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