Home | History | Annotate | Line # | Download | only in sh
exec.c revision 1.11
      1 /*-
      2  * Copyright (c) 1991, 1993
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * This code is derived from software contributed to Berkeley by
      6  * Kenneth Almquist.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #ifndef lint
     38 /*static char sccsid[] = "from: @(#)exec.c	8.1 (Berkeley) 5/31/93";*/
     39 static char *rcsid = "$Id: exec.c,v 1.11 1994/09/23 11:28:40 mycroft Exp $";
     40 #endif /* not lint */
     41 
     42 /*
     43  * When commands are first encountered, they are entered in a hash table.
     44  * This ensures that a full path search will not have to be done for them
     45  * on each invocation.
     46  *
     47  * We should investigate converting to a linear search, even though that
     48  * would make the command name "hash" a misnomer.
     49  */
     50 
     51 #include "shell.h"
     52 #include "main.h"
     53 #include "nodes.h"
     54 #include "parser.h"
     55 #include "redir.h"
     56 #include "eval.h"
     57 #include "exec.h"
     58 #include "builtins.h"
     59 #include "var.h"
     60 #include "options.h"
     61 #include "input.h"
     62 #include "output.h"
     63 #include "syntax.h"
     64 #include "memalloc.h"
     65 #include "error.h"
     66 #include "init.h"
     67 #include "mystring.h"
     68 #include "jobs.h"
     69 #include <sys/types.h>
     70 #include <sys/stat.h>
     71 #include <unistd.h>
     72 #include <fcntl.h>
     73 #include <errno.h>
     74 
     75 
     76 #define CMDTABLESIZE 31		/* should be prime */
     77 #define ARB 1			/* actual size determined at run time */
     78 
     79 
     80 
     81 struct tblentry {
     82 	struct tblentry *next;	/* next entry in hash chain */
     83 	union param param;	/* definition of builtin function */
     84 	short cmdtype;		/* index identifying command */
     85 	char rehash;		/* if set, cd done since entry created */
     86 	char cmdname[ARB];	/* name of command */
     87 };
     88 
     89 
     90 STATIC struct tblentry *cmdtable[CMDTABLESIZE];
     91 STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
     92 
     93 
     94 #ifdef __STDC__
     95 STATIC void tryexec(char *, char **, char **);
     96 STATIC void execinterp(char **, char **);
     97 STATIC void printentry(struct tblentry *, int);
     98 STATIC void clearcmdentry(int);
     99 STATIC struct tblentry *cmdlookup(char *, int);
    100 STATIC void delete_cmd_entry(void);
    101 #else
    102 STATIC void tryexec();
    103 STATIC void execinterp();
    104 STATIC void printentry();
    105 STATIC void clearcmdentry();
    106 STATIC struct tblentry *cmdlookup();
    107 STATIC void delete_cmd_entry();
    108 #endif
    109 
    110 
    111 
    112 /*
    113  * Exec a program.  Never returns.  If you change this routine, you may
    114  * have to change the find_command routine as well.
    115  */
    116 
    117 void
    118 shellexec(argv, envp, path, index)
    119 	char **argv, **envp;
    120 	char *path;
    121 	{
    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;
    150 	char *p;
    151 
    152 #ifdef SYSV
    153 	do {
    154 		execve(cmd, argv, envp);
    155 	} while (errno == EINTR);
    156 #else
    157 	execve(cmd, argv, envp);
    158 #endif
    159 	e = errno;
    160 	if (e == ENOEXEC) {
    161 		initshellproc();
    162 		setinputfile(cmd, 0);
    163 		commandname = arg0 = savestr(argv[0]);
    164 #ifndef BSD
    165 		pgetc(); pungetc();		/* fill up input buffer */
    166 		p = parsenextc;
    167 		if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
    168 			argv[0] = cmd;
    169 			execinterp(argv, envp);
    170 		}
    171 #endif
    172 		setparam(argv + 1);
    173 		exraise(EXSHELLPROC);
    174 		/*NOTREACHED*/
    175 	}
    176 	errno = e;
    177 }
    178 
    179 
    180 #ifndef BSD
    181 /*
    182  * Execute an interpreter introduced by "#!", for systems where this
    183  * feature has not been built into the kernel.  If the interpreter is
    184  * the shell, return (effectively ignoring the "#!").  If the execution
    185  * of the interpreter fails, exit.
    186  *
    187  * This code peeks inside the input buffer in order to avoid actually
    188  * reading any input.  It would benefit from a rewrite.
    189  */
    190 
    191 #define NEWARGS 5
    192 
    193 STATIC void
    194 execinterp(argv, envp)
    195 	char **argv, **envp;
    196 	{
    197 	int n;
    198 	char *inp;
    199 	char *outp;
    200 	char c;
    201 	char *p;
    202 	char **ap;
    203 	char *newargs[NEWARGS];
    204 	int i;
    205 	char **ap2;
    206 	char **new;
    207 
    208 	n = parsenleft - 2;
    209 	inp = parsenextc + 2;
    210 	ap = newargs;
    211 	for (;;) {
    212 		while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
    213 			inp++;
    214 		if (n < 0)
    215 			goto bad;
    216 		if ((c = *inp++) == '\n')
    217 			break;
    218 		if (ap == &newargs[NEWARGS])
    219 bad:		  error("Bad #! line");
    220 		STARTSTACKSTR(outp);
    221 		do {
    222 			STPUTC(c, outp);
    223 		} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
    224 		STPUTC('\0', outp);
    225 		n++, inp--;
    226 		*ap++ = grabstackstr(outp);
    227 	}
    228 	if (ap == newargs + 1) {	/* if no args, maybe no exec is needed */
    229 		p = newargs[0];
    230 		for (;;) {
    231 			if (equal(p, "sh") || equal(p, "ash")) {
    232 				return;
    233 			}
    234 			while (*p != '/') {
    235 				if (*p == '\0')
    236 					goto break2;
    237 				p++;
    238 			}
    239 			p++;
    240 		}
    241 break2:;
    242 	}
    243 	i = (char *)ap - (char *)newargs;		/* size in bytes */
    244 	if (i == 0)
    245 		error("Bad #! line");
    246 	for (ap2 = argv ; *ap2++ != NULL ; );
    247 	new = ckmalloc(i + ((char *)ap2 - (char *)argv));
    248 	ap = newargs, ap2 = new;
    249 	while ((i -= sizeof (char **)) >= 0)
    250 		*ap2++ = *ap++;
    251 	ap = argv;
    252 	while (*ap2++ = *ap++);
    253 	shellexec(new, envp, pathval(), 0);
    254 }
    255 #endif
    256 
    257 
    258 
    259 /*
    260  * Do a path search.  The variable path (passed by reference) should be
    261  * set to the start of the path before the first call; padvance will update
    262  * this value as it proceeds.  Successive calls to padvance will return
    263  * the possible path expansions in sequence.  If an option (indicated by
    264  * a percent sign) appears in the path entry then the global variable
    265  * pathopt will be set to point to it; otherwise pathopt will be set to
    266  * NULL.
    267  */
    268 
    269 char *pathopt;
    270 
    271 char *
    272 padvance(path, name)
    273 	char **path;
    274 	char *name;
    275 	{
    276 	register char *p, *q;
    277 	char *start;
    278 	int len;
    279 
    280 	if (*path == NULL)
    281 		return NULL;
    282 	start = *path;
    283 	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
    284 	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
    285 	while (stackblocksize() < len)
    286 		growstackblock();
    287 	q = stackblock();
    288 	if (p != start) {
    289 		memcpy(q, start, p - start);
    290 		q += p - start;
    291 		*q++ = '/';
    292 	}
    293 	strcpy(q, name);
    294 	pathopt = NULL;
    295 	if (*p == '%') {
    296 		pathopt = ++p;
    297 		while (*p && *p != ':')  p++;
    298 	}
    299 	if (*p == ':')
    300 		*path = p + 1;
    301 	else
    302 		*path = NULL;
    303 	return stalloc(len);
    304 }
    305 
    306 
    307 
    308 /*** Command hashing code ***/
    309 
    310 
    311 hashcmd(argc, argv)  char **argv; {
    312 	struct tblentry **pp;
    313 	struct tblentry *cmdp;
    314 	int c;
    315 	int verbose;
    316 	struct cmdentry entry;
    317 	char *name;
    318 
    319 	verbose = 0;
    320 	while ((c = nextopt("rv")) != '\0') {
    321 		if (c == 'r') {
    322 			clearcmdentry(0);
    323 		} else if (c == 'v') {
    324 			verbose++;
    325 		}
    326 	}
    327 	if (*argptr == NULL) {
    328 		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
    329 			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
    330 				printentry(cmdp, verbose);
    331 			}
    332 		}
    333 		return 0;
    334 	}
    335 	while ((name = *argptr) != NULL) {
    336 		if ((cmdp = cmdlookup(name, 0)) != NULL
    337 		 && (cmdp->cmdtype == CMDNORMAL
    338 		     || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
    339 			delete_cmd_entry();
    340 		find_command(name, &entry, 1);
    341 		if (verbose) {
    342 			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
    343 				cmdp = cmdlookup(name, 0);
    344 				printentry(cmdp, verbose);
    345 			}
    346 			flushall();
    347 		}
    348 		argptr++;
    349 	}
    350 	return 0;
    351 }
    352 
    353 
    354 STATIC void
    355 printentry(cmdp, verbose)
    356 	struct tblentry *cmdp;
    357 	int verbose;
    358 	{
    359 	int index;
    360 	char *path;
    361 	char *name;
    362 
    363 	if (cmdp->cmdtype == CMDNORMAL) {
    364 		index = cmdp->param.index;
    365 		path = pathval();
    366 		do {
    367 			name = padvance(&path, cmdp->cmdname);
    368 			stunalloc(name);
    369 		} while (--index >= 0);
    370 		out1str(name);
    371 	} else if (cmdp->cmdtype == CMDBUILTIN) {
    372 		out1fmt("builtin %s", cmdp->cmdname);
    373 	} else if (cmdp->cmdtype == CMDFUNCTION) {
    374 		out1fmt("function %s", cmdp->cmdname);
    375 		if (verbose) {
    376 			INTOFF;
    377 			name = commandtext(cmdp->param.func);
    378 			out1c(' ');
    379 			out1str(name);
    380 			ckfree(name);
    381 			INTON;
    382 		}
    383 #ifdef DEBUG
    384 	} else {
    385 		error("internal error: cmdtype %d", cmdp->cmdtype);
    386 #endif
    387 	}
    388 	if (cmdp->rehash)
    389 		out1c('*');
    390 	out1c('\n');
    391 }
    392 
    393 
    394 
    395 /*
    396  * Resolve a command name.  If you change this routine, you may have to
    397  * change the shellexec routine as well.
    398  */
    399 
    400 void
    401 find_command(name, entry, printerr)
    402 	char *name;
    403 	struct cmdentry *entry;
    404 	{
    405 	struct tblentry *cmdp;
    406 	int index;
    407 	int prev;
    408 	char *path;
    409 	char *fullname;
    410 	struct stat statb;
    411 	int e;
    412 	int i;
    413 
    414 	/* If name contains a slash, don't use the hash table */
    415 	if (strchr(name, '/') != NULL) {
    416 		entry->cmdtype = CMDNORMAL;
    417 		entry->u.index = 0;
    418 		return;
    419 	}
    420 
    421 	/* If name is in the table, and not invalidated by cd, we're done */
    422 	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
    423 		goto success;
    424 
    425 	/* If %builtin not in path, check for builtin next */
    426 	if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
    427 		INTOFF;
    428 		cmdp = cmdlookup(name, 1);
    429 		cmdp->cmdtype = CMDBUILTIN;
    430 		cmdp->param.index = i;
    431 		INTON;
    432 		goto success;
    433 	}
    434 
    435 	/* We have to search path. */
    436 	prev = -1;		/* where to start */
    437 	if (cmdp) {		/* doing a rehash */
    438 		if (cmdp->cmdtype == CMDBUILTIN)
    439 			prev = builtinloc;
    440 		else
    441 			prev = cmdp->param.index;
    442 	}
    443 
    444 	path = pathval();
    445 	e = ENOENT;
    446 	index = -1;
    447 loop:
    448 	while ((fullname = padvance(&path, name)) != NULL) {
    449 		stunalloc(fullname);
    450 		index++;
    451 		if (pathopt) {
    452 			if (prefix("builtin", pathopt)) {
    453 				if ((i = find_builtin(name)) < 0)
    454 					goto loop;
    455 				INTOFF;
    456 				cmdp = cmdlookup(name, 1);
    457 				cmdp->cmdtype = CMDBUILTIN;
    458 				cmdp->param.index = i;
    459 				INTON;
    460 				goto success;
    461 			} else if (prefix("func", pathopt)) {
    462 				/* handled below */
    463 			} else {
    464 				goto loop;	/* ignore unimplemented options */
    465 			}
    466 		}
    467 		/* if rehash, don't redo absolute path names */
    468 		if (fullname[0] == '/' && index <= prev) {
    469 			if (index < prev)
    470 				goto loop;
    471 			TRACE(("searchexec \"%s\": no change\n", name));
    472 			goto success;
    473 		}
    474 		while (stat(fullname, &statb) < 0) {
    475 #ifdef SYSV
    476 			if (errno == EINTR)
    477 				continue;
    478 #endif
    479 			if (errno != ENOENT && errno != ENOTDIR)
    480 				e = errno;
    481 			goto loop;
    482 		}
    483 		e = EACCES;	/* if we fail, this will be the error */
    484 		if ((statb.st_mode & S_IFMT) != S_IFREG)
    485 			goto loop;
    486 		if (pathopt) {		/* this is a %func directory */
    487 			stalloc(strlen(fullname) + 1);
    488 			readcmdfile(fullname);
    489 			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
    490 				error("%s not defined in %s", name, fullname);
    491 			stunalloc(fullname);
    492 			goto success;
    493 		}
    494 #ifdef notdef
    495 		if (statb.st_uid == geteuid()) {
    496 			if ((statb.st_mode & 0100) == 0)
    497 				goto loop;
    498 		} else if (statb.st_gid == getegid()) {
    499 			if ((statb.st_mode & 010) == 0)
    500 				goto loop;
    501 		} else {
    502 			if ((statb.st_mode & 01) == 0)
    503 				goto loop;
    504 		}
    505 #endif
    506 		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
    507 		INTOFF;
    508 		cmdp = cmdlookup(name, 1);
    509 		cmdp->cmdtype = CMDNORMAL;
    510 		cmdp->param.index = index;
    511 		INTON;
    512 		goto success;
    513 	}
    514 
    515 	/* We failed.  If there was an entry for this command, delete it */
    516 	if (cmdp)
    517 		delete_cmd_entry();
    518 	if (printerr)
    519 		outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
    520 	entry->cmdtype = CMDUNKNOWN;
    521 	return;
    522 
    523 success:
    524 	cmdp->rehash = 0;
    525 	entry->cmdtype = cmdp->cmdtype;
    526 	entry->u = cmdp->param;
    527 }
    528 
    529 
    530 
    531 /*
    532  * Search the table of builtin commands.
    533  */
    534 
    535 int
    536 find_builtin(name)
    537 	char *name;
    538 	{
    539 	const register struct builtincmd *bp;
    540 
    541 	for (bp = builtincmd ; bp->name ; bp++) {
    542 		if (*bp->name == *name && equal(bp->name, name))
    543 			return bp->code;
    544 	}
    545 	return -1;
    546 }
    547 
    548 
    549 
    550 /*
    551  * Called when a cd is done.  Marks all commands so the next time they
    552  * are executed they will be rehashed.
    553  */
    554 
    555 void
    556 hashcd() {
    557 	struct tblentry **pp;
    558 	struct tblentry *cmdp;
    559 
    560 	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
    561 		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
    562 			if (cmdp->cmdtype == CMDNORMAL
    563 			 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
    564 				cmdp->rehash = 1;
    565 		}
    566 	}
    567 }
    568 
    569 
    570 
    571 /*
    572  * Called before PATH is changed.  The argument is the new value of PATH;
    573  * pathval() still returns the old value at this point.  Called with
    574  * interrupts off.
    575  */
    576 
    577 void
    578 changepath(newval)
    579 	char *newval;
    580 	{
    581 	char *old, *new;
    582 	int index;
    583 	int firstchange;
    584 	int bltin;
    585 	int hasdot;
    586 
    587 	old = pathval();
    588 	new = newval;
    589 	firstchange = 9999;	/* assume no change */
    590 	index = hasdot = 0;
    591 	bltin = -1;
    592 	if (*new == ':')
    593 		hasdot++;
    594 	for (;;) {
    595 		if (*old != *new) {
    596 			firstchange = index;
    597 			if (*old == '\0' && *new == ':'
    598 			 || *old == ':' && *new == '\0')
    599 				firstchange++;
    600 			old = new;	/* ignore subsequent differences */
    601 		}
    602 		if (*new == '\0')
    603 			break;
    604 		if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
    605 			bltin = index;
    606 		if (*new == ':') {
    607 			char c = *(new+1);
    608 
    609 			index++;
    610 			if (c == ':' || c == '\0' || (c == '.' &&
    611 			   ((c = *(new+2)) == ':' || c == '\0')))
    612 				hasdot++;
    613 		}
    614 		new++, old++;
    615 	}
    616 	if (hasdot && geteuid() == 0)
    617 		out2str("sh: warning: running as root with dot in PATH\n");
    618 	if (builtinloc < 0 && bltin >= 0)
    619 		builtinloc = bltin;		/* zap builtins */
    620 	if (builtinloc >= 0 && bltin < 0)
    621 		firstchange = 0;
    622 	clearcmdentry(firstchange);
    623 	builtinloc = bltin;
    624 }
    625 
    626 
    627 /*
    628  * Clear out command entries.  The argument specifies the first entry in
    629  * PATH which has changed.
    630  */
    631 
    632 STATIC void
    633 clearcmdentry(firstchange) {
    634 	struct tblentry **tblp;
    635 	struct tblentry **pp;
    636 	struct tblentry *cmdp;
    637 
    638 	INTOFF;
    639 	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
    640 		pp = tblp;
    641 		while ((cmdp = *pp) != NULL) {
    642 			if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
    643 			 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
    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;
    705 	{
    706 	int hashval;
    707 	register char *p;
    708 	struct tblentry *cmdp;
    709 	struct tblentry **pp;
    710 
    711 	p = name;
    712 	hashval = *p << 4;
    713 	while (*p)
    714 		hashval += *p++;
    715 	hashval &= 0x7FFF;
    716 	pp = &cmdtable[hashval % CMDTABLESIZE];
    717 	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
    718 		if (equal(cmdp->cmdname, name))
    719 			break;
    720 		pp = &cmdp->next;
    721 	}
    722 	if (add && cmdp == NULL) {
    723 		INTOFF;
    724 		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
    725 					+ strlen(name) + 1);
    726 		cmdp->next = NULL;
    727 		cmdp->cmdtype = CMDUNKNOWN;
    728 		cmdp->rehash = 0;
    729 		strcpy(cmdp->cmdname, name);
    730 		INTON;
    731 	}
    732 	lastcmdentry = pp;
    733 	return cmdp;
    734 }
    735 
    736 /*
    737  * Delete the command entry returned on the last lookup.
    738  */
    739 
    740 STATIC void
    741 delete_cmd_entry() {
    742 	struct tblentry *cmdp;
    743 
    744 	INTOFF;
    745 	cmdp = *lastcmdentry;
    746 	*lastcmdentry = cmdp->next;
    747 	ckfree(cmdp);
    748 	INTON;
    749 }
    750 
    751 
    752 
    753 #ifdef notdef
    754 void
    755 getcmdentry(name, entry)
    756 	char *name;
    757 	struct cmdentry *entry;
    758 	{
    759 	struct tblentry *cmdp = cmdlookup(name, 0);
    760 
    761 	if (cmdp) {
    762 		entry->u = cmdp->param;
    763 		entry->cmdtype = cmdp->cmdtype;
    764 	} else {
    765 		entry->cmdtype = CMDUNKNOWN;
    766 		entry->u.index = 0;
    767 	}
    768 }
    769 #endif
    770 
    771 
    772 /*
    773  * Add a new command entry, replacing any existing command entry for
    774  * the same name.
    775  */
    776 
    777 void
    778 addcmdentry(name, entry)
    779 	char *name;
    780 	struct cmdentry *entry;
    781 	{
    782 	struct tblentry *cmdp;
    783 
    784 	INTOFF;
    785 	cmdp = cmdlookup(name, 1);
    786 	if (cmdp->cmdtype == CMDFUNCTION) {
    787 		freefunc(cmdp->param.func);
    788 	}
    789 	cmdp->cmdtype = entry->cmdtype;
    790 	cmdp->param = entry->u;
    791 	INTON;
    792 }
    793 
    794 
    795 /*
    796  * Define a shell function.
    797  */
    798 
    799 void
    800 defun(name, func)
    801 	char *name;
    802 	union node *func;
    803 	{
    804 	struct cmdentry entry;
    805 
    806 	INTOFF;
    807 	entry.cmdtype = CMDFUNCTION;
    808 	entry.u.func = copyfunc(func);
    809 	addcmdentry(name, &entry);
    810 	INTON;
    811 }
    812 
    813 
    814 /*
    815  * Delete a function if it exists.
    816  */
    817 
    818 int
    819 unsetfunc(name)
    820 	char *name;
    821 	{
    822 	struct tblentry *cmdp;
    823 
    824 	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
    825 		freefunc(cmdp->param.func);
    826 		delete_cmd_entry();
    827 		return (0);
    828 	}
    829 	return (1);
    830 }
    831