Annotation of src/bin/sh/exec.c, Revision 1.25
1.25 ! mycroft 1: /* $NetBSD: exec.c,v 1.24 1997/07/20 21:27:36 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.25 ! mycroft 44: __RCSID("$NetBSD: exec.c,v 1.24 1997/07/20 21:27:36 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>
50: #include <unistd.h>
51: #include <fcntl.h>
52: #include <errno.h>
53: #include <stdlib.h>
54:
1.1 cgd 55: /*
56: * When commands are first encountered, they are entered in a hash table.
57: * This ensures that a full path search will not have to be done for them
58: * on each invocation.
59: *
60: * We should investigate converting to a linear search, even though that
61: * would make the command name "hash" a misnomer.
62: */
63:
64: #include "shell.h"
65: #include "main.h"
66: #include "nodes.h"
67: #include "parser.h"
68: #include "redir.h"
69: #include "eval.h"
70: #include "exec.h"
71: #include "builtins.h"
72: #include "var.h"
73: #include "options.h"
74: #include "input.h"
75: #include "output.h"
76: #include "syntax.h"
77: #include "memalloc.h"
78: #include "error.h"
79: #include "init.h"
80: #include "mystring.h"
1.16 christos 81: #include "show.h"
1.8 jtc 82: #include "jobs.h"
1.22 christos 83: #include "alias.h"
1.1 cgd 84:
85:
86: #define CMDTABLESIZE 31 /* should be prime */
87: #define ARB 1 /* actual size determined at run time */
88:
89:
90:
91: struct tblentry {
92: struct tblentry *next; /* next entry in hash chain */
93: union param param; /* definition of builtin function */
94: short cmdtype; /* index identifying command */
95: char rehash; /* if set, cd done since entry created */
96: char cmdname[ARB]; /* name of command */
97: };
98:
99:
100: STATIC struct tblentry *cmdtable[CMDTABLESIZE];
101: STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
1.19 christos 102: int exerrno = 0; /* Last exec error */
1.1 cgd 103:
104:
1.16 christos 105: STATIC void tryexec __P((char *, char **, char **));
106: STATIC void execinterp __P((char **, char **));
107: STATIC void printentry __P((struct tblentry *, int));
108: STATIC void clearcmdentry __P((int));
109: STATIC struct tblentry *cmdlookup __P((char *, int));
110: STATIC void delete_cmd_entry __P((void));
1.1 cgd 111:
112:
113:
114: /*
115: * Exec a program. Never returns. If you change this routine, you may
116: * have to change the find_command routine as well.
117: */
118:
119: void
120: shellexec(argv, envp, path, index)
121: char **argv, **envp;
122: char *path;
1.12 cgd 123: int index;
124: {
1.1 cgd 125: char *cmdname;
126: int e;
127:
128: if (strchr(argv[0], '/') != NULL) {
129: tryexec(argv[0], argv, envp);
130: e = errno;
131: } else {
132: e = ENOENT;
133: while ((cmdname = padvance(&path, argv[0])) != NULL) {
134: if (--index < 0 && pathopt == NULL) {
135: tryexec(cmdname, argv, envp);
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: }
155: exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
1.1 cgd 156: }
157:
158:
159: STATIC void
160: tryexec(cmd, argv, envp)
161: char *cmd;
162: char **argv;
163: char **envp;
164: {
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) {
179: initshellproc();
180: setinputfile(cmd, 0);
181: commandname = arg0 = savestr(argv[0]);
182: #ifndef BSD
183: pgetc(); pungetc(); /* fill up input buffer */
184: p = parsenextc;
185: if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
186: argv[0] = cmd;
187: execinterp(argv, envp);
188: }
189: #endif
190: setparam(argv + 1);
191: exraise(EXSHELLPROC);
1.25 ! mycroft 192: /* NOTREACHED */
1.1 cgd 193: }
194: errno = e;
195: }
196:
197:
198: #ifndef BSD
199: /*
200: * Execute an interpreter introduced by "#!", for systems where this
201: * feature has not been built into the kernel. If the interpreter is
202: * the shell, return (effectively ignoring the "#!"). If the execution
203: * of the interpreter fails, exit.
204: *
205: * This code peeks inside the input buffer in order to avoid actually
206: * reading any input. It would benefit from a rewrite.
207: */
208:
209: #define NEWARGS 5
210:
211: STATIC void
212: execinterp(argv, envp)
213: char **argv, **envp;
214: {
215: int n;
216: char *inp;
217: char *outp;
218: char c;
219: char *p;
220: char **ap;
221: char *newargs[NEWARGS];
222: int i;
223: char **ap2;
224: char **new;
225:
226: n = parsenleft - 2;
227: inp = parsenextc + 2;
228: ap = newargs;
229: for (;;) {
230: while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
231: inp++;
232: if (n < 0)
233: goto bad;
234: if ((c = *inp++) == '\n')
235: break;
236: if (ap == &newargs[NEWARGS])
237: bad: error("Bad #! line");
238: STARTSTACKSTR(outp);
239: do {
240: STPUTC(c, outp);
241: } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
242: STPUTC('\0', outp);
243: n++, inp--;
244: *ap++ = grabstackstr(outp);
245: }
246: if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
247: p = newargs[0];
248: for (;;) {
249: if (equal(p, "sh") || equal(p, "ash")) {
250: return;
251: }
252: while (*p != '/') {
253: if (*p == '\0')
254: goto break2;
255: p++;
256: }
257: p++;
258: }
259: break2:;
260: }
261: i = (char *)ap - (char *)newargs; /* size in bytes */
262: if (i == 0)
263: error("Bad #! line");
264: for (ap2 = argv ; *ap2++ != NULL ; );
265: new = ckmalloc(i + ((char *)ap2 - (char *)argv));
266: ap = newargs, ap2 = new;
267: while ((i -= sizeof (char **)) >= 0)
268: *ap2++ = *ap++;
269: ap = argv;
270: while (*ap2++ = *ap++);
271: shellexec(new, envp, pathval(), 0);
272: }
273: #endif
274:
275:
276:
277: /*
278: * Do a path search. The variable path (passed by reference) should be
279: * set to the start of the path before the first call; padvance will update
280: * this value as it proceeds. Successive calls to padvance will return
281: * the possible path expansions in sequence. If an option (indicated by
282: * a percent sign) appears in the path entry then the global variable
283: * pathopt will be set to point to it; otherwise pathopt will be set to
284: * NULL.
285: */
286:
287: char *pathopt;
288:
289: char *
290: padvance(path, name)
291: char **path;
292: char *name;
293: {
1.21 tls 294: char *p, *q;
1.1 cgd 295: char *start;
296: int len;
297:
298: if (*path == NULL)
299: return NULL;
300: start = *path;
301: for (p = start ; *p && *p != ':' && *p != '%' ; p++);
302: len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
303: while (stackblocksize() < len)
304: growstackblock();
305: q = stackblock();
306: if (p != start) {
1.11 mycroft 307: memcpy(q, start, p - start);
1.1 cgd 308: q += p - start;
309: *q++ = '/';
310: }
311: strcpy(q, name);
312: pathopt = NULL;
313: if (*p == '%') {
314: pathopt = ++p;
315: while (*p && *p != ':') p++;
316: }
317: if (*p == ':')
318: *path = p + 1;
319: else
320: *path = NULL;
321: return stalloc(len);
322: }
323:
324:
325:
326: /*** Command hashing code ***/
327:
328:
1.12 cgd 329: int
330: hashcmd(argc, argv)
331: int argc;
1.19 christos 332: char **argv;
1.12 cgd 333: {
1.1 cgd 334: struct tblentry **pp;
335: struct tblentry *cmdp;
336: int c;
337: int verbose;
338: struct cmdentry entry;
339: char *name;
340:
341: verbose = 0;
342: while ((c = nextopt("rv")) != '\0') {
343: if (c == 'r') {
344: clearcmdentry(0);
345: } else if (c == 'v') {
346: verbose++;
347: }
348: }
1.8 jtc 349: if (*argptr == NULL) {
350: for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
351: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
352: printentry(cmdp, verbose);
353: }
354: }
355: return 0;
356: }
1.1 cgd 357: while ((name = *argptr) != NULL) {
358: if ((cmdp = cmdlookup(name, 0)) != NULL
359: && (cmdp->cmdtype == CMDNORMAL
1.16 christos 360: || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
1.1 cgd 361: delete_cmd_entry();
1.24 christos 362: find_command(name, &entry, DO_ERR, pathval());
1.1 cgd 363: if (verbose) {
364: if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
365: cmdp = cmdlookup(name, 0);
1.8 jtc 366: printentry(cmdp, verbose);
1.1 cgd 367: }
368: flushall();
369: }
370: argptr++;
371: }
372: return 0;
373: }
374:
375:
376: STATIC void
1.8 jtc 377: printentry(cmdp, verbose)
1.1 cgd 378: struct tblentry *cmdp;
1.8 jtc 379: int verbose;
1.1 cgd 380: {
381: int index;
382: char *path;
383: char *name;
384:
385: if (cmdp->cmdtype == CMDNORMAL) {
386: index = cmdp->param.index;
387: path = pathval();
388: do {
389: name = padvance(&path, cmdp->cmdname);
390: stunalloc(name);
391: } while (--index >= 0);
392: out1str(name);
393: } else if (cmdp->cmdtype == CMDBUILTIN) {
394: out1fmt("builtin %s", cmdp->cmdname);
395: } else if (cmdp->cmdtype == CMDFUNCTION) {
396: out1fmt("function %s", cmdp->cmdname);
1.8 jtc 397: if (verbose) {
398: INTOFF;
399: name = commandtext(cmdp->param.func);
400: out1c(' ');
401: out1str(name);
402: ckfree(name);
403: INTON;
404: }
1.1 cgd 405: #ifdef DEBUG
406: } else {
407: error("internal error: cmdtype %d", cmdp->cmdtype);
408: #endif
409: }
410: if (cmdp->rehash)
411: out1c('*');
412: out1c('\n');
413: }
414:
415:
416:
417: /*
418: * Resolve a command name. If you change this routine, you may have to
419: * change the shellexec routine as well.
420: */
421:
422: void
1.24 christos 423: find_command(name, entry, act, path)
1.1 cgd 424: char *name;
425: struct cmdentry *entry;
1.24 christos 426: int act;
1.17 christos 427: char *path;
1.12 cgd 428: {
1.1 cgd 429: struct tblentry *cmdp;
430: int index;
431: int prev;
432: char *fullname;
433: struct stat statb;
434: int e;
435: int i;
436:
437: /* If name contains a slash, don't use the hash table */
438: if (strchr(name, '/') != NULL) {
1.24 christos 439: if (act & DO_ABS) {
440: while (stat(name, &statb) < 0) {
441: #ifdef SYSV
442: if (errno == EINTR)
443: continue;
444: #endif
445: if (errno != ENOENT && errno != ENOTDIR)
446: e = errno;
447: entry->cmdtype = CMDUNKNOWN;
448: entry->u.index = -1;
449: return;
450: }
451: entry->cmdtype = CMDNORMAL;
452: entry->u.index = -1;
453: return;
454: }
1.1 cgd 455: entry->cmdtype = CMDNORMAL;
456: entry->u.index = 0;
457: return;
458: }
459:
460: /* If name is in the table, and not invalidated by cd, we're done */
461: if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
462: goto success;
463:
464: /* If %builtin not in path, check for builtin next */
465: if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
466: INTOFF;
467: cmdp = cmdlookup(name, 1);
468: cmdp->cmdtype = CMDBUILTIN;
469: cmdp->param.index = i;
470: INTON;
471: goto success;
472: }
473:
474: /* We have to search path. */
475: prev = -1; /* where to start */
476: if (cmdp) { /* doing a rehash */
477: if (cmdp->cmdtype == CMDBUILTIN)
478: prev = builtinloc;
479: else
480: prev = cmdp->param.index;
481: }
482:
483: e = ENOENT;
484: index = -1;
485: loop:
486: while ((fullname = padvance(&path, name)) != NULL) {
487: stunalloc(fullname);
488: index++;
489: if (pathopt) {
490: if (prefix("builtin", pathopt)) {
491: if ((i = find_builtin(name)) < 0)
492: goto loop;
493: INTOFF;
494: cmdp = cmdlookup(name, 1);
495: cmdp->cmdtype = CMDBUILTIN;
496: cmdp->param.index = i;
497: INTON;
498: goto success;
499: } else if (prefix("func", pathopt)) {
500: /* handled below */
501: } else {
502: goto loop; /* ignore unimplemented options */
503: }
504: }
505: /* if rehash, don't redo absolute path names */
506: if (fullname[0] == '/' && index <= prev) {
507: if (index < prev)
508: goto loop;
509: TRACE(("searchexec \"%s\": no change\n", name));
510: goto success;
511: }
512: while (stat(fullname, &statb) < 0) {
513: #ifdef SYSV
514: if (errno == EINTR)
515: continue;
516: #endif
517: if (errno != ENOENT && errno != ENOTDIR)
518: e = errno;
519: goto loop;
520: }
521: e = EACCES; /* if we fail, this will be the error */
1.14 mycroft 522: if (!S_ISREG(statb.st_mode))
1.1 cgd 523: goto loop;
524: if (pathopt) { /* this is a %func directory */
525: stalloc(strlen(fullname) + 1);
526: readcmdfile(fullname);
527: if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
528: error("%s not defined in %s", name, fullname);
529: stunalloc(fullname);
530: goto success;
531: }
1.8 jtc 532: #ifdef notdef
533: if (statb.st_uid == geteuid()) {
1.1 cgd 534: if ((statb.st_mode & 0100) == 0)
535: goto loop;
536: } else if (statb.st_gid == getegid()) {
537: if ((statb.st_mode & 010) == 0)
538: goto loop;
539: } else {
1.8 jtc 540: if ((statb.st_mode & 01) == 0)
1.1 cgd 541: goto loop;
542: }
1.4 cgd 543: #endif
1.1 cgd 544: TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
545: INTOFF;
546: cmdp = cmdlookup(name, 1);
547: cmdp->cmdtype = CMDNORMAL;
548: cmdp->param.index = index;
549: INTON;
550: goto success;
551: }
552:
553: /* We failed. If there was an entry for this command, delete it */
554: if (cmdp)
555: delete_cmd_entry();
1.24 christos 556: if (act & DO_ERR)
1.20 abrown 557: outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
1.1 cgd 558: entry->cmdtype = CMDUNKNOWN;
559: return;
560:
561: success:
562: cmdp->rehash = 0;
563: entry->cmdtype = cmdp->cmdtype;
564: entry->u = cmdp->param;
565: }
566:
567:
568:
569: /*
570: * Search the table of builtin commands.
571: */
572:
573: int
574: find_builtin(name)
575: char *name;
1.12 cgd 576: {
1.21 tls 577: const struct builtincmd *bp;
1.1 cgd 578:
579: for (bp = builtincmd ; bp->name ; bp++) {
580: if (*bp->name == *name && equal(bp->name, name))
581: return bp->code;
582: }
583: return -1;
584: }
585:
586:
587:
588: /*
589: * Called when a cd is done. Marks all commands so the next time they
590: * are executed they will be rehashed.
591: */
592:
593: void
594: hashcd() {
595: struct tblentry **pp;
596: struct tblentry *cmdp;
597:
598: for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
599: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
600: if (cmdp->cmdtype == CMDNORMAL
1.16 christos 601: || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
1.1 cgd 602: cmdp->rehash = 1;
603: }
604: }
605: }
606:
607:
608:
609: /*
610: * Called before PATH is changed. The argument is the new value of PATH;
611: * pathval() still returns the old value at this point. Called with
612: * interrupts off.
613: */
614:
615: void
616: changepath(newval)
1.18 christos 617: const char *newval;
1.13 mycroft 618: {
1.18 christos 619: const char *old, *new;
1.1 cgd 620: int index;
621: int firstchange;
622: int bltin;
623:
624: old = pathval();
625: new = newval;
626: firstchange = 9999; /* assume no change */
1.13 mycroft 627: index = 0;
1.1 cgd 628: bltin = -1;
629: for (;;) {
630: if (*old != *new) {
631: firstchange = index;
1.16 christos 632: if ((*old == '\0' && *new == ':')
633: || (*old == ':' && *new == '\0'))
1.1 cgd 634: firstchange++;
635: old = new; /* ignore subsequent differences */
636: }
637: if (*new == '\0')
638: break;
639: if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
640: bltin = index;
641: if (*new == ':') {
642: index++;
643: }
644: new++, old++;
645: }
646: if (builtinloc < 0 && bltin >= 0)
647: builtinloc = bltin; /* zap builtins */
648: if (builtinloc >= 0 && bltin < 0)
649: firstchange = 0;
650: clearcmdentry(firstchange);
651: builtinloc = bltin;
652: }
653:
654:
655: /*
656: * Clear out command entries. The argument specifies the first entry in
657: * PATH which has changed.
658: */
659:
660: STATIC void
1.12 cgd 661: clearcmdentry(firstchange)
662: int firstchange;
663: {
1.1 cgd 664: struct tblentry **tblp;
665: struct tblentry **pp;
666: struct tblentry *cmdp;
667:
668: INTOFF;
669: for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
670: pp = tblp;
671: while ((cmdp = *pp) != NULL) {
1.16 christos 672: if ((cmdp->cmdtype == CMDNORMAL &&
673: cmdp->param.index >= firstchange)
674: || (cmdp->cmdtype == CMDBUILTIN &&
675: builtinloc >= firstchange)) {
1.1 cgd 676: *pp = cmdp->next;
677: ckfree(cmdp);
678: } else {
679: pp = &cmdp->next;
680: }
681: }
682: }
683: INTON;
684: }
685:
686:
687: /*
688: * Delete all functions.
689: */
690:
691: #ifdef mkinit
1.23 christos 692: MKINIT void deletefuncs __P((void));
1.1 cgd 693:
694: SHELLPROC {
695: deletefuncs();
696: }
697: #endif
698:
699: void
700: deletefuncs() {
701: struct tblentry **tblp;
702: struct tblentry **pp;
703: struct tblentry *cmdp;
704:
705: INTOFF;
706: for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
707: pp = tblp;
708: while ((cmdp = *pp) != NULL) {
709: if (cmdp->cmdtype == CMDFUNCTION) {
710: *pp = cmdp->next;
711: freefunc(cmdp->param.func);
712: ckfree(cmdp);
713: } else {
714: pp = &cmdp->next;
715: }
716: }
717: }
718: INTON;
719: }
720:
721:
722:
723: /*
724: * Locate a command in the command hash table. If "add" is nonzero,
725: * add the command to the table if it is not already present. The
726: * variable "lastcmdentry" is set to point to the address of the link
727: * pointing to the entry, so that delete_cmd_entry can delete the
728: * entry.
729: */
730:
731: struct tblentry **lastcmdentry;
732:
733:
734: STATIC struct tblentry *
735: cmdlookup(name, add)
736: char *name;
1.12 cgd 737: int add;
738: {
1.1 cgd 739: int hashval;
1.21 tls 740: char *p;
1.1 cgd 741: struct tblentry *cmdp;
742: struct tblentry **pp;
743:
744: p = name;
745: hashval = *p << 4;
746: while (*p)
747: hashval += *p++;
748: hashval &= 0x7FFF;
749: pp = &cmdtable[hashval % CMDTABLESIZE];
750: for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
751: if (equal(cmdp->cmdname, name))
752: break;
753: pp = &cmdp->next;
754: }
755: if (add && cmdp == NULL) {
756: INTOFF;
757: cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
758: + strlen(name) + 1);
759: cmdp->next = NULL;
760: cmdp->cmdtype = CMDUNKNOWN;
761: cmdp->rehash = 0;
762: strcpy(cmdp->cmdname, name);
763: INTON;
764: }
765: lastcmdentry = pp;
766: return cmdp;
767: }
768:
769: /*
770: * Delete the command entry returned on the last lookup.
771: */
772:
773: STATIC void
774: delete_cmd_entry() {
775: struct tblentry *cmdp;
776:
777: INTOFF;
778: cmdp = *lastcmdentry;
779: *lastcmdentry = cmdp->next;
780: ckfree(cmdp);
781: INTON;
782: }
783:
784:
785:
786: #ifdef notdef
787: void
788: getcmdentry(name, entry)
789: char *name;
1.19 christos 790: struct cmdentry *entry;
1.1 cgd 791: {
792: struct tblentry *cmdp = cmdlookup(name, 0);
793:
794: if (cmdp) {
795: entry->u = cmdp->param;
796: entry->cmdtype = cmdp->cmdtype;
797: } else {
798: entry->cmdtype = CMDUNKNOWN;
799: entry->u.index = 0;
800: }
801: }
802: #endif
803:
804:
805: /*
806: * Add a new command entry, replacing any existing command entry for
807: * the same name.
808: */
809:
810: void
811: addcmdentry(name, entry)
812: char *name;
813: struct cmdentry *entry;
814: {
815: struct tblentry *cmdp;
816:
817: INTOFF;
818: cmdp = cmdlookup(name, 1);
819: if (cmdp->cmdtype == CMDFUNCTION) {
820: freefunc(cmdp->param.func);
821: }
822: cmdp->cmdtype = entry->cmdtype;
823: cmdp->param = entry->u;
824: INTON;
825: }
826:
827:
828: /*
829: * Define a shell function.
830: */
831:
832: void
833: defun(name, func)
834: char *name;
835: union node *func;
836: {
837: struct cmdentry entry;
838:
839: INTOFF;
840: entry.cmdtype = CMDFUNCTION;
841: entry.u.func = copyfunc(func);
842: addcmdentry(name, &entry);
843: INTON;
844: }
845:
846:
847: /*
848: * Delete a function if it exists.
849: */
850:
1.8 jtc 851: int
1.1 cgd 852: unsetfunc(name)
853: char *name;
854: {
855: struct tblentry *cmdp;
856:
857: if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
858: freefunc(cmdp->param.func);
859: delete_cmd_entry();
1.8 jtc 860: return (0);
1.1 cgd 861: }
1.8 jtc 862: return (1);
1.22 christos 863: }
864:
865: /*
866: * Locate and print what a word is...
867: */
868:
869: int
870: typecmd(argc, argv)
871: int argc;
872: char **argv;
873: {
874: struct cmdentry entry;
875: struct tblentry *cmdp;
876: char **pp;
877: struct alias *ap;
878: int i;
879: int error = 0;
880: extern char *const parsekwd[];
881:
882: for (i = 1; i < argc; i++) {
883: out1str(argv[i]);
884: /* First look at the keywords */
885: for (pp = (char **)parsekwd; *pp; pp++)
886: if (**pp == *argv[i] && equal(*pp, argv[i]))
887: break;
888:
889: if (*pp) {
890: out1str(" is a shell keyword\n");
891: continue;
892: }
893:
894: /* Then look at the aliases */
895: if ((ap = lookupalias(argv[i], 1)) != NULL) {
896: out1fmt(" is an alias for %s\n", ap->val);
897: continue;
898: }
899:
900: /* Then check if it is a tracked alias */
901: if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
902: entry.cmdtype = cmdp->cmdtype;
903: entry.u = cmdp->param;
904: }
905: else {
906: /* Finally use brute force */
1.24 christos 907: find_command(argv[i], &entry, DO_ABS, pathval());
1.22 christos 908: }
909:
910: switch (entry.cmdtype) {
911: case CMDNORMAL: {
912: int j = entry.u.index;
913: char *path = pathval(), *name;
1.24 christos 914: if (j == -1)
915: name = argv[i];
916: else {
917: do {
918: name = padvance(&path, argv[i]);
919: stunalloc(name);
920: } while (--j >= 0);
921: }
1.22 christos 922: out1fmt(" is%s %s\n",
923: cmdp ? " a tracked alias for" : "", name);
924: break;
925: }
926: case CMDFUNCTION:
927: out1str(" is a shell function\n");
928: break;
929:
930: case CMDBUILTIN:
931: out1str(" is a shell builtin\n");
932: break;
933:
934: default:
935: out1str(" not found\n");
936: error |= 127;
937: break;
938: }
939: }
940: return error;
1.1 cgd 941: }
CVSweb <webmaster@jp.NetBSD.org>