Annotation of src/bin/sh/exec.c, Revision 1.35
1.35 ! dsl 1: /* $NetBSD: exec.c,v 1.34 2002/11/24 22:35:39 christos Exp $ */
1.15 cgd 2:
1.1 cgd 3: /*-
1.8 jtc 4: * Copyright (c) 1991, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 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. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
1.23 christos 39: #include <sys/cdefs.h>
1.1 cgd 40: #ifndef lint
1.15 cgd 41: #if 0
1.17 christos 42: static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
1.15 cgd 43: #else
1.35 ! dsl 44: __RCSID("$NetBSD: exec.c,v 1.34 2002/11/24 22:35:39 christos Exp $");
1.15 cgd 45: #endif
1.1 cgd 46: #endif /* not lint */
47:
1.16 christos 48: #include <sys/types.h>
49: #include <sys/stat.h>
1.33 christos 50: #include <sys/wait.h>
1.16 christos 51: #include <unistd.h>
52: #include <fcntl.h>
53: #include <errno.h>
1.33 christos 54: #include <stdio.h>
1.16 christos 55: #include <stdlib.h>
56:
1.1 cgd 57: /*
58: * When commands are first encountered, they are entered in a hash table.
59: * This ensures that a full path search will not have to be done for them
60: * on each invocation.
61: *
62: * We should investigate converting to a linear search, even though that
63: * would make the command name "hash" a misnomer.
64: */
65:
66: #include "shell.h"
67: #include "main.h"
68: #include "nodes.h"
69: #include "parser.h"
70: #include "redir.h"
71: #include "eval.h"
72: #include "exec.h"
73: #include "builtins.h"
74: #include "var.h"
75: #include "options.h"
76: #include "input.h"
77: #include "output.h"
78: #include "syntax.h"
79: #include "memalloc.h"
80: #include "error.h"
81: #include "init.h"
82: #include "mystring.h"
1.16 christos 83: #include "show.h"
1.8 jtc 84: #include "jobs.h"
1.22 christos 85: #include "alias.h"
1.1 cgd 86:
87:
88: #define CMDTABLESIZE 31 /* should be prime */
89: #define ARB 1 /* actual size determined at run time */
90:
91:
92:
93: struct tblentry {
94: struct tblentry *next; /* next entry in hash chain */
95: union param param; /* definition of builtin function */
96: short cmdtype; /* index identifying command */
97: char rehash; /* if set, cd done since entry created */
98: char cmdname[ARB]; /* name of command */
99: };
100:
101:
102: STATIC struct tblentry *cmdtable[CMDTABLESIZE];
103: STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
1.19 christos 104: int exerrno = 0; /* Last exec error */
1.1 cgd 105:
106:
1.34 christos 107: STATIC void tryexec(char *, char **, char **, int);
108: STATIC void execinterp(char **, char **);
109: STATIC void printentry(struct tblentry *, int);
110: STATIC void clearcmdentry(int);
111: STATIC struct tblentry *cmdlookup(const char *, int);
112: STATIC void delete_cmd_entry(void);
1.1 cgd 113:
114:
1.32 christos 115: extern char *const parsekwd[];
1.1 cgd 116:
117: /*
118: * Exec a program. Never returns. If you change this routine, you may
119: * have to change the find_command routine as well.
120: */
121:
122: void
1.34 christos 123: shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
1.12 cgd 124: {
1.1 cgd 125: char *cmdname;
126: int e;
127:
128: if (strchr(argv[0], '/') != NULL) {
1.33 christos 129: tryexec(argv[0], argv, envp, vforked);
1.1 cgd 130: e = errno;
131: } else {
132: e = ENOENT;
133: while ((cmdname = padvance(&path, argv[0])) != NULL) {
1.27 christos 134: if (--idx < 0 && pathopt == NULL) {
1.33 christos 135: tryexec(cmdname, argv, envp, vforked);
1.1 cgd 136: if (errno != ENOENT && errno != ENOTDIR)
137: e = errno;
138: }
139: stunalloc(cmdname);
140: }
141: }
1.19 christos 142:
143: /* Map to POSIX errors */
144: switch (e) {
145: case EACCES:
146: exerrno = 126;
147: break;
148: case ENOENT:
149: exerrno = 127;
150: break;
151: default:
152: exerrno = 2;
153: break;
154: }
1.34 christos 155: TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
156: argv[0], e, vforked, suppressint ));
1.19 christos 157: exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
1.26 mycroft 158: /* NOTREACHED */
1.1 cgd 159: }
160:
161:
162: STATIC void
1.34 christos 163: tryexec(char *cmd, char **argv, char **envp, int vforked)
164: {
1.1 cgd 165: int e;
1.16 christos 166: #ifndef BSD
1.1 cgd 167: char *p;
1.16 christos 168: #endif
1.1 cgd 169:
170: #ifdef SYSV
171: do {
172: execve(cmd, argv, envp);
173: } while (errno == EINTR);
174: #else
175: execve(cmd, argv, envp);
176: #endif
177: e = errno;
178: if (e == ENOEXEC) {
1.33 christos 179: if (vforked) {
180: /* We are currently vfork(2)ed, so raise an
181: * exception, and evalcommand will try again
182: * with a normal fork(2).
183: */
184: exraise(EXSHELLPROC);
185: }
1.1 cgd 186: initshellproc();
187: setinputfile(cmd, 0);
188: commandname = arg0 = savestr(argv[0]);
189: #ifndef BSD
190: pgetc(); pungetc(); /* fill up input buffer */
191: p = parsenextc;
192: if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
193: argv[0] = cmd;
194: execinterp(argv, envp);
195: }
196: #endif
197: setparam(argv + 1);
198: exraise(EXSHELLPROC);
199: }
200: errno = e;
201: }
202:
203:
204: #ifndef BSD
205: /*
206: * Execute an interpreter introduced by "#!", for systems where this
207: * feature has not been built into the kernel. If the interpreter is
208: * the shell, return (effectively ignoring the "#!"). If the execution
209: * of the interpreter fails, exit.
210: *
211: * This code peeks inside the input buffer in order to avoid actually
212: * reading any input. It would benefit from a rewrite.
213: */
214:
215: #define NEWARGS 5
216:
217: STATIC void
1.34 christos 218: execinterp(char **argv, char **envp)
219: {
1.1 cgd 220: int n;
221: char *inp;
222: char *outp;
223: char c;
224: char *p;
225: char **ap;
226: char *newargs[NEWARGS];
227: int i;
228: char **ap2;
229: char **new;
230:
231: n = parsenleft - 2;
232: inp = parsenextc + 2;
233: ap = newargs;
234: for (;;) {
235: while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
236: inp++;
237: if (n < 0)
238: goto bad;
239: if ((c = *inp++) == '\n')
240: break;
241: if (ap == &newargs[NEWARGS])
242: bad: error("Bad #! line");
243: STARTSTACKSTR(outp);
244: do {
245: STPUTC(c, outp);
246: } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
247: STPUTC('\0', outp);
248: n++, inp--;
249: *ap++ = grabstackstr(outp);
250: }
251: if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
252: p = newargs[0];
253: for (;;) {
254: if (equal(p, "sh") || equal(p, "ash")) {
255: return;
256: }
257: while (*p != '/') {
258: if (*p == '\0')
259: goto break2;
260: p++;
261: }
262: p++;
263: }
264: break2:;
265: }
266: i = (char *)ap - (char *)newargs; /* size in bytes */
267: if (i == 0)
268: error("Bad #! line");
269: for (ap2 = argv ; *ap2++ != NULL ; );
270: new = ckmalloc(i + ((char *)ap2 - (char *)argv));
271: ap = newargs, ap2 = new;
272: while ((i -= sizeof (char **)) >= 0)
273: *ap2++ = *ap++;
274: ap = argv;
275: while (*ap2++ = *ap++);
276: shellexec(new, envp, pathval(), 0);
1.26 mycroft 277: /* NOTREACHED */
1.1 cgd 278: }
279: #endif
280:
281:
282:
283: /*
284: * Do a path search. The variable path (passed by reference) should be
285: * set to the start of the path before the first call; padvance will update
286: * this value as it proceeds. Successive calls to padvance will return
287: * the possible path expansions in sequence. If an option (indicated by
288: * a percent sign) appears in the path entry then the global variable
289: * pathopt will be set to point to it; otherwise pathopt will be set to
290: * NULL.
291: */
292:
1.27 christos 293: const char *pathopt;
1.1 cgd 294:
295: char *
1.34 christos 296: padvance(const char **path, const char *name)
297: {
1.27 christos 298: const char *p;
299: char *q;
300: const char *start;
1.1 cgd 301: int len;
302:
303: if (*path == NULL)
304: return NULL;
305: start = *path;
306: for (p = start ; *p && *p != ':' && *p != '%' ; p++);
307: len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
308: while (stackblocksize() < len)
309: growstackblock();
310: q = stackblock();
311: if (p != start) {
1.11 mycroft 312: memcpy(q, start, p - start);
1.1 cgd 313: q += p - start;
314: *q++ = '/';
315: }
316: strcpy(q, name);
317: pathopt = NULL;
318: if (*p == '%') {
319: pathopt = ++p;
320: while (*p && *p != ':') p++;
321: }
322: if (*p == ':')
323: *path = p + 1;
324: else
325: *path = NULL;
326: return stalloc(len);
327: }
328:
329:
330:
331: /*** Command hashing code ***/
332:
333:
1.12 cgd 334: int
1.34 christos 335: hashcmd(int argc, char **argv)
1.12 cgd 336: {
1.1 cgd 337: struct tblentry **pp;
338: struct tblentry *cmdp;
339: int c;
340: int verbose;
341: struct cmdentry entry;
342: char *name;
343:
344: verbose = 0;
345: while ((c = nextopt("rv")) != '\0') {
346: if (c == 'r') {
347: clearcmdentry(0);
348: } else if (c == 'v') {
349: verbose++;
350: }
351: }
1.8 jtc 352: if (*argptr == NULL) {
353: for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
354: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
355: printentry(cmdp, verbose);
356: }
357: }
358: return 0;
359: }
1.1 cgd 360: while ((name = *argptr) != NULL) {
361: if ((cmdp = cmdlookup(name, 0)) != NULL
362: && (cmdp->cmdtype == CMDNORMAL
1.16 christos 363: || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
1.1 cgd 364: delete_cmd_entry();
1.24 christos 365: find_command(name, &entry, DO_ERR, pathval());
1.1 cgd 366: if (verbose) {
367: if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
368: cmdp = cmdlookup(name, 0);
1.8 jtc 369: printentry(cmdp, verbose);
1.1 cgd 370: }
371: flushall();
372: }
373: argptr++;
374: }
375: return 0;
376: }
377:
378:
379: STATIC void
1.34 christos 380: printentry(struct tblentry *cmdp, int verbose)
381: {
1.27 christos 382: int idx;
383: const char *path;
1.1 cgd 384: char *name;
385:
1.34 christos 386: switch (cmdp->cmdtype) {
387: case CMDNORMAL:
1.27 christos 388: idx = cmdp->param.index;
1.1 cgd 389: path = pathval();
390: do {
391: name = padvance(&path, cmdp->cmdname);
392: stunalloc(name);
1.27 christos 393: } while (--idx >= 0);
1.1 cgd 394: out1str(name);
1.34 christos 395: break;
396: case CMDSPLBLTIN:
397: out1fmt("special builtin %s", cmdp->cmdname);
398: break;
399: case CMDBUILTIN:
1.1 cgd 400: out1fmt("builtin %s", cmdp->cmdname);
1.34 christos 401: break;
402: case CMDFUNCTION:
1.1 cgd 403: out1fmt("function %s", cmdp->cmdname);
1.8 jtc 404: if (verbose) {
1.34 christos 405: struct procstat ps;
1.8 jtc 406: INTOFF;
1.34 christos 407: commandtext(&ps, cmdp->param.func);
1.8 jtc 408: out1c(' ');
1.34 christos 409: out1str(ps.cmd);
1.8 jtc 410: INTON;
411: }
1.34 christos 412: break;
413: default:
414: error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
1.1 cgd 415: }
416: if (cmdp->rehash)
417: out1c('*');
418: out1c('\n');
419: }
420:
421:
422:
423: /*
424: * Resolve a command name. If you change this routine, you may have to
425: * change the shellexec routine as well.
426: */
427:
428: void
1.34 christos 429: find_command(char *name, struct cmdentry *entry, int act, const char *path)
1.12 cgd 430: {
1.35 ! dsl 431: struct tblentry *cmdp, loc_cmd;
1.27 christos 432: int idx;
1.1 cgd 433: int prev;
434: char *fullname;
435: struct stat statb;
436: int e;
1.34 christos 437: int (*bltin)(int,char **);
1.1 cgd 438:
1.35 ! dsl 439: /* If name contains a slash, don't use PATH or hash table */
1.1 cgd 440: if (strchr(name, '/') != NULL) {
1.24 christos 441: if (act & DO_ABS) {
442: while (stat(name, &statb) < 0) {
1.34 christos 443: #ifdef SYSV
1.24 christos 444: if (errno == EINTR)
445: continue;
1.34 christos 446: #endif
1.24 christos 447: if (errno != ENOENT && errno != ENOTDIR)
448: e = errno;
449: entry->cmdtype = CMDUNKNOWN;
450: entry->u.index = -1;
451: return;
452: }
453: entry->cmdtype = CMDNORMAL;
454: entry->u.index = -1;
455: return;
456: }
1.1 cgd 457: entry->cmdtype = CMDNORMAL;
458: entry->u.index = 0;
459: return;
460: }
461:
1.35 ! dsl 462: if (path != pathval())
! 463: act |= DO_ALTPATH;
! 464:
! 465: if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
! 466: act |= DO_ALTBLTIN;
! 467:
! 468: /* If name is in the table, check answer will be ok */
! 469: if ((cmdp = cmdlookup(name, 0)) != NULL) {
! 470: do {
! 471: switch (cmdp->cmdtype) {
! 472: case CMDNORMAL:
! 473: if (act & DO_ALTPATH) {
! 474: cmdp = NULL;
! 475: continue;
! 476: }
! 477: break;
! 478: case CMDFUNCTION:
! 479: if (act & DO_NOFUNC) {
! 480: cmdp = NULL;
! 481: continue;
! 482: }
! 483: break;
! 484: case CMDBUILTIN:
! 485: if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
! 486: cmdp = NULL;
! 487: continue;
! 488: }
! 489: break;
! 490: }
! 491: /* if not invalidated by cd, we're done */
! 492: if (cmdp->rehash == 0)
! 493: goto success;
! 494: } while (0);
! 495: }
1.1 cgd 496:
497: /* If %builtin not in path, check for builtin next */
1.35 ! dsl 498: if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
! 499: (bltin = find_builtin(name)) != 0)
! 500: goto builtin_success;
1.1 cgd 501:
502: /* We have to search path. */
503: prev = -1; /* where to start */
504: if (cmdp) { /* doing a rehash */
505: if (cmdp->cmdtype == CMDBUILTIN)
506: prev = builtinloc;
507: else
508: prev = cmdp->param.index;
509: }
510:
511: e = ENOENT;
1.27 christos 512: idx = -1;
1.1 cgd 513: loop:
514: while ((fullname = padvance(&path, name)) != NULL) {
515: stunalloc(fullname);
1.27 christos 516: idx++;
1.1 cgd 517: if (pathopt) {
518: if (prefix("builtin", pathopt)) {
1.34 christos 519: if ((bltin = find_builtin(name)) == 0)
1.1 cgd 520: goto loop;
1.35 ! dsl 521: goto builtin_success;
1.1 cgd 522: } else if (prefix("func", pathopt)) {
523: /* handled below */
524: } else {
1.35 ! dsl 525: /* ignore unimplemented options */
! 526: goto loop;
1.1 cgd 527: }
528: }
529: /* if rehash, don't redo absolute path names */
1.27 christos 530: if (fullname[0] == '/' && idx <= prev) {
531: if (idx < prev)
1.1 cgd 532: goto loop;
533: TRACE(("searchexec \"%s\": no change\n", name));
534: goto success;
535: }
536: while (stat(fullname, &statb) < 0) {
537: #ifdef SYSV
538: if (errno == EINTR)
539: continue;
540: #endif
541: if (errno != ENOENT && errno != ENOTDIR)
542: e = errno;
543: goto loop;
544: }
545: e = EACCES; /* if we fail, this will be the error */
1.14 mycroft 546: if (!S_ISREG(statb.st_mode))
1.1 cgd 547: goto loop;
548: if (pathopt) { /* this is a %func directory */
1.35 ! dsl 549: if (act & DO_NOFUNC)
! 550: goto loop;
1.1 cgd 551: stalloc(strlen(fullname) + 1);
552: readcmdfile(fullname);
1.35 ! dsl 553: if ((cmdp = cmdlookup(name, 0)) == NULL ||
! 554: cmdp->cmdtype != CMDFUNCTION)
1.1 cgd 555: error("%s not defined in %s", name, fullname);
556: stunalloc(fullname);
557: goto success;
558: }
1.8 jtc 559: #ifdef notdef
1.35 ! dsl 560: /* XXX this code stops root executing stuff, and is buggy
! 561: if you need a group from the group list. */
1.8 jtc 562: if (statb.st_uid == geteuid()) {
1.1 cgd 563: if ((statb.st_mode & 0100) == 0)
564: goto loop;
565: } else if (statb.st_gid == getegid()) {
566: if ((statb.st_mode & 010) == 0)
567: goto loop;
568: } else {
1.8 jtc 569: if ((statb.st_mode & 01) == 0)
1.1 cgd 570: goto loop;
571: }
1.4 cgd 572: #endif
1.1 cgd 573: TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
574: INTOFF;
1.35 ! dsl 575: if (act & DO_ALTPATH) {
! 576: stalloc(strlen(fullname) + 1);
! 577: cmdp = &loc_cmd;
! 578: } else
! 579: cmdp = cmdlookup(name, 1);
1.1 cgd 580: cmdp->cmdtype = CMDNORMAL;
1.27 christos 581: cmdp->param.index = idx;
1.1 cgd 582: INTON;
583: goto success;
584: }
585:
586: /* We failed. If there was an entry for this command, delete it */
587: if (cmdp)
588: delete_cmd_entry();
1.24 christos 589: if (act & DO_ERR)
1.20 abrown 590: outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
1.1 cgd 591: entry->cmdtype = CMDUNKNOWN;
592: return;
593:
1.35 ! dsl 594: builtin_success:
! 595: INTOFF;
! 596: if (act & DO_ALTPATH)
! 597: cmdp = &loc_cmd;
! 598: else
! 599: cmdp = cmdlookup(name, 1);
! 600: cmdp->cmdtype = CMDBUILTIN;
! 601: cmdp->param.bltin = bltin;
! 602: INTON;
1.1 cgd 603: success:
604: cmdp->rehash = 0;
605: entry->cmdtype = cmdp->cmdtype;
606: entry->u = cmdp->param;
607: }
608:
609:
610:
611: /*
612: * Search the table of builtin commands.
613: */
614:
615: int
1.34 christos 616: (*find_builtin(name))(int, char **)
1.1 cgd 617: char *name;
1.12 cgd 618: {
1.21 tls 619: const struct builtincmd *bp;
1.1 cgd 620:
621: for (bp = builtincmd ; bp->name ; bp++) {
622: if (*bp->name == *name && equal(bp->name, name))
1.34 christos 623: return bp->builtin;
624: }
625: return 0;
626: }
627:
628: int
629: (*find_splbltin(name))(int, char **)
630: char *name;
631: {
632: const struct builtincmd *bp;
633:
634: for (bp = splbltincmd ; bp->name ; bp++) {
635: if (*bp->name == *name && equal(bp->name, name))
636: return bp->builtin;
637: }
638: return 0;
639: }
640:
641: /*
642: * At shell startup put special builtins into hash table.
643: * ensures they are executed first (see posix).
644: * We stop functions being added with the same name
645: * (as they are impossible to call)
646: */
647:
648: void
649: hash_special_builtins(void)
650: {
651: const struct builtincmd *bp;
652: struct tblentry *cmdp;
653:
654: for (bp = splbltincmd ; bp->name ; bp++) {
655: cmdp = cmdlookup(bp->name, 1);
656: cmdp->cmdtype = CMDSPLBLTIN;
657: cmdp->param.bltin = bp->builtin;
1.1 cgd 658: }
659: }
660:
661:
662:
663: /*
664: * Called when a cd is done. Marks all commands so the next time they
665: * are executed they will be rehashed.
666: */
667:
668: void
1.34 christos 669: hashcd(void)
670: {
1.1 cgd 671: struct tblentry **pp;
672: struct tblentry *cmdp;
673:
674: for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
675: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
676: if (cmdp->cmdtype == CMDNORMAL
1.16 christos 677: || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
1.1 cgd 678: cmdp->rehash = 1;
679: }
680: }
681: }
682:
683:
684:
685: /*
1.35 ! dsl 686: * Fix command hash table when PATH changed.
1.1 cgd 687: * Called before PATH is changed. The argument is the new value of PATH;
1.35 ! dsl 688: * pathval() still returns the old value at this point.
! 689: * Called with interrupts off.
1.1 cgd 690: */
691:
692: void
1.34 christos 693: changepath(const char *newval)
1.13 mycroft 694: {
1.18 christos 695: const char *old, *new;
1.27 christos 696: int idx;
1.1 cgd 697: int firstchange;
698: int bltin;
699:
700: old = pathval();
701: new = newval;
702: firstchange = 9999; /* assume no change */
1.27 christos 703: idx = 0;
1.1 cgd 704: bltin = -1;
705: for (;;) {
706: if (*old != *new) {
1.27 christos 707: firstchange = idx;
1.16 christos 708: if ((*old == '\0' && *new == ':')
709: || (*old == ':' && *new == '\0'))
1.1 cgd 710: firstchange++;
711: old = new; /* ignore subsequent differences */
712: }
713: if (*new == '\0')
714: break;
715: if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
1.27 christos 716: bltin = idx;
1.1 cgd 717: if (*new == ':') {
1.27 christos 718: idx++;
1.1 cgd 719: }
720: new++, old++;
721: }
722: if (builtinloc < 0 && bltin >= 0)
723: builtinloc = bltin; /* zap builtins */
724: if (builtinloc >= 0 && bltin < 0)
725: firstchange = 0;
726: clearcmdentry(firstchange);
727: builtinloc = bltin;
728: }
729:
730:
731: /*
732: * Clear out command entries. The argument specifies the first entry in
733: * PATH which has changed.
734: */
735:
736: STATIC void
1.34 christos 737: clearcmdentry(int firstchange)
1.12 cgd 738: {
1.1 cgd 739: struct tblentry **tblp;
740: struct tblentry **pp;
741: struct tblentry *cmdp;
742:
743: INTOFF;
744: for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
745: pp = tblp;
746: while ((cmdp = *pp) != NULL) {
1.16 christos 747: if ((cmdp->cmdtype == CMDNORMAL &&
748: cmdp->param.index >= firstchange)
749: || (cmdp->cmdtype == CMDBUILTIN &&
750: builtinloc >= firstchange)) {
1.1 cgd 751: *pp = cmdp->next;
752: ckfree(cmdp);
753: } else {
754: pp = &cmdp->next;
755: }
756: }
757: }
758: INTON;
759: }
760:
761:
762: /*
763: * Delete all functions.
764: */
765:
766: #ifdef mkinit
1.34 christos 767: MKINIT void deletefuncs(void);
768: MKINIT void hash_special_builtins(void);
769:
770: INIT {
771: hash_special_builtins();
772: }
1.1 cgd 773:
774: SHELLPROC {
775: deletefuncs();
776: }
777: #endif
778:
779: void
1.34 christos 780: deletefuncs(void)
781: {
1.1 cgd 782: struct tblentry **tblp;
783: struct tblentry **pp;
784: struct tblentry *cmdp;
785:
786: INTOFF;
787: for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
788: pp = tblp;
789: while ((cmdp = *pp) != NULL) {
790: if (cmdp->cmdtype == CMDFUNCTION) {
791: *pp = cmdp->next;
792: freefunc(cmdp->param.func);
793: ckfree(cmdp);
794: } else {
795: pp = &cmdp->next;
796: }
797: }
798: }
799: INTON;
800: }
801:
802:
803:
804: /*
805: * Locate a command in the command hash table. If "add" is nonzero,
806: * add the command to the table if it is not already present. The
807: * variable "lastcmdentry" is set to point to the address of the link
808: * pointing to the entry, so that delete_cmd_entry can delete the
809: * entry.
810: */
811:
812: struct tblentry **lastcmdentry;
813:
814:
815: STATIC struct tblentry *
1.34 christos 816: cmdlookup(const char *name, int add)
1.12 cgd 817: {
1.1 cgd 818: int hashval;
1.34 christos 819: const char *p;
1.1 cgd 820: struct tblentry *cmdp;
821: struct tblentry **pp;
822:
823: p = name;
824: hashval = *p << 4;
825: while (*p)
826: hashval += *p++;
827: hashval &= 0x7FFF;
828: pp = &cmdtable[hashval % CMDTABLESIZE];
829: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
830: if (equal(cmdp->cmdname, name))
831: break;
832: pp = &cmdp->next;
833: }
834: if (add && cmdp == NULL) {
835: INTOFF;
836: cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
837: + strlen(name) + 1);
838: cmdp->next = NULL;
839: cmdp->cmdtype = CMDUNKNOWN;
840: cmdp->rehash = 0;
841: strcpy(cmdp->cmdname, name);
842: INTON;
843: }
844: lastcmdentry = pp;
845: return cmdp;
846: }
847:
848: /*
849: * Delete the command entry returned on the last lookup.
850: */
851:
852: STATIC void
1.34 christos 853: delete_cmd_entry(void)
854: {
1.1 cgd 855: struct tblentry *cmdp;
856:
857: INTOFF;
858: cmdp = *lastcmdentry;
859: *lastcmdentry = cmdp->next;
860: ckfree(cmdp);
861: INTON;
862: }
863:
864:
865:
866: #ifdef notdef
867: void
1.34 christos 868: getcmdentry(char *name, struct cmdentry *entry)
869: {
1.1 cgd 870: struct tblentry *cmdp = cmdlookup(name, 0);
871:
872: if (cmdp) {
873: entry->u = cmdp->param;
874: entry->cmdtype = cmdp->cmdtype;
875: } else {
876: entry->cmdtype = CMDUNKNOWN;
877: entry->u.index = 0;
878: }
879: }
880: #endif
881:
882:
883: /*
884: * Add a new command entry, replacing any existing command entry for
1.34 christos 885: * the same name - except special builtins.
1.1 cgd 886: */
887:
1.35 ! dsl 888: STATIC void
1.34 christos 889: addcmdentry(char *name, struct cmdentry *entry)
890: {
1.1 cgd 891: struct tblentry *cmdp;
892:
893: INTOFF;
894: cmdp = cmdlookup(name, 1);
1.34 christos 895: if (cmdp->cmdtype != CMDSPLBLTIN) {
896: if (cmdp->cmdtype == CMDFUNCTION) {
897: freefunc(cmdp->param.func);
898: }
899: cmdp->cmdtype = entry->cmdtype;
900: cmdp->param = entry->u;
1.1 cgd 901: }
902: INTON;
903: }
904:
905:
906: /*
907: * Define a shell function.
908: */
909:
910: void
1.34 christos 911: defun(char *name, union node *func)
912: {
1.1 cgd 913: struct cmdentry entry;
914:
915: INTOFF;
916: entry.cmdtype = CMDFUNCTION;
917: entry.u.func = copyfunc(func);
918: addcmdentry(name, &entry);
919: INTON;
920: }
921:
922:
923: /*
924: * Delete a function if it exists.
925: */
926:
1.8 jtc 927: int
1.34 christos 928: unsetfunc(char *name)
929: {
1.1 cgd 930: struct tblentry *cmdp;
931:
1.35 ! dsl 932: if ((cmdp = cmdlookup(name, 0)) != NULL &&
! 933: cmdp->cmdtype == CMDFUNCTION) {
1.1 cgd 934: freefunc(cmdp->param.func);
935: delete_cmd_entry();
1.8 jtc 936: return (0);
1.1 cgd 937: }
1.8 jtc 938: return (1);
1.22 christos 939: }
940:
941: /*
942: * Locate and print what a word is...
1.35 ! dsl 943: * also used for 'command -[v|V]'
1.22 christos 944: */
945:
946: int
1.34 christos 947: typecmd(int argc, char **argv)
1.22 christos 948: {
949: struct cmdentry entry;
950: struct tblentry *cmdp;
1.30 matt 951: char * const *pp;
1.22 christos 952: struct alias *ap;
1.27 christos 953: int err = 0;
1.35 ! dsl 954: char *arg;
! 955: int c;
! 956: int V_flag = 0;
! 957: int v_flag = 0;
! 958: int p_flag = 0;
1.22 christos 959:
1.35 ! dsl 960: while ((c = nextopt("vVp")) != 0) {
! 961: switch (c) {
! 962: case 'v': v_flag = 1; break;
! 963: case 'V': V_flag = 1; break;
! 964: case 'p': p_flag = 1; break;
! 965: }
! 966: }
! 967:
! 968: if (p_flag && (v_flag || V_flag))
! 969: error("cannot specify -p with -v or -V");
! 970:
! 971: while ((arg = *argptr++)) {
! 972: if (!v_flag)
! 973: out1str(arg);
1.22 christos 974: /* First look at the keywords */
1.30 matt 975: for (pp = parsekwd; *pp; pp++)
1.35 ! dsl 976: if (**pp == *arg && equal(*pp, arg))
1.22 christos 977: break;
978:
979: if (*pp) {
1.35 ! dsl 980: if (v_flag)
! 981: err = 1;
! 982: else
! 983: out1str(" is a shell keyword\n");
1.22 christos 984: continue;
985: }
986:
987: /* Then look at the aliases */
1.35 ! dsl 988: if ((ap = lookupalias(arg, 1)) != NULL) {
! 989: if (!v_flag)
! 990: out1fmt(" is an alias for \n");
! 991: out1fmt("%s\n", ap->val);
1.22 christos 992: continue;
993: }
994:
995: /* Then check if it is a tracked alias */
1.35 ! dsl 996: if ((cmdp = cmdlookup(arg, 0)) != NULL) {
1.22 christos 997: entry.cmdtype = cmdp->cmdtype;
998: entry.u = cmdp->param;
1.35 ! dsl 999: } else {
1.22 christos 1000: /* Finally use brute force */
1.35 ! dsl 1001: find_command(arg, &entry, DO_ABS, pathval());
1.22 christos 1002: }
1003:
1004: switch (entry.cmdtype) {
1005: case CMDNORMAL: {
1.35 ! dsl 1006: if (strchr(arg, '/') == NULL) {
1.31 christos 1007: const char *path = pathval();
1008: char *name;
1009: int j = entry.u.index;
1010: do {
1.35 ! dsl 1011: name = padvance(&path, arg);
1.24 christos 1012: stunalloc(name);
1013: } while (--j >= 0);
1.35 ! dsl 1014: if (!v_flag)
! 1015: out1fmt(" is%s ",
! 1016: cmdp ? " a tracked alias for" : "");
! 1017: out1fmt("%s\n", name);
1.31 christos 1018: } else {
1.35 ! dsl 1019: if (access(arg, X_OK) == 0) {
! 1020: if (!v_flag)
! 1021: out1fmt(" is ");
! 1022: out1fmt("%s\n", arg);
! 1023: } else {
! 1024: if (!v_flag)
! 1025: out1fmt(": %s\n",
! 1026: strerror(errno));
! 1027: else
! 1028: err = 126;
! 1029: }
1.24 christos 1030: }
1.31 christos 1031: break;
1.22 christos 1032: }
1033: case CMDFUNCTION:
1.35 ! dsl 1034: if (!v_flag)
! 1035: out1str(" is a shell function\n");
! 1036: else
! 1037: out1fmt("%s\n", arg);
1.22 christos 1038: break;
1039:
1040: case CMDBUILTIN:
1.35 ! dsl 1041: if (!v_flag)
! 1042: out1str(" is a shell builtin\n");
! 1043: else
! 1044: out1fmt("%s\n", arg);
1.34 christos 1045: break;
1046:
1047: case CMDSPLBLTIN:
1.35 ! dsl 1048: if (!v_flag)
! 1049: out1str(" is a special shell builtin\n");
! 1050: else
! 1051: out1fmt("%s\n", arg);
1.22 christos 1052: break;
1053:
1054: default:
1.35 ! dsl 1055: if (!v_flag)
! 1056: out1str(": not found\n");
! 1057: err = 127;
1.22 christos 1058: break;
1059: }
1060: }
1.27 christos 1061: return err;
1.1 cgd 1062: }
CVSweb <webmaster@jp.NetBSD.org>