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