147114Sbostic /*- 247114Sbostic * Copyright (c) 1991 The Regents of the University of California. 347114Sbostic * All rights reserved. 447114Sbostic * 547114Sbostic * This code is derived from software contributed to Berkeley by 647114Sbostic * Kenneth Almquist. 747114Sbostic * 853180Smarc * Redistribution and use in source and binary forms, with or without 953180Smarc * modification, are permitted provided that the following conditions 1053180Smarc * are met: 1153180Smarc * 1. Redistributions of source code must retain the above copyright 1253180Smarc * notice, this list of conditions and the following disclaimer. 1353180Smarc * 2. Redistributions in binary form must reproduce the above copyright 1453180Smarc * notice, this list of conditions and the following disclaimer in the 1553180Smarc * documentation and/or other materials provided with the distribution. 1653180Smarc * 3. All advertising materials mentioning features or use of this software 1753180Smarc * must display the following acknowledgement: 1853180Smarc * This product includes software developed by the University of 1953180Smarc * California, Berkeley and its contributors. 2053180Smarc * 4. Neither the name of the University nor the names of its contributors 2153180Smarc * may be used to endorse or promote products derived from this software 2253180Smarc * without specific prior written permission. 2353180Smarc * 2453180Smarc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2553180Smarc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2653180Smarc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2753180Smarc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2853180Smarc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2953180Smarc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3053180Smarc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3153180Smarc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3253180Smarc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3353180Smarc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3453180Smarc * SUCH DAMAGE. 3547114Sbostic */ 3647114Sbostic 3747114Sbostic #ifndef lint 3853180Smarc static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91"; 3947114Sbostic #endif /* not lint */ 4047114Sbostic 4147114Sbostic /* 4247114Sbostic * When commands are first encountered, they are entered in a hash table. 4347114Sbostic * This ensures that a full path search will not have to be done for them 4447114Sbostic * on each invocation. 4547114Sbostic * 4647114Sbostic * We should investigate converting to a linear search, even though that 4747114Sbostic * would make the command name "hash" a misnomer. 4847114Sbostic */ 4947114Sbostic 5047114Sbostic #include "shell.h" 5147114Sbostic #include "main.h" 5247114Sbostic #include "nodes.h" 5347114Sbostic #include "parser.h" 5447114Sbostic #include "redir.h" 5547114Sbostic #include "eval.h" 5647114Sbostic #include "exec.h" 5747114Sbostic #include "builtins.h" 5847114Sbostic #include "var.h" 5947114Sbostic #include "options.h" 6047114Sbostic #include "input.h" 6147114Sbostic #include "output.h" 6247114Sbostic #include "syntax.h" 6347114Sbostic #include "memalloc.h" 6447114Sbostic #include "error.h" 6547114Sbostic #include "init.h" 6647114Sbostic #include "mystring.h" 67*55302Smarc #include "jobs.h" 6847114Sbostic #include <sys/types.h> 6947114Sbostic #include <sys/stat.h> 7047114Sbostic #include <fcntl.h> 7147114Sbostic #include <errno.h> 7247114Sbostic 7347114Sbostic 7447114Sbostic #define CMDTABLESIZE 31 /* should be prime */ 7547114Sbostic #define ARB 1 /* actual size determined at run time */ 7647114Sbostic 7747114Sbostic 7847114Sbostic 7947114Sbostic struct tblentry { 8047114Sbostic struct tblentry *next; /* next entry in hash chain */ 8147114Sbostic union param param; /* definition of builtin function */ 8247114Sbostic short cmdtype; /* index identifying command */ 8347114Sbostic char rehash; /* if set, cd done since entry created */ 8447114Sbostic char cmdname[ARB]; /* name of command */ 8547114Sbostic }; 8647114Sbostic 8747114Sbostic 8847114Sbostic STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 8947293Smarc STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 9047114Sbostic 9147114Sbostic 9247114Sbostic #ifdef __STDC__ 9347114Sbostic STATIC void tryexec(char *, char **, char **); 9447114Sbostic STATIC void execinterp(char **, char **); 9555297Smarc STATIC void printentry(struct tblentry *, int); 9647114Sbostic STATIC void clearcmdentry(int); 9747114Sbostic STATIC struct tblentry *cmdlookup(char *, int); 9847114Sbostic STATIC void delete_cmd_entry(void); 9947114Sbostic #else 10047114Sbostic STATIC void tryexec(); 10147114Sbostic STATIC void execinterp(); 10247114Sbostic STATIC void printentry(); 10347114Sbostic STATIC void clearcmdentry(); 10447114Sbostic STATIC struct tblentry *cmdlookup(); 10547114Sbostic STATIC void delete_cmd_entry(); 10647114Sbostic #endif 10747114Sbostic 10847114Sbostic 10947114Sbostic 11047114Sbostic /* 11147114Sbostic * Exec a program. Never returns. If you change this routine, you may 11247114Sbostic * have to change the find_command routine as well. 11347114Sbostic */ 11447114Sbostic 11547114Sbostic void 11647114Sbostic shellexec(argv, envp, path, index) 11747114Sbostic char **argv, **envp; 11847114Sbostic char *path; 11947114Sbostic { 12047114Sbostic char *cmdname; 12147114Sbostic int e; 12247114Sbostic 12347114Sbostic if (strchr(argv[0], '/') != NULL) { 12447114Sbostic tryexec(argv[0], argv, envp); 12547114Sbostic e = errno; 12647114Sbostic } else { 12747114Sbostic e = ENOENT; 12847114Sbostic while ((cmdname = padvance(&path, argv[0])) != NULL) { 12947114Sbostic if (--index < 0 && pathopt == NULL) { 13047114Sbostic tryexec(cmdname, argv, envp); 13147114Sbostic if (errno != ENOENT && errno != ENOTDIR) 13247114Sbostic e = errno; 13347114Sbostic } 13447114Sbostic stunalloc(cmdname); 13547114Sbostic } 13647114Sbostic } 13747114Sbostic error2(argv[0], errmsg(e, E_EXEC)); 13847114Sbostic } 13947114Sbostic 14047114Sbostic 14147114Sbostic STATIC void 14247114Sbostic tryexec(cmd, argv, envp) 14347114Sbostic char *cmd; 14447114Sbostic char **argv; 14547114Sbostic char **envp; 14647114Sbostic { 14747114Sbostic int e; 14847114Sbostic char *p; 14947114Sbostic 15047114Sbostic #ifdef SYSV 15147114Sbostic do { 15247114Sbostic execve(cmd, argv, envp); 15347114Sbostic } while (errno == EINTR); 15447114Sbostic #else 15547114Sbostic execve(cmd, argv, envp); 15647114Sbostic #endif 15747114Sbostic e = errno; 15847114Sbostic if (e == ENOEXEC) { 15947114Sbostic initshellproc(); 16047114Sbostic setinputfile(cmd, 0); 16147114Sbostic commandname = arg0 = savestr(argv[0]); 16247114Sbostic #ifndef BSD 16347114Sbostic pgetc(); pungetc(); /* fill up input buffer */ 16447114Sbostic p = parsenextc; 16547114Sbostic if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 16647114Sbostic argv[0] = cmd; 16747114Sbostic execinterp(argv, envp); 16847114Sbostic } 16947114Sbostic #endif 17047114Sbostic setparam(argv + 1); 17147114Sbostic exraise(EXSHELLPROC); 17247114Sbostic /*NOTREACHED*/ 17347114Sbostic } 17447114Sbostic errno = e; 17547114Sbostic } 17647114Sbostic 17747114Sbostic 17847114Sbostic #ifndef BSD 17947114Sbostic /* 18047114Sbostic * Execute an interpreter introduced by "#!", for systems where this 18147114Sbostic * feature has not been built into the kernel. If the interpreter is 18247114Sbostic * the shell, return (effectively ignoring the "#!"). If the execution 18347114Sbostic * of the interpreter fails, exit. 18447114Sbostic * 18547114Sbostic * This code peeks inside the input buffer in order to avoid actually 18647114Sbostic * reading any input. It would benefit from a rewrite. 18747114Sbostic */ 18847114Sbostic 18947114Sbostic #define NEWARGS 5 19047114Sbostic 19147114Sbostic STATIC void 19247114Sbostic execinterp(argv, envp) 19347114Sbostic char **argv, **envp; 19447114Sbostic { 19547114Sbostic int n; 19647114Sbostic char *inp; 19747114Sbostic char *outp; 19847114Sbostic char c; 19947114Sbostic char *p; 20047114Sbostic char **ap; 20147114Sbostic char *newargs[NEWARGS]; 20247114Sbostic int i; 20347114Sbostic char **ap2; 20447114Sbostic char **new; 20547114Sbostic 20647114Sbostic n = parsenleft - 2; 20747114Sbostic inp = parsenextc + 2; 20847114Sbostic ap = newargs; 20947114Sbostic for (;;) { 21047114Sbostic while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 21147114Sbostic inp++; 21247114Sbostic if (n < 0) 21347114Sbostic goto bad; 21447114Sbostic if ((c = *inp++) == '\n') 21547114Sbostic break; 21647114Sbostic if (ap == &newargs[NEWARGS]) 21747114Sbostic bad: error("Bad #! line"); 21847114Sbostic STARTSTACKSTR(outp); 21947114Sbostic do { 22047114Sbostic STPUTC(c, outp); 22147114Sbostic } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 22247114Sbostic STPUTC('\0', outp); 22347114Sbostic n++, inp--; 22447114Sbostic *ap++ = grabstackstr(outp); 22547114Sbostic } 22647114Sbostic if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 22747114Sbostic p = newargs[0]; 22847114Sbostic for (;;) { 22947114Sbostic if (equal(p, "sh") || equal(p, "ash")) { 23047114Sbostic return; 23147114Sbostic } 23247114Sbostic while (*p != '/') { 23347114Sbostic if (*p == '\0') 23447114Sbostic goto break2; 23547114Sbostic p++; 23647114Sbostic } 23747114Sbostic p++; 23847114Sbostic } 23947114Sbostic break2:; 24047114Sbostic } 24147114Sbostic i = (char *)ap - (char *)newargs; /* size in bytes */ 24247114Sbostic if (i == 0) 24347114Sbostic error("Bad #! line"); 24447114Sbostic for (ap2 = argv ; *ap2++ != NULL ; ); 24547114Sbostic new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 24647114Sbostic ap = newargs, ap2 = new; 24747114Sbostic while ((i -= sizeof (char **)) >= 0) 24847114Sbostic *ap2++ = *ap++; 24947114Sbostic ap = argv; 25047114Sbostic while (*ap2++ = *ap++); 25147114Sbostic shellexec(new, envp, pathval(), 0); 25247114Sbostic } 25347114Sbostic #endif 25447114Sbostic 25547114Sbostic 25647114Sbostic 25747114Sbostic /* 25847114Sbostic * Do a path search. The variable path (passed by reference) should be 25947114Sbostic * set to the start of the path before the first call; padvance will update 26047114Sbostic * this value as it proceeds. Successive calls to padvance will return 26147114Sbostic * the possible path expansions in sequence. If an option (indicated by 26247114Sbostic * a percent sign) appears in the path entry then the global variable 26347114Sbostic * pathopt will be set to point to it; otherwise pathopt will be set to 26447114Sbostic * NULL. 26547114Sbostic */ 26647114Sbostic 26747114Sbostic char *pathopt; 26847114Sbostic 26947114Sbostic char * 27047114Sbostic padvance(path, name) 27147114Sbostic char **path; 27247114Sbostic char *name; 27347114Sbostic { 27447114Sbostic register char *p, *q; 27547114Sbostic char *start; 27647114Sbostic int len; 27747114Sbostic 27847114Sbostic if (*path == NULL) 27947114Sbostic return NULL; 28047114Sbostic start = *path; 28147114Sbostic for (p = start ; *p && *p != ':' && *p != '%' ; p++); 28247114Sbostic len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 28347114Sbostic while (stackblocksize() < len) 28447114Sbostic growstackblock(); 28547114Sbostic q = stackblock(); 28647114Sbostic if (p != start) { 28747114Sbostic bcopy(start, q, p - start); 28847114Sbostic q += p - start; 28947114Sbostic *q++ = '/'; 29047114Sbostic } 29147114Sbostic strcpy(q, name); 29247114Sbostic pathopt = NULL; 29347114Sbostic if (*p == '%') { 29447114Sbostic pathopt = ++p; 29547114Sbostic while (*p && *p != ':') p++; 29647114Sbostic } 29747114Sbostic if (*p == ':') 29847114Sbostic *path = p + 1; 29947114Sbostic else 30047114Sbostic *path = NULL; 30147114Sbostic return stalloc(len); 30247114Sbostic } 30347114Sbostic 30447114Sbostic 30547114Sbostic 30647114Sbostic /*** Command hashing code ***/ 30747114Sbostic 30847114Sbostic 30947114Sbostic hashcmd(argc, argv) char **argv; { 31047114Sbostic struct tblentry **pp; 31147114Sbostic struct tblentry *cmdp; 31247114Sbostic int c; 31347114Sbostic int verbose; 31447114Sbostic struct cmdentry entry; 31547114Sbostic char *name; 31647114Sbostic 31747114Sbostic verbose = 0; 31847293Smarc while ((c = nextopt("rv")) != '\0') { 31947114Sbostic if (c == 'r') { 32047114Sbostic clearcmdentry(0); 32147114Sbostic } else if (c == 'v') { 32247114Sbostic verbose++; 32347114Sbostic } 32447114Sbostic } 32555297Smarc if (*argptr == NULL) { 32655297Smarc for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 32755297Smarc for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 32855297Smarc printentry(cmdp, verbose); 32955297Smarc } 33055297Smarc } 33155297Smarc return 0; 33255297Smarc } 33347114Sbostic while ((name = *argptr) != NULL) { 33447114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL 33547114Sbostic && (cmdp->cmdtype == CMDNORMAL 33647114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 33747114Sbostic delete_cmd_entry(); 33847114Sbostic find_command(name, &entry, 1); 33947114Sbostic if (verbose) { 34047114Sbostic if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 34147114Sbostic cmdp = cmdlookup(name, 0); 34255297Smarc printentry(cmdp, verbose); 34347114Sbostic } 34447114Sbostic flushall(); 34547114Sbostic } 34647114Sbostic argptr++; 34747114Sbostic } 34847114Sbostic return 0; 34947114Sbostic } 35047114Sbostic 35147114Sbostic 35247114Sbostic STATIC void 35355297Smarc printentry(cmdp, verbose) 35447114Sbostic struct tblentry *cmdp; 35555297Smarc int verbose; 35647114Sbostic { 35747114Sbostic int index; 35847114Sbostic char *path; 35947114Sbostic char *name; 36047114Sbostic 36147114Sbostic if (cmdp->cmdtype == CMDNORMAL) { 36247114Sbostic index = cmdp->param.index; 36347114Sbostic path = pathval(); 36447114Sbostic do { 36547114Sbostic name = padvance(&path, cmdp->cmdname); 36647114Sbostic stunalloc(name); 36747114Sbostic } while (--index >= 0); 36847114Sbostic out1str(name); 36947114Sbostic } else if (cmdp->cmdtype == CMDBUILTIN) { 37047114Sbostic out1fmt("builtin %s", cmdp->cmdname); 37147114Sbostic } else if (cmdp->cmdtype == CMDFUNCTION) { 37247114Sbostic out1fmt("function %s", cmdp->cmdname); 37355297Smarc if (verbose) { 37455297Smarc INTOFF; 37555297Smarc name = commandtext(cmdp->param.func); 37655297Smarc out1c(' '); 37755297Smarc out1str(name); 37855297Smarc ckfree(name); 37955297Smarc INTON; 38055297Smarc } 38147114Sbostic #ifdef DEBUG 38247114Sbostic } else { 38347114Sbostic error("internal error: cmdtype %d", cmdp->cmdtype); 38447114Sbostic #endif 38547114Sbostic } 38647114Sbostic if (cmdp->rehash) 38747114Sbostic out1c('*'); 38847114Sbostic out1c('\n'); 38947114Sbostic } 39047114Sbostic 39147114Sbostic 39247114Sbostic 39347114Sbostic /* 39447114Sbostic * Resolve a command name. If you change this routine, you may have to 39547114Sbostic * change the shellexec routine as well. 39647114Sbostic */ 39747114Sbostic 39847114Sbostic void 39947114Sbostic find_command(name, entry, printerr) 40047114Sbostic char *name; 40147114Sbostic struct cmdentry *entry; 40247114Sbostic { 40347114Sbostic struct tblentry *cmdp; 40447114Sbostic int index; 40547114Sbostic int prev; 40647114Sbostic char *path; 40747114Sbostic char *fullname; 40847114Sbostic struct stat statb; 40947114Sbostic int e; 41047114Sbostic int i; 41147114Sbostic 41247114Sbostic /* If name contains a slash, don't use the hash table */ 41347114Sbostic if (strchr(name, '/') != NULL) { 41447114Sbostic entry->cmdtype = CMDNORMAL; 41547114Sbostic entry->u.index = 0; 41647114Sbostic return; 41747114Sbostic } 41847114Sbostic 41947114Sbostic /* If name is in the table, and not invalidated by cd, we're done */ 42047114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 42147114Sbostic goto success; 42247114Sbostic 42347114Sbostic /* If %builtin not in path, check for builtin next */ 42447114Sbostic if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 42547114Sbostic INTOFF; 42647114Sbostic cmdp = cmdlookup(name, 1); 42747114Sbostic cmdp->cmdtype = CMDBUILTIN; 42847114Sbostic cmdp->param.index = i; 42947114Sbostic INTON; 43047114Sbostic goto success; 43147114Sbostic } 43247114Sbostic 43347114Sbostic /* We have to search path. */ 43447114Sbostic prev = -1; /* where to start */ 43547114Sbostic if (cmdp) { /* doing a rehash */ 43647114Sbostic if (cmdp->cmdtype == CMDBUILTIN) 43747114Sbostic prev = builtinloc; 43847114Sbostic else 43947114Sbostic prev = cmdp->param.index; 44047114Sbostic } 44147114Sbostic 44247114Sbostic path = pathval(); 44347114Sbostic e = ENOENT; 44447114Sbostic index = -1; 44547114Sbostic loop: 44647114Sbostic while ((fullname = padvance(&path, name)) != NULL) { 44747114Sbostic stunalloc(fullname); 44847114Sbostic index++; 44947114Sbostic if (pathopt) { 45047114Sbostic if (prefix("builtin", pathopt)) { 45147114Sbostic if ((i = find_builtin(name)) < 0) 45247114Sbostic goto loop; 45347114Sbostic INTOFF; 45447114Sbostic cmdp = cmdlookup(name, 1); 45547114Sbostic cmdp->cmdtype = CMDBUILTIN; 45647114Sbostic cmdp->param.index = i; 45747114Sbostic INTON; 45847114Sbostic goto success; 45947114Sbostic } else if (prefix("func", pathopt)) { 46047114Sbostic /* handled below */ 46147114Sbostic } else { 46247114Sbostic goto loop; /* ignore unimplemented options */ 46347114Sbostic } 46447114Sbostic } 46547114Sbostic /* if rehash, don't redo absolute path names */ 46647114Sbostic if (fullname[0] == '/' && index <= prev) { 46747114Sbostic if (index < prev) 46847114Sbostic goto loop; 46947114Sbostic TRACE(("searchexec \"%s\": no change\n", name)); 47047114Sbostic goto success; 47147114Sbostic } 47247114Sbostic while (stat(fullname, &statb) < 0) { 47347114Sbostic #ifdef SYSV 47447114Sbostic if (errno == EINTR) 47547114Sbostic continue; 47647114Sbostic #endif 47747114Sbostic if (errno != ENOENT && errno != ENOTDIR) 47847114Sbostic e = errno; 47947114Sbostic goto loop; 48047114Sbostic } 48147114Sbostic e = EACCES; /* if we fail, this will be the error */ 48247114Sbostic if ((statb.st_mode & S_IFMT) != S_IFREG) 48347114Sbostic goto loop; 48447114Sbostic if (pathopt) { /* this is a %func directory */ 48547114Sbostic stalloc(strlen(fullname) + 1); 48647114Sbostic readcmdfile(fullname); 48747114Sbostic if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 48847114Sbostic error("%s not defined in %s", name, fullname); 48947114Sbostic stunalloc(fullname); 49047114Sbostic goto success; 49147114Sbostic } 49255244Smarc #ifdef notdef 49347114Sbostic if (statb.st_uid == geteuid()) { 49447114Sbostic if ((statb.st_mode & 0100) == 0) 49547114Sbostic goto loop; 49647114Sbostic } else if (statb.st_gid == getegid()) { 49747114Sbostic if ((statb.st_mode & 010) == 0) 49847114Sbostic goto loop; 49947114Sbostic } else { 50047114Sbostic if ((statb.st_mode & 01) == 0) 50147114Sbostic goto loop; 50247114Sbostic } 50355244Smarc #endif 50447114Sbostic TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 50547114Sbostic INTOFF; 50647114Sbostic cmdp = cmdlookup(name, 1); 50747114Sbostic cmdp->cmdtype = CMDNORMAL; 50847114Sbostic cmdp->param.index = index; 50947114Sbostic INTON; 51047114Sbostic goto success; 51147114Sbostic } 51247114Sbostic 51347114Sbostic /* We failed. If there was an entry for this command, delete it */ 51447114Sbostic if (cmdp) 51547114Sbostic delete_cmd_entry(); 51647114Sbostic if (printerr) 51747114Sbostic outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 51847114Sbostic entry->cmdtype = CMDUNKNOWN; 51947114Sbostic return; 52047114Sbostic 52147114Sbostic success: 52247114Sbostic cmdp->rehash = 0; 52347114Sbostic entry->cmdtype = cmdp->cmdtype; 52447114Sbostic entry->u = cmdp->param; 52547114Sbostic } 52647114Sbostic 52747114Sbostic 52847114Sbostic 52947114Sbostic /* 53047114Sbostic * Search the table of builtin commands. 53147114Sbostic */ 53247114Sbostic 53347114Sbostic int 53447114Sbostic find_builtin(name) 53547114Sbostic char *name; 53647114Sbostic { 53747114Sbostic const register struct builtincmd *bp; 53847114Sbostic 53947114Sbostic for (bp = builtincmd ; bp->name ; bp++) { 54047114Sbostic if (*bp->name == *name && equal(bp->name, name)) 54147114Sbostic return bp->code; 54247114Sbostic } 54347114Sbostic return -1; 54447114Sbostic } 54547114Sbostic 54647114Sbostic 54747114Sbostic 54847114Sbostic /* 54947114Sbostic * Called when a cd is done. Marks all commands so the next time they 55047114Sbostic * are executed they will be rehashed. 55147114Sbostic */ 55247114Sbostic 55347114Sbostic void 55447114Sbostic hashcd() { 55547114Sbostic struct tblentry **pp; 55647114Sbostic struct tblentry *cmdp; 55747114Sbostic 55847114Sbostic for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 55947114Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 56047114Sbostic if (cmdp->cmdtype == CMDNORMAL 56147114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) 56247114Sbostic cmdp->rehash = 1; 56347114Sbostic } 56447114Sbostic } 56547114Sbostic } 56647114Sbostic 56747114Sbostic 56847114Sbostic 56947114Sbostic /* 57047114Sbostic * Called before PATH is changed. The argument is the new value of PATH; 57147114Sbostic * pathval() still returns the old value at this point. Called with 57247114Sbostic * interrupts off. 57347114Sbostic */ 57447114Sbostic 57547114Sbostic void 57647114Sbostic changepath(newval) 57747114Sbostic char *newval; 57847114Sbostic { 57947114Sbostic char *old, *new; 58047114Sbostic int index; 58147114Sbostic int firstchange; 58247114Sbostic int bltin; 58353180Smarc int hasdot; 58447114Sbostic 58547114Sbostic old = pathval(); 58647114Sbostic new = newval; 58747114Sbostic firstchange = 9999; /* assume no change */ 58853180Smarc index = hasdot = 0; 58947114Sbostic bltin = -1; 59053180Smarc if (*new == ':') 59153180Smarc hasdot++; 59247114Sbostic for (;;) { 59347114Sbostic if (*old != *new) { 59447114Sbostic firstchange = index; 59547114Sbostic if (*old == '\0' && *new == ':' 59647114Sbostic || *old == ':' && *new == '\0') 59747114Sbostic firstchange++; 59847114Sbostic old = new; /* ignore subsequent differences */ 59947114Sbostic } 60047114Sbostic if (*new == '\0') 60147114Sbostic break; 60247114Sbostic if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 60347114Sbostic bltin = index; 60447114Sbostic if (*new == ':') { 60553180Smarc char c = *(new+1); 60653180Smarc 60747114Sbostic index++; 60853180Smarc if (c == ':' || c == '\0' || (c == '.' && 60953180Smarc ((c = *(new+2)) == ':' || c == '\0'))) 61053180Smarc hasdot++; 61147114Sbostic } 61247114Sbostic new++, old++; 61347114Sbostic } 61453180Smarc if (hasdot && geteuid() == 0) 61553180Smarc out2str("sh: warning: running as root with dot in PATH\n"); 61647114Sbostic if (builtinloc < 0 && bltin >= 0) 61747114Sbostic builtinloc = bltin; /* zap builtins */ 61847114Sbostic if (builtinloc >= 0 && bltin < 0) 61947114Sbostic firstchange = 0; 62047114Sbostic clearcmdentry(firstchange); 62147114Sbostic builtinloc = bltin; 62247114Sbostic } 62347114Sbostic 62447114Sbostic 62547114Sbostic /* 62647114Sbostic * Clear out command entries. The argument specifies the first entry in 62747114Sbostic * PATH which has changed. 62847114Sbostic */ 62947114Sbostic 63047114Sbostic STATIC void 63147114Sbostic clearcmdentry(firstchange) { 63247114Sbostic struct tblentry **tblp; 63347114Sbostic struct tblentry **pp; 63447114Sbostic struct tblentry *cmdp; 63547114Sbostic 63647114Sbostic INTOFF; 63747114Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 63847114Sbostic pp = tblp; 63947114Sbostic while ((cmdp = *pp) != NULL) { 64047114Sbostic if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange 64147114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { 64247114Sbostic *pp = cmdp->next; 64347114Sbostic ckfree(cmdp); 64447114Sbostic } else { 64547114Sbostic pp = &cmdp->next; 64647114Sbostic } 64747114Sbostic } 64847114Sbostic } 64947114Sbostic INTON; 65047114Sbostic } 65147114Sbostic 65247114Sbostic 65347114Sbostic /* 65447114Sbostic * Delete all functions. 65547114Sbostic */ 65647114Sbostic 65747114Sbostic #ifdef mkinit 65847114Sbostic MKINIT void deletefuncs(); 65947114Sbostic 66047114Sbostic SHELLPROC { 66147114Sbostic deletefuncs(); 66247114Sbostic } 66347114Sbostic #endif 66447114Sbostic 66547114Sbostic void 66647114Sbostic deletefuncs() { 66747114Sbostic struct tblentry **tblp; 66847114Sbostic struct tblentry **pp; 66947114Sbostic struct tblentry *cmdp; 67047114Sbostic 67147114Sbostic INTOFF; 67247114Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 67347114Sbostic pp = tblp; 67447114Sbostic while ((cmdp = *pp) != NULL) { 67547114Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 67647114Sbostic *pp = cmdp->next; 67747114Sbostic freefunc(cmdp->param.func); 67847114Sbostic ckfree(cmdp); 67947114Sbostic } else { 68047114Sbostic pp = &cmdp->next; 68147114Sbostic } 68247114Sbostic } 68347114Sbostic } 68447114Sbostic INTON; 68547114Sbostic } 68647114Sbostic 68747114Sbostic 68847114Sbostic 68947114Sbostic /* 69047114Sbostic * Locate a command in the command hash table. If "add" is nonzero, 69147114Sbostic * add the command to the table if it is not already present. The 69247114Sbostic * variable "lastcmdentry" is set to point to the address of the link 69347114Sbostic * pointing to the entry, so that delete_cmd_entry can delete the 69447114Sbostic * entry. 69547114Sbostic */ 69647114Sbostic 69747114Sbostic struct tblentry **lastcmdentry; 69847114Sbostic 69947114Sbostic 70047114Sbostic STATIC struct tblentry * 70147114Sbostic cmdlookup(name, add) 70247114Sbostic char *name; 70347114Sbostic { 70447114Sbostic int hashval; 70547114Sbostic register char *p; 70647114Sbostic struct tblentry *cmdp; 70747114Sbostic struct tblentry **pp; 70847114Sbostic 70947114Sbostic p = name; 71047114Sbostic hashval = *p << 4; 71147114Sbostic while (*p) 71247114Sbostic hashval += *p++; 71347114Sbostic hashval &= 0x7FFF; 71447114Sbostic pp = &cmdtable[hashval % CMDTABLESIZE]; 71547114Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 71647114Sbostic if (equal(cmdp->cmdname, name)) 71747114Sbostic break; 71847114Sbostic pp = &cmdp->next; 71947114Sbostic } 72047114Sbostic if (add && cmdp == NULL) { 72147114Sbostic INTOFF; 72247114Sbostic cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 72347114Sbostic + strlen(name) + 1); 72447114Sbostic cmdp->next = NULL; 72547114Sbostic cmdp->cmdtype = CMDUNKNOWN; 72647114Sbostic cmdp->rehash = 0; 72747114Sbostic strcpy(cmdp->cmdname, name); 72847114Sbostic INTON; 72947114Sbostic } 73047114Sbostic lastcmdentry = pp; 73147114Sbostic return cmdp; 73247114Sbostic } 73347114Sbostic 73447114Sbostic /* 73547114Sbostic * Delete the command entry returned on the last lookup. 73647114Sbostic */ 73747114Sbostic 73847114Sbostic STATIC void 73947114Sbostic delete_cmd_entry() { 74047114Sbostic struct tblentry *cmdp; 74147114Sbostic 74247114Sbostic INTOFF; 74347114Sbostic cmdp = *lastcmdentry; 74447114Sbostic *lastcmdentry = cmdp->next; 74547114Sbostic ckfree(cmdp); 74647114Sbostic INTON; 74747114Sbostic } 74847114Sbostic 74947114Sbostic 75047114Sbostic 75147114Sbostic #ifdef notdef 75247114Sbostic void 75347114Sbostic getcmdentry(name, entry) 75447114Sbostic char *name; 75547114Sbostic struct cmdentry *entry; 75647114Sbostic { 75747114Sbostic struct tblentry *cmdp = cmdlookup(name, 0); 75847114Sbostic 75947114Sbostic if (cmdp) { 76047114Sbostic entry->u = cmdp->param; 76147114Sbostic entry->cmdtype = cmdp->cmdtype; 76247114Sbostic } else { 76347114Sbostic entry->cmdtype = CMDUNKNOWN; 76447114Sbostic entry->u.index = 0; 76547114Sbostic } 76647114Sbostic } 76747114Sbostic #endif 76847114Sbostic 76947114Sbostic 77047114Sbostic /* 77147114Sbostic * Add a new command entry, replacing any existing command entry for 77247114Sbostic * the same name. 77347114Sbostic */ 77447114Sbostic 77547114Sbostic void 77647114Sbostic addcmdentry(name, entry) 77747114Sbostic char *name; 77847114Sbostic struct cmdentry *entry; 77947114Sbostic { 78047114Sbostic struct tblentry *cmdp; 78147114Sbostic 78247114Sbostic INTOFF; 78347114Sbostic cmdp = cmdlookup(name, 1); 78447114Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 78547114Sbostic freefunc(cmdp->param.func); 78647114Sbostic } 78747114Sbostic cmdp->cmdtype = entry->cmdtype; 78847114Sbostic cmdp->param = entry->u; 78947114Sbostic INTON; 79047114Sbostic } 79147114Sbostic 79247114Sbostic 79347114Sbostic /* 79447114Sbostic * Define a shell function. 79547114Sbostic */ 79647114Sbostic 79747114Sbostic void 79847114Sbostic defun(name, func) 79947114Sbostic char *name; 80047114Sbostic union node *func; 80147114Sbostic { 80247114Sbostic struct cmdentry entry; 80347114Sbostic 80447114Sbostic INTOFF; 80547114Sbostic entry.cmdtype = CMDFUNCTION; 80647114Sbostic entry.u.func = copyfunc(func); 80747114Sbostic addcmdentry(name, &entry); 80847114Sbostic INTON; 80947114Sbostic } 81047114Sbostic 81147114Sbostic 81247114Sbostic /* 81347114Sbostic * Delete a function if it exists. 81447114Sbostic */ 81547114Sbostic 81654328Smarc int 81747114Sbostic unsetfunc(name) 81847114Sbostic char *name; 81947114Sbostic { 82047114Sbostic struct tblentry *cmdp; 82147114Sbostic 82247114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 82347114Sbostic freefunc(cmdp->param.func); 82447114Sbostic delete_cmd_entry(); 82554328Smarc return (0); 82647114Sbostic } 82754328Smarc return (1); 82847114Sbostic } 829