11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 331556Srgrimes#ifndef lint 3436150Scharnier#if 0 3536150Scharnierstatic char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 3636150Scharnier#endif 371556Srgrimes#endif /* not lint */ 3899110Sobrien#include <sys/cdefs.h> 3999110Sobrien__FBSDID("$FreeBSD$"); 401556Srgrimes 4117987Speter#include <sys/types.h> 4217987Speter#include <sys/stat.h> 4317987Speter#include <unistd.h> 4417987Speter#include <fcntl.h> 4517987Speter#include <errno.h> 46218306Sjilles#include <paths.h> 4717987Speter#include <stdlib.h> 4817987Speter 491556Srgrimes/* 501556Srgrimes * When commands are first encountered, they are entered in a hash table. 511556Srgrimes * This ensures that a full path search will not have to be done for them 521556Srgrimes * on each invocation. 531556Srgrimes * 541556Srgrimes * We should investigate converting to a linear search, even though that 551556Srgrimes * would make the command name "hash" a misnomer. 561556Srgrimes */ 571556Srgrimes 581556Srgrimes#include "shell.h" 591556Srgrimes#include "main.h" 601556Srgrimes#include "nodes.h" 611556Srgrimes#include "parser.h" 621556Srgrimes#include "redir.h" 631556Srgrimes#include "eval.h" 641556Srgrimes#include "exec.h" 651556Srgrimes#include "builtins.h" 661556Srgrimes#include "var.h" 671556Srgrimes#include "options.h" 681556Srgrimes#include "input.h" 691556Srgrimes#include "output.h" 701556Srgrimes#include "syntax.h" 711556Srgrimes#include "memalloc.h" 721556Srgrimes#include "error.h" 731556Srgrimes#include "mystring.h" 7417987Speter#include "show.h" 751556Srgrimes#include "jobs.h" 7625223Ssteve#include "alias.h" 771556Srgrimes 781556Srgrimes 791556Srgrimes#define CMDTABLESIZE 31 /* should be prime */ 801556Srgrimes 811556Srgrimes 821556Srgrimes 831556Srgrimesstruct tblentry { 841556Srgrimes struct tblentry *next; /* next entry in hash chain */ 851556Srgrimes union param param; /* definition of builtin function */ 86157601Sstefanf int special; /* flag for special builtin commands */ 87242620Sjilles signed char cmdtype; /* index identifying command */ 88242530Sjilles char cmdname[]; /* name of command */ 891556Srgrimes}; 901556Srgrimes 911556Srgrimes 92213760Sobrienstatic struct tblentry *cmdtable[CMDTABLESIZE]; 93245426Sjillesstatic int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */ 9420425Ssteveint exerrno = 0; /* Last exec error */ 951556Srgrimes 961556Srgrimes 97213811Sobrienstatic void tryexec(char *, char **, char **); 98213811Sobrienstatic void printentry(struct tblentry *, int); 99213811Sobrienstatic struct tblentry *cmdlookup(const char *, int); 100213811Sobrienstatic void delete_cmd_entry(void); 101229220Sjillesstatic void addcmdentry(const char *, struct cmdentry *); 1021556Srgrimes 1031556Srgrimes 1041556Srgrimes 1051556Srgrimes/* 1061556Srgrimes * Exec a program. Never returns. If you change this routine, you may 1071556Srgrimes * have to change the find_command routine as well. 108218306Sjilles * 109218306Sjilles * The argv array may be changed and element argv[-1] should be writable. 1101556Srgrimes */ 1111556Srgrimes 1121556Srgrimesvoid 113201053Sjillesshellexec(char **argv, char **envp, const char *path, int idx) 11417987Speter{ 1151556Srgrimes char *cmdname; 1161556Srgrimes int e; 1171556Srgrimes 1181556Srgrimes if (strchr(argv[0], '/') != NULL) { 1191556Srgrimes tryexec(argv[0], argv, envp); 1201556Srgrimes e = errno; 1211556Srgrimes } else { 1221556Srgrimes e = ENOENT; 1231556Srgrimes while ((cmdname = padvance(&path, argv[0])) != NULL) { 124201053Sjilles if (--idx < 0 && pathopt == NULL) { 1251556Srgrimes tryexec(cmdname, argv, envp); 1261556Srgrimes if (errno != ENOENT && errno != ENOTDIR) 1271556Srgrimes e = errno; 128218320Sjilles if (e == ENOEXEC) 129218320Sjilles break; 1301556Srgrimes } 1311556Srgrimes stunalloc(cmdname); 1321556Srgrimes } 1331556Srgrimes } 13420425Ssteve 13520425Ssteve /* Map to POSIX errors */ 136218242Sjilles if (e == ENOENT || e == ENOTDIR) { 137218242Sjilles exerrno = 127; 138218242Sjilles exerror(EXEXEC, "%s: not found", argv[0]); 139218242Sjilles } else { 14020425Ssteve exerrno = 126; 141218242Sjilles exerror(EXEXEC, "%s: %s", argv[0], strerror(e)); 14220425Ssteve } 1431556Srgrimes} 1441556Srgrimes 1451556Srgrimes 146213811Sobrienstatic void 14790111Simptryexec(char *cmd, char **argv, char **envp) 14890111Simp{ 149218320Sjilles int e, in; 150218320Sjilles ssize_t n; 151218320Sjilles char buf[256]; 1521556Srgrimes 1531556Srgrimes execve(cmd, argv, envp); 1541556Srgrimes e = errno; 1551556Srgrimes if (e == ENOEXEC) { 156218320Sjilles INTOFF; 157218320Sjilles in = open(cmd, O_RDONLY | O_NONBLOCK); 158218320Sjilles if (in != -1) { 159218320Sjilles n = pread(in, buf, sizeof buf, 0); 160218320Sjilles close(in); 161218320Sjilles if (n > 0 && memchr(buf, '\0', n) != NULL) { 162218320Sjilles errno = ENOEXEC; 163218320Sjilles return; 164218320Sjilles } 165218320Sjilles } 166218306Sjilles *argv = cmd; 167248980Sjilles *--argv = __DECONST(char *, _PATH_BSHELL); 168218306Sjilles execve(_PATH_BSHELL, argv, envp); 1691556Srgrimes } 1701556Srgrimes errno = e; 1711556Srgrimes} 1721556Srgrimes 1731556Srgrimes/* 1741556Srgrimes * Do a path search. The variable path (passed by reference) should be 1751556Srgrimes * set to the start of the path before the first call; padvance will update 1761556Srgrimes * this value as it proceeds. Successive calls to padvance will return 1771556Srgrimes * the possible path expansions in sequence. If an option (indicated by 1781556Srgrimes * a percent sign) appears in the path entry then the global variable 1791556Srgrimes * pathopt will be set to point to it; otherwise pathopt will be set to 1801556Srgrimes * NULL. 1811556Srgrimes */ 1821556Srgrimes 183200956Sjillesconst char *pathopt; 1841556Srgrimes 1851556Srgrimeschar * 186200956Sjillespadvance(const char **path, const char *name) 18790111Simp{ 188200956Sjilles const char *p, *start; 189200956Sjilles char *q; 190262951Sjmmv size_t len, namelen; 1911556Srgrimes 1921556Srgrimes if (*path == NULL) 1931556Srgrimes return NULL; 1941556Srgrimes start = *path; 195193223Srse for (p = start; *p && *p != ':' && *p != '%'; p++) 196193223Srse ; /* nothing */ 197262951Sjmmv namelen = strlen(name); 198262951Sjmmv len = p - start + namelen + 2; /* "2" is for '/' and '\0' */ 199216706Sjilles STARTSTACKSTR(q); 200216706Sjilles CHECKSTRSPACE(len, q); 2011556Srgrimes if (p != start) { 20217987Speter memcpy(q, start, p - start); 2031556Srgrimes q += p - start; 2041556Srgrimes *q++ = '/'; 2051556Srgrimes } 206262951Sjmmv memcpy(q, name, namelen + 1); 2071556Srgrimes pathopt = NULL; 2081556Srgrimes if (*p == '%') { 2091556Srgrimes pathopt = ++p; 2101556Srgrimes while (*p && *p != ':') p++; 2111556Srgrimes } 2121556Srgrimes if (*p == ':') 2131556Srgrimes *path = p + 1; 2141556Srgrimes else 2151556Srgrimes *path = NULL; 2161556Srgrimes return stalloc(len); 2171556Srgrimes} 2181556Srgrimes 2191556Srgrimes 2201556Srgrimes 2211556Srgrimes/*** Command hashing code ***/ 2221556Srgrimes 2231556Srgrimes 22417987Speterint 22590111Simphashcmd(int argc __unused, char **argv __unused) 22617987Speter{ 2271556Srgrimes struct tblentry **pp; 2281556Srgrimes struct tblentry *cmdp; 2291556Srgrimes int c; 2301556Srgrimes int verbose; 2311556Srgrimes struct cmdentry entry; 2321556Srgrimes char *name; 233231535Sjilles int errors; 2341556Srgrimes 235231535Sjilles errors = 0; 2361556Srgrimes verbose = 0; 2371556Srgrimes while ((c = nextopt("rv")) != '\0') { 2381556Srgrimes if (c == 'r') { 239218324Sjilles clearcmdentry(); 2401556Srgrimes } else if (c == 'v') { 2411556Srgrimes verbose++; 2421556Srgrimes } 2431556Srgrimes } 2441556Srgrimes if (*argptr == NULL) { 2451556Srgrimes for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 2461556Srgrimes for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 24798157Stjr if (cmdp->cmdtype == CMDNORMAL) 24898157Stjr printentry(cmdp, verbose); 2491556Srgrimes } 2501556Srgrimes } 2511556Srgrimes return 0; 2521556Srgrimes } 2531556Srgrimes while ((name = *argptr) != NULL) { 2541556Srgrimes if ((cmdp = cmdlookup(name, 0)) != NULL 255217206Sjilles && cmdp->cmdtype == CMDNORMAL) 2561556Srgrimes delete_cmd_entry(); 257204800Sjilles find_command(name, &entry, DO_ERR, pathval()); 258231535Sjilles if (entry.cmdtype == CMDUNKNOWN) 259231535Sjilles errors = 1; 260231535Sjilles else if (verbose) { 261231535Sjilles cmdp = cmdlookup(name, 0); 262231535Sjilles if (cmdp != NULL) 263231535Sjilles printentry(cmdp, verbose); 264231535Sjilles else { 265231535Sjilles outfmt(out2, "%s: not found\n", name); 266231535Sjilles errors = 1; 2671556Srgrimes } 2681556Srgrimes flushall(); 2691556Srgrimes } 2701556Srgrimes argptr++; 2711556Srgrimes } 272231535Sjilles return errors; 2731556Srgrimes} 2741556Srgrimes 2751556Srgrimes 276213811Sobrienstatic void 27790111Simpprintentry(struct tblentry *cmdp, int verbose) 27890111Simp{ 279201053Sjilles int idx; 280200956Sjilles const char *path; 2811556Srgrimes char *name; 2821556Srgrimes 2831556Srgrimes if (cmdp->cmdtype == CMDNORMAL) { 284201053Sjilles idx = cmdp->param.index; 2851556Srgrimes path = pathval(); 2861556Srgrimes do { 2871556Srgrimes name = padvance(&path, cmdp->cmdname); 2881556Srgrimes stunalloc(name); 289201053Sjilles } while (--idx >= 0); 2901556Srgrimes out1str(name); 2911556Srgrimes } else if (cmdp->cmdtype == CMDBUILTIN) { 2921556Srgrimes out1fmt("builtin %s", cmdp->cmdname); 2931556Srgrimes } else if (cmdp->cmdtype == CMDFUNCTION) { 2941556Srgrimes out1fmt("function %s", cmdp->cmdname); 2951556Srgrimes if (verbose) { 2961556Srgrimes INTOFF; 297196634Sjilles name = commandtext(getfuncnode(cmdp->param.func)); 2981556Srgrimes out1c(' '); 2991556Srgrimes out1str(name); 3001556Srgrimes ckfree(name); 3011556Srgrimes INTON; 3021556Srgrimes } 3031556Srgrimes#ifdef DEBUG 3041556Srgrimes } else { 3051556Srgrimes error("internal error: cmdtype %d", cmdp->cmdtype); 3061556Srgrimes#endif 3071556Srgrimes } 3081556Srgrimes out1c('\n'); 3091556Srgrimes} 3101556Srgrimes 3111556Srgrimes 3121556Srgrimes 3131556Srgrimes/* 3141556Srgrimes * Resolve a command name. If you change this routine, you may have to 3151556Srgrimes * change the shellexec routine as well. 3161556Srgrimes */ 3171556Srgrimes 3181556Srgrimesvoid 319204800Sjillesfind_command(const char *name, struct cmdentry *entry, int act, 320200956Sjilles const char *path) 32117987Speter{ 322204800Sjilles struct tblentry *cmdp, loc_cmd; 323201053Sjilles int idx; 3241556Srgrimes char *fullname; 3251556Srgrimes struct stat statb; 3261556Srgrimes int e; 3271556Srgrimes int i; 328157601Sstefanf int spec; 329245426Sjilles int cd; 3301556Srgrimes 3311556Srgrimes /* If name contains a slash, don't use the hash table */ 3321556Srgrimes if (strchr(name, '/') != NULL) { 3331556Srgrimes entry->cmdtype = CMDNORMAL; 3341556Srgrimes entry->u.index = 0; 3351556Srgrimes return; 3361556Srgrimes } 3371556Srgrimes 338245426Sjilles cd = 0; 339245426Sjilles 3401556Srgrimes /* If name is in the table, and not invalidated by cd, we're done */ 341245426Sjilles if ((cmdp = cmdlookup(name, 0)) != NULL) { 342204800Sjilles if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC) 343204800Sjilles cmdp = NULL; 344204800Sjilles else 345204800Sjilles goto success; 346204800Sjilles } 3471556Srgrimes 348217206Sjilles /* Check for builtin next */ 349217206Sjilles if ((i = find_builtin(name, &spec)) >= 0) { 3501556Srgrimes INTOFF; 3511556Srgrimes cmdp = cmdlookup(name, 1); 352204800Sjilles if (cmdp->cmdtype == CMDFUNCTION) 353204800Sjilles cmdp = &loc_cmd; 3541556Srgrimes cmdp->cmdtype = CMDBUILTIN; 3551556Srgrimes cmdp->param.index = i; 356157601Sstefanf cmdp->special = spec; 3571556Srgrimes INTON; 3581556Srgrimes goto success; 3591556Srgrimes } 3601556Srgrimes 3611556Srgrimes /* We have to search path. */ 3621556Srgrimes 3631556Srgrimes e = ENOENT; 364201053Sjilles idx = -1; 3651556Srgrimesloop: 3661556Srgrimes while ((fullname = padvance(&path, name)) != NULL) { 3671556Srgrimes stunalloc(fullname); 368201053Sjilles idx++; 3691556Srgrimes if (pathopt) { 370217206Sjilles if (prefix("func", pathopt)) { 3711556Srgrimes /* handled below */ 3721556Srgrimes } else { 3731556Srgrimes goto loop; /* ignore unimplemented options */ 3741556Srgrimes } 3751556Srgrimes } 376245426Sjilles if (fullname[0] != '/') 377245426Sjilles cd = 1; 378100351Stjr if (stat(fullname, &statb) < 0) { 3791556Srgrimes if (errno != ENOENT && errno != ENOTDIR) 3801556Srgrimes e = errno; 3811556Srgrimes goto loop; 3821556Srgrimes } 3831556Srgrimes e = EACCES; /* if we fail, this will be the error */ 38417987Speter if (!S_ISREG(statb.st_mode)) 3851556Srgrimes goto loop; 3861556Srgrimes if (pathopt) { /* this is a %func directory */ 3871556Srgrimes stalloc(strlen(fullname) + 1); 3881556Srgrimes readcmdfile(fullname); 3891556Srgrimes if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 3901556Srgrimes error("%s not defined in %s", name, fullname); 3911556Srgrimes stunalloc(fullname); 3921556Srgrimes goto success; 3931556Srgrimes } 3941556Srgrimes#ifdef notdef 3951556Srgrimes if (statb.st_uid == geteuid()) { 3961556Srgrimes if ((statb.st_mode & 0100) == 0) 3971556Srgrimes goto loop; 3981556Srgrimes } else if (statb.st_gid == getegid()) { 3991556Srgrimes if ((statb.st_mode & 010) == 0) 4001556Srgrimes goto loop; 4011556Srgrimes } else { 4021556Srgrimes if ((statb.st_mode & 01) == 0) 4031556Srgrimes goto loop; 4041556Srgrimes } 4051556Srgrimes#endif 4061556Srgrimes TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 4071556Srgrimes INTOFF; 4081556Srgrimes cmdp = cmdlookup(name, 1); 409204800Sjilles if (cmdp->cmdtype == CMDFUNCTION) 410204800Sjilles cmdp = &loc_cmd; 4111556Srgrimes cmdp->cmdtype = CMDNORMAL; 412201053Sjilles cmdp->param.index = idx; 4131556Srgrimes INTON; 4141556Srgrimes goto success; 4151556Srgrimes } 4161556Srgrimes 417204800Sjilles if (act & DO_ERR) { 418104283Stjr if (e == ENOENT || e == ENOTDIR) 419104283Stjr outfmt(out2, "%s: not found\n", name); 420104283Stjr else 421104283Stjr outfmt(out2, "%s: %s\n", name, strerror(e)); 422104283Stjr } 4231556Srgrimes entry->cmdtype = CMDUNKNOWN; 424197820Sjilles entry->u.index = 0; 4251556Srgrimes return; 4261556Srgrimes 4271556Srgrimessuccess: 428245426Sjilles if (cd) 429245426Sjilles cmdtable_cd = 1; 4301556Srgrimes entry->cmdtype = cmdp->cmdtype; 4311556Srgrimes entry->u = cmdp->param; 432157601Sstefanf entry->special = cmdp->special; 4331556Srgrimes} 4341556Srgrimes 4351556Srgrimes 4361556Srgrimes 4371556Srgrimes/* 4381556Srgrimes * Search the table of builtin commands. 4391556Srgrimes */ 4401556Srgrimes 4411556Srgrimesint 442200956Sjillesfind_builtin(const char *name, int *special) 44317987Speter{ 44425223Ssteve const struct builtincmd *bp; 4451556Srgrimes 4461556Srgrimes for (bp = builtincmd ; bp->name ; bp++) { 447157601Sstefanf if (*bp->name == *name && equal(bp->name, name)) { 448157601Sstefanf *special = bp->special; 4491556Srgrimes return bp->code; 450157601Sstefanf } 4511556Srgrimes } 4521556Srgrimes return -1; 4531556Srgrimes} 4541556Srgrimes 4551556Srgrimes 4561556Srgrimes 4571556Srgrimes/* 458245426Sjilles * Called when a cd is done. If any entry in cmdtable depends on the current 459245426Sjilles * directory, simply clear cmdtable completely. 4601556Srgrimes */ 4611556Srgrimes 4621556Srgrimesvoid 46390111Simphashcd(void) 46490111Simp{ 465245426Sjilles if (cmdtable_cd) 466245426Sjilles clearcmdentry(); 4671556Srgrimes} 4681556Srgrimes 4691556Srgrimes 4701556Srgrimes 4711556Srgrimes/* 4721556Srgrimes * Called before PATH is changed. The argument is the new value of PATH; 4731556Srgrimes * pathval() still returns the old value at this point. Called with 4741556Srgrimes * interrupts off. 4751556Srgrimes */ 4761556Srgrimes 4771556Srgrimesvoid 478230530Scharnierchangepath(const char *newval __unused) 47917987Speter{ 480218324Sjilles clearcmdentry(); 4811556Srgrimes} 4821556Srgrimes 4831556Srgrimes 4841556Srgrimes/* 4851556Srgrimes * Clear out command entries. The argument specifies the first entry in 4861556Srgrimes * PATH which has changed. 4871556Srgrimes */ 4881556Srgrimes 48954884Scracauervoid 490218324Sjillesclearcmdentry(void) 49117987Speter{ 4921556Srgrimes struct tblentry **tblp; 4931556Srgrimes struct tblentry **pp; 4941556Srgrimes struct tblentry *cmdp; 4951556Srgrimes 4961556Srgrimes INTOFF; 4971556Srgrimes for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 4981556Srgrimes pp = tblp; 4991556Srgrimes while ((cmdp = *pp) != NULL) { 500218324Sjilles if (cmdp->cmdtype == CMDNORMAL) { 5011556Srgrimes *pp = cmdp->next; 5021556Srgrimes ckfree(cmdp); 5031556Srgrimes } else { 5041556Srgrimes pp = &cmdp->next; 5051556Srgrimes } 5061556Srgrimes } 5071556Srgrimes } 508245426Sjilles cmdtable_cd = 0; 5091556Srgrimes INTON; 5101556Srgrimes} 5111556Srgrimes 5121556Srgrimes 5131556Srgrimes/* 5141556Srgrimes * Locate a command in the command hash table. If "add" is nonzero, 5151556Srgrimes * add the command to the table if it is not already present. The 5161556Srgrimes * variable "lastcmdentry" is set to point to the address of the link 5171556Srgrimes * pointing to the entry, so that delete_cmd_entry can delete the 5181556Srgrimes * entry. 5191556Srgrimes */ 5201556Srgrimes 521213760Sobrienstatic struct tblentry **lastcmdentry; 5221556Srgrimes 5231556Srgrimes 524213811Sobrienstatic struct tblentry * 525200956Sjillescmdlookup(const char *name, int add) 52617987Speter{ 5271556Srgrimes int hashval; 528200956Sjilles const char *p; 5291556Srgrimes struct tblentry *cmdp; 5301556Srgrimes struct tblentry **pp; 531262951Sjmmv size_t len; 5321556Srgrimes 5331556Srgrimes p = name; 5341556Srgrimes hashval = *p << 4; 5351556Srgrimes while (*p) 5361556Srgrimes hashval += *p++; 5371556Srgrimes hashval &= 0x7FFF; 5381556Srgrimes pp = &cmdtable[hashval % CMDTABLESIZE]; 5391556Srgrimes for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 5401556Srgrimes if (equal(cmdp->cmdname, name)) 5411556Srgrimes break; 5421556Srgrimes pp = &cmdp->next; 5431556Srgrimes } 5441556Srgrimes if (add && cmdp == NULL) { 5451556Srgrimes INTOFF; 546262951Sjmmv len = strlen(name); 547262951Sjmmv cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1); 5481556Srgrimes cmdp->next = NULL; 5491556Srgrimes cmdp->cmdtype = CMDUNKNOWN; 550262951Sjmmv memcpy(cmdp->cmdname, name, len + 1); 5511556Srgrimes INTON; 5521556Srgrimes } 5531556Srgrimes lastcmdentry = pp; 5541556Srgrimes return cmdp; 5551556Srgrimes} 5561556Srgrimes 5571556Srgrimes/* 5581556Srgrimes * Delete the command entry returned on the last lookup. 5591556Srgrimes */ 5601556Srgrimes 561213811Sobrienstatic void 56290111Simpdelete_cmd_entry(void) 56390111Simp{ 5641556Srgrimes struct tblentry *cmdp; 5651556Srgrimes 5661556Srgrimes INTOFF; 5671556Srgrimes cmdp = *lastcmdentry; 5681556Srgrimes *lastcmdentry = cmdp->next; 5691556Srgrimes ckfree(cmdp); 5701556Srgrimes INTON; 5711556Srgrimes} 5721556Srgrimes 5731556Srgrimes 5741556Srgrimes 5751556Srgrimes/* 5761556Srgrimes * Add a new command entry, replacing any existing command entry for 5771556Srgrimes * the same name. 5781556Srgrimes */ 5791556Srgrimes 580229220Sjillesstatic void 581200956Sjillesaddcmdentry(const char *name, struct cmdentry *entry) 58290111Simp{ 5831556Srgrimes struct tblentry *cmdp; 5841556Srgrimes 5851556Srgrimes INTOFF; 5861556Srgrimes cmdp = cmdlookup(name, 1); 5871556Srgrimes if (cmdp->cmdtype == CMDFUNCTION) { 588196483Sjilles unreffunc(cmdp->param.func); 5891556Srgrimes } 5901556Srgrimes cmdp->cmdtype = entry->cmdtype; 5911556Srgrimes cmdp->param = entry->u; 5921556Srgrimes INTON; 5931556Srgrimes} 5941556Srgrimes 5951556Srgrimes 5961556Srgrimes/* 5971556Srgrimes * Define a shell function. 5981556Srgrimes */ 5991556Srgrimes 6001556Srgrimesvoid 601200956Sjillesdefun(const char *name, union node *func) 60290111Simp{ 6031556Srgrimes struct cmdentry entry; 6041556Srgrimes 6051556Srgrimes INTOFF; 6061556Srgrimes entry.cmdtype = CMDFUNCTION; 6071556Srgrimes entry.u.func = copyfunc(func); 6081556Srgrimes addcmdentry(name, &entry); 6091556Srgrimes INTON; 6101556Srgrimes} 6111556Srgrimes 6121556Srgrimes 6131556Srgrimes/* 6141556Srgrimes * Delete a function if it exists. 615264478Sjilles * Called with interrupts off. 6161556Srgrimes */ 6171556Srgrimes 6181556Srgrimesint 619200956Sjillesunsetfunc(const char *name) 62090111Simp{ 6211556Srgrimes struct tblentry *cmdp; 6221556Srgrimes 6231556Srgrimes if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 624196483Sjilles unreffunc(cmdp->param.func); 6251556Srgrimes delete_cmd_entry(); 6261556Srgrimes return (0); 6271556Srgrimes } 628135856Sdes return (0); 6291556Srgrimes} 63025223Ssteve 631238468Sjilles 63225223Ssteve/* 633238468Sjilles * Check if a function by a certain name exists. 634238468Sjilles */ 635238468Sjillesint 636238468Sjillesisfunc(const char *name) 637238468Sjilles{ 638238468Sjilles struct tblentry *cmdp; 639238468Sjilles cmdp = cmdlookup(name, 0); 640238468Sjilles return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION); 641238468Sjilles} 642238468Sjilles 643238468Sjilles 644238468Sjilles/* 645151810Sstefanf * Shared code for the following builtin commands: 646151810Sstefanf * type, command -v, command -V 64725223Ssteve */ 64825223Ssteve 64925223Ssteveint 650201343Sjillestypecmd_impl(int argc, char **argv, int cmd, const char *path) 65125223Ssteve{ 65225223Ssteve struct cmdentry entry; 65325223Ssteve struct tblentry *cmdp; 654201053Sjilles const char *const *pp; 65525223Ssteve struct alias *ap; 65625223Ssteve int i; 657201053Sjilles int error1 = 0; 65825223Ssteve 659201343Sjilles if (path != pathval()) 660218324Sjilles clearcmdentry(); 661201343Sjilles 66225223Ssteve for (i = 1; i < argc; i++) { 66325223Ssteve /* First look at the keywords */ 664201053Sjilles for (pp = parsekwd; *pp; pp++) 66525223Ssteve if (**pp == *argv[i] && equal(*pp, argv[i])) 66625223Ssteve break; 66725223Ssteve 66825223Ssteve if (*pp) { 669151810Sstefanf if (cmd == TYPECMD_SMALLV) 670151810Sstefanf out1fmt("%s\n", argv[i]); 671151810Sstefanf else 672185401Sstefanf out1fmt("%s is a shell keyword\n", argv[i]); 67325223Ssteve continue; 67425223Ssteve } 67525223Ssteve 67625223Ssteve /* Then look at the aliases */ 67725223Ssteve if ((ap = lookupalias(argv[i], 1)) != NULL) { 678262951Sjmmv if (cmd == TYPECMD_SMALLV) { 679262951Sjmmv out1fmt("alias %s=", argv[i]); 680262951Sjmmv out1qstr(ap->val); 681262951Sjmmv outcslow('\n', out1); 682262951Sjmmv } else 683185401Sstefanf out1fmt("%s is an alias for %s\n", argv[i], 684185401Sstefanf ap->val); 68525223Ssteve continue; 68625223Ssteve } 68725223Ssteve 68825223Ssteve /* Then check if it is a tracked alias */ 68925223Ssteve if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 69025223Ssteve entry.cmdtype = cmdp->cmdtype; 69125223Ssteve entry.u = cmdp->param; 692194922Sjilles entry.special = cmdp->special; 69325223Ssteve } 69425223Ssteve else { 69525223Ssteve /* Finally use brute force */ 696201343Sjilles find_command(argv[i], &entry, 0, path); 69725223Ssteve } 69825223Ssteve 69925223Ssteve switch (entry.cmdtype) { 70025223Ssteve case CMDNORMAL: { 70164704Scracauer if (strchr(argv[i], '/') == NULL) { 702201343Sjilles const char *path2 = path; 703200956Sjilles char *name; 70464704Scracauer int j = entry.u.index; 70564704Scracauer do { 706201343Sjilles name = padvance(&path2, argv[i]); 70764704Scracauer stunalloc(name); 70864704Scracauer } while (--j >= 0); 709151810Sstefanf if (cmd == TYPECMD_SMALLV) 710151810Sstefanf out1fmt("%s\n", name); 711151810Sstefanf else 712185401Sstefanf out1fmt("%s is%s %s\n", argv[i], 713151810Sstefanf (cmdp && cmd == TYPECMD_TYPE) ? 714151810Sstefanf " a tracked alias for" : "", 715151810Sstefanf name); 71664704Scracauer } else { 717166101Sstefanf if (eaccess(argv[i], X_OK) == 0) { 718151810Sstefanf if (cmd == TYPECMD_SMALLV) 719151810Sstefanf out1fmt("%s\n", argv[i]); 720151810Sstefanf else 721185401Sstefanf out1fmt("%s is %s\n", argv[i], 722185401Sstefanf argv[i]); 723165930Sstefanf } else { 724165930Sstefanf if (cmd != TYPECMD_SMALLV) 725185401Sstefanf outfmt(out2, "%s: %s\n", 726185401Sstefanf argv[i], strerror(errno)); 727201053Sjilles error1 |= 127; 728151810Sstefanf } 72964704Scracauer } 73025223Ssteve break; 73125223Ssteve } 73225223Ssteve case CMDFUNCTION: 733151810Sstefanf if (cmd == TYPECMD_SMALLV) 734151810Sstefanf out1fmt("%s\n", argv[i]); 735151810Sstefanf else 736185401Sstefanf out1fmt("%s is a shell function\n", argv[i]); 73725223Ssteve break; 73825223Ssteve 73925223Ssteve case CMDBUILTIN: 740151810Sstefanf if (cmd == TYPECMD_SMALLV) 741151810Sstefanf out1fmt("%s\n", argv[i]); 742194922Sjilles else if (entry.special) 743194922Sjilles out1fmt("%s is a special shell builtin\n", 744194922Sjilles argv[i]); 745151810Sstefanf else 746185401Sstefanf out1fmt("%s is a shell builtin\n", argv[i]); 74725223Ssteve break; 74825223Ssteve 74925223Ssteve default: 750151810Sstefanf if (cmd != TYPECMD_SMALLV) 751185401Sstefanf outfmt(out2, "%s: not found\n", argv[i]); 752201053Sjilles error1 |= 127; 75325223Ssteve break; 75425223Ssteve } 75525223Ssteve } 756201343Sjilles 757201343Sjilles if (path != pathval()) 758218324Sjilles clearcmdentry(); 759201343Sjilles 760201053Sjilles return error1; 76125223Ssteve} 762151810Sstefanf 763151810Sstefanf/* 764151810Sstefanf * Locate and print what a word is... 765151810Sstefanf */ 766151810Sstefanf 767151810Sstefanfint 768151810Sstefanftypecmd(int argc, char **argv) 769151810Sstefanf{ 770255072Sjilles if (argc > 2 && strcmp(argv[1], "--") == 0) 771255072Sjilles argc--, argv++; 772201344Sjilles return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); 773151810Sstefanf} 774