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