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