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