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{ 527286813Sjilles unsigned int hashval; 528200956Sjilles const char *p; 5291556Srgrimes struct tblentry *cmdp; 5301556Srgrimes struct tblentry **pp; 531262951Sjmmv size_t len; 5321556Srgrimes 5331556Srgrimes p = name; 534286813Sjilles hashval = (unsigned char)*p << 4; 5351556Srgrimes while (*p) 5361556Srgrimes hashval += *p++; 5371556Srgrimes pp = &cmdtable[hashval % CMDTABLESIZE]; 5381556Srgrimes for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 5391556Srgrimes if (equal(cmdp->cmdname, name)) 5401556Srgrimes break; 5411556Srgrimes pp = &cmdp->next; 5421556Srgrimes } 5431556Srgrimes if (add && cmdp == NULL) { 5441556Srgrimes INTOFF; 545262951Sjmmv len = strlen(name); 546262951Sjmmv cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1); 5471556Srgrimes cmdp->next = NULL; 5481556Srgrimes cmdp->cmdtype = CMDUNKNOWN; 549262951Sjmmv memcpy(cmdp->cmdname, name, len + 1); 5501556Srgrimes INTON; 5511556Srgrimes } 5521556Srgrimes lastcmdentry = pp; 5531556Srgrimes return cmdp; 5541556Srgrimes} 5551556Srgrimes 5561556Srgrimes/* 5571556Srgrimes * Delete the command entry returned on the last lookup. 5581556Srgrimes */ 5591556Srgrimes 560213811Sobrienstatic void 56190111Simpdelete_cmd_entry(void) 56290111Simp{ 5631556Srgrimes struct tblentry *cmdp; 5641556Srgrimes 5651556Srgrimes INTOFF; 5661556Srgrimes cmdp = *lastcmdentry; 5671556Srgrimes *lastcmdentry = cmdp->next; 5681556Srgrimes ckfree(cmdp); 5691556Srgrimes INTON; 5701556Srgrimes} 5711556Srgrimes 5721556Srgrimes 5731556Srgrimes 5741556Srgrimes/* 5751556Srgrimes * Add a new command entry, replacing any existing command entry for 5761556Srgrimes * the same name. 5771556Srgrimes */ 5781556Srgrimes 579229220Sjillesstatic void 580200956Sjillesaddcmdentry(const char *name, struct cmdentry *entry) 58190111Simp{ 5821556Srgrimes struct tblentry *cmdp; 5831556Srgrimes 5841556Srgrimes INTOFF; 5851556Srgrimes cmdp = cmdlookup(name, 1); 5861556Srgrimes if (cmdp->cmdtype == CMDFUNCTION) { 587196483Sjilles unreffunc(cmdp->param.func); 5881556Srgrimes } 5891556Srgrimes cmdp->cmdtype = entry->cmdtype; 5901556Srgrimes cmdp->param = entry->u; 5911556Srgrimes INTON; 5921556Srgrimes} 5931556Srgrimes 5941556Srgrimes 5951556Srgrimes/* 5961556Srgrimes * Define a shell function. 5971556Srgrimes */ 5981556Srgrimes 5991556Srgrimesvoid 600200956Sjillesdefun(const char *name, union node *func) 60190111Simp{ 6021556Srgrimes struct cmdentry entry; 6031556Srgrimes 6041556Srgrimes INTOFF; 6051556Srgrimes entry.cmdtype = CMDFUNCTION; 6061556Srgrimes entry.u.func = copyfunc(func); 6071556Srgrimes addcmdentry(name, &entry); 6081556Srgrimes INTON; 6091556Srgrimes} 6101556Srgrimes 6111556Srgrimes 6121556Srgrimes/* 6131556Srgrimes * Delete a function if it exists. 614264478Sjilles * Called with interrupts off. 6151556Srgrimes */ 6161556Srgrimes 6171556Srgrimesint 618200956Sjillesunsetfunc(const char *name) 61990111Simp{ 6201556Srgrimes struct tblentry *cmdp; 6211556Srgrimes 6221556Srgrimes if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 623196483Sjilles unreffunc(cmdp->param.func); 6241556Srgrimes delete_cmd_entry(); 6251556Srgrimes return (0); 6261556Srgrimes } 627135856Sdes return (0); 6281556Srgrimes} 62925223Ssteve 630238468Sjilles 63125223Ssteve/* 632238468Sjilles * Check if a function by a certain name exists. 633238468Sjilles */ 634238468Sjillesint 635238468Sjillesisfunc(const char *name) 636238468Sjilles{ 637238468Sjilles struct tblentry *cmdp; 638238468Sjilles cmdp = cmdlookup(name, 0); 639238468Sjilles return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION); 640238468Sjilles} 641238468Sjilles 642238468Sjilles 643238468Sjilles/* 644151810Sstefanf * Shared code for the following builtin commands: 645151810Sstefanf * type, command -v, command -V 64625223Ssteve */ 64725223Ssteve 64825223Ssteveint 649201343Sjillestypecmd_impl(int argc, char **argv, int cmd, const char *path) 65025223Ssteve{ 65125223Ssteve struct cmdentry entry; 65225223Ssteve struct tblentry *cmdp; 653201053Sjilles const char *const *pp; 65425223Ssteve struct alias *ap; 65525223Ssteve int i; 656201053Sjilles int error1 = 0; 65725223Ssteve 658201343Sjilles if (path != pathval()) 659218324Sjilles clearcmdentry(); 660201343Sjilles 66125223Ssteve for (i = 1; i < argc; i++) { 66225223Ssteve /* First look at the keywords */ 663201053Sjilles for (pp = parsekwd; *pp; pp++) 66425223Ssteve if (**pp == *argv[i] && equal(*pp, argv[i])) 66525223Ssteve break; 66625223Ssteve 66725223Ssteve if (*pp) { 668151810Sstefanf if (cmd == TYPECMD_SMALLV) 669151810Sstefanf out1fmt("%s\n", argv[i]); 670151810Sstefanf else 671185401Sstefanf out1fmt("%s is a shell keyword\n", argv[i]); 67225223Ssteve continue; 67325223Ssteve } 67425223Ssteve 67525223Ssteve /* Then look at the aliases */ 67625223Ssteve if ((ap = lookupalias(argv[i], 1)) != NULL) { 677262951Sjmmv if (cmd == TYPECMD_SMALLV) { 678262951Sjmmv out1fmt("alias %s=", argv[i]); 679262951Sjmmv out1qstr(ap->val); 680262951Sjmmv outcslow('\n', out1); 681262951Sjmmv } else 682185401Sstefanf out1fmt("%s is an alias for %s\n", argv[i], 683185401Sstefanf ap->val); 68425223Ssteve continue; 68525223Ssteve } 68625223Ssteve 68725223Ssteve /* Then check if it is a tracked alias */ 68825223Ssteve if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 68925223Ssteve entry.cmdtype = cmdp->cmdtype; 69025223Ssteve entry.u = cmdp->param; 691194922Sjilles entry.special = cmdp->special; 69225223Ssteve } 69325223Ssteve else { 69425223Ssteve /* Finally use brute force */ 695201343Sjilles find_command(argv[i], &entry, 0, path); 69625223Ssteve } 69725223Ssteve 69825223Ssteve switch (entry.cmdtype) { 69925223Ssteve case CMDNORMAL: { 70064704Scracauer if (strchr(argv[i], '/') == NULL) { 701201343Sjilles const char *path2 = path; 702200956Sjilles char *name; 70364704Scracauer int j = entry.u.index; 70464704Scracauer do { 705201343Sjilles name = padvance(&path2, argv[i]); 70664704Scracauer stunalloc(name); 70764704Scracauer } while (--j >= 0); 708151810Sstefanf if (cmd == TYPECMD_SMALLV) 709151810Sstefanf out1fmt("%s\n", name); 710151810Sstefanf else 711185401Sstefanf out1fmt("%s is%s %s\n", argv[i], 712151810Sstefanf (cmdp && cmd == TYPECMD_TYPE) ? 713151810Sstefanf " a tracked alias for" : "", 714151810Sstefanf name); 71564704Scracauer } else { 716166101Sstefanf if (eaccess(argv[i], X_OK) == 0) { 717151810Sstefanf if (cmd == TYPECMD_SMALLV) 718151810Sstefanf out1fmt("%s\n", argv[i]); 719151810Sstefanf else 720185401Sstefanf out1fmt("%s is %s\n", argv[i], 721185401Sstefanf argv[i]); 722165930Sstefanf } else { 723165930Sstefanf if (cmd != TYPECMD_SMALLV) 724185401Sstefanf outfmt(out2, "%s: %s\n", 725185401Sstefanf argv[i], strerror(errno)); 726201053Sjilles error1 |= 127; 727151810Sstefanf } 72864704Scracauer } 72925223Ssteve break; 73025223Ssteve } 73125223Ssteve case CMDFUNCTION: 732151810Sstefanf if (cmd == TYPECMD_SMALLV) 733151810Sstefanf out1fmt("%s\n", argv[i]); 734151810Sstefanf else 735185401Sstefanf out1fmt("%s is a shell function\n", argv[i]); 73625223Ssteve break; 73725223Ssteve 73825223Ssteve case CMDBUILTIN: 739151810Sstefanf if (cmd == TYPECMD_SMALLV) 740151810Sstefanf out1fmt("%s\n", argv[i]); 741194922Sjilles else if (entry.special) 742194922Sjilles out1fmt("%s is a special shell builtin\n", 743194922Sjilles argv[i]); 744151810Sstefanf else 745185401Sstefanf out1fmt("%s is a shell builtin\n", argv[i]); 74625223Ssteve break; 74725223Ssteve 74825223Ssteve default: 749151810Sstefanf if (cmd != TYPECMD_SMALLV) 750185401Sstefanf outfmt(out2, "%s: not found\n", argv[i]); 751201053Sjilles error1 |= 127; 75225223Ssteve break; 75325223Ssteve } 75425223Ssteve } 755201343Sjilles 756201343Sjilles if (path != pathval()) 757218324Sjilles clearcmdentry(); 758201343Sjilles 759201053Sjilles return error1; 76025223Ssteve} 761151810Sstefanf 762151810Sstefanf/* 763151810Sstefanf * Locate and print what a word is... 764151810Sstefanf */ 765151810Sstefanf 766151810Sstefanfint 767151810Sstefanftypecmd(int argc, char **argv) 768151810Sstefanf{ 769255072Sjilles if (argc > 2 && strcmp(argv[1], "--") == 0) 770255072Sjilles argc--, argv++; 771201344Sjilles return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); 772151810Sstefanf} 773