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" 6747114Sbostic #include <sys/types.h> 6847114Sbostic #include <sys/stat.h> 6947114Sbostic #include <fcntl.h> 7047114Sbostic #include <errno.h> 7147114Sbostic 7247114Sbostic 7347114Sbostic #define CMDTABLESIZE 31 /* should be prime */ 7447114Sbostic #define ARB 1 /* actual size determined at run time */ 7547114Sbostic 7647114Sbostic 7747114Sbostic 7847114Sbostic struct tblentry { 7947114Sbostic struct tblentry *next; /* next entry in hash chain */ 8047114Sbostic union param param; /* definition of builtin function */ 8147114Sbostic short cmdtype; /* index identifying command */ 8247114Sbostic char rehash; /* if set, cd done since entry created */ 8347114Sbostic char cmdname[ARB]; /* name of command */ 8447114Sbostic }; 8547114Sbostic 8647114Sbostic 8747114Sbostic STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 8847293Smarc STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 8947114Sbostic 9047114Sbostic 9147114Sbostic #ifdef __STDC__ 9247114Sbostic STATIC void tryexec(char *, char **, char **); 9347114Sbostic STATIC void execinterp(char **, char **); 9447114Sbostic STATIC void printentry(struct tblentry *); 9547114Sbostic STATIC void clearcmdentry(int); 9647114Sbostic STATIC struct tblentry *cmdlookup(char *, int); 9747114Sbostic STATIC void delete_cmd_entry(void); 9847114Sbostic #else 9947114Sbostic STATIC void tryexec(); 10047114Sbostic STATIC void execinterp(); 10147114Sbostic STATIC void printentry(); 10247114Sbostic STATIC void clearcmdentry(); 10347114Sbostic STATIC struct tblentry *cmdlookup(); 10447114Sbostic STATIC void delete_cmd_entry(); 10547114Sbostic #endif 10647114Sbostic 10747114Sbostic 10847114Sbostic 10947114Sbostic /* 11047114Sbostic * Exec a program. Never returns. If you change this routine, you may 11147114Sbostic * have to change the find_command routine as well. 11247114Sbostic */ 11347114Sbostic 11447114Sbostic void 11547114Sbostic shellexec(argv, envp, path, index) 11647114Sbostic char **argv, **envp; 11747114Sbostic char *path; 11847114Sbostic { 11947114Sbostic char *cmdname; 12047114Sbostic int e; 12147114Sbostic 12247114Sbostic if (strchr(argv[0], '/') != NULL) { 12347114Sbostic tryexec(argv[0], argv, envp); 12447114Sbostic e = errno; 12547114Sbostic } else { 12647114Sbostic e = ENOENT; 12747114Sbostic while ((cmdname = padvance(&path, argv[0])) != NULL) { 12847114Sbostic if (--index < 0 && pathopt == NULL) { 12947114Sbostic tryexec(cmdname, argv, envp); 13047114Sbostic if (errno != ENOENT && errno != ENOTDIR) 13147114Sbostic e = errno; 13247114Sbostic } 13347114Sbostic stunalloc(cmdname); 13447114Sbostic } 13547114Sbostic } 13647114Sbostic error2(argv[0], errmsg(e, E_EXEC)); 13747114Sbostic } 13847114Sbostic 13947114Sbostic 14047114Sbostic STATIC void 14147114Sbostic tryexec(cmd, argv, envp) 14247114Sbostic char *cmd; 14347114Sbostic char **argv; 14447114Sbostic char **envp; 14547114Sbostic { 14647114Sbostic int e; 14747114Sbostic char *p; 14847114Sbostic 14947114Sbostic #ifdef SYSV 15047114Sbostic do { 15147114Sbostic execve(cmd, argv, envp); 15247114Sbostic } while (errno == EINTR); 15347114Sbostic #else 15447114Sbostic execve(cmd, argv, envp); 15547114Sbostic #endif 15647114Sbostic e = errno; 15747114Sbostic if (e == ENOEXEC) { 15847114Sbostic initshellproc(); 15947114Sbostic setinputfile(cmd, 0); 16047114Sbostic commandname = arg0 = savestr(argv[0]); 16147114Sbostic #ifndef BSD 16247114Sbostic pgetc(); pungetc(); /* fill up input buffer */ 16347114Sbostic p = parsenextc; 16447114Sbostic if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 16547114Sbostic argv[0] = cmd; 16647114Sbostic execinterp(argv, envp); 16747114Sbostic } 16847114Sbostic #endif 16947114Sbostic setparam(argv + 1); 17047114Sbostic exraise(EXSHELLPROC); 17147114Sbostic /*NOTREACHED*/ 17247114Sbostic } 17347114Sbostic errno = e; 17447114Sbostic } 17547114Sbostic 17647114Sbostic 17747114Sbostic #ifndef BSD 17847114Sbostic /* 17947114Sbostic * Execute an interpreter introduced by "#!", for systems where this 18047114Sbostic * feature has not been built into the kernel. If the interpreter is 18147114Sbostic * the shell, return (effectively ignoring the "#!"). If the execution 18247114Sbostic * of the interpreter fails, exit. 18347114Sbostic * 18447114Sbostic * This code peeks inside the input buffer in order to avoid actually 18547114Sbostic * reading any input. It would benefit from a rewrite. 18647114Sbostic */ 18747114Sbostic 18847114Sbostic #define NEWARGS 5 18947114Sbostic 19047114Sbostic STATIC void 19147114Sbostic execinterp(argv, envp) 19247114Sbostic char **argv, **envp; 19347114Sbostic { 19447114Sbostic int n; 19547114Sbostic char *inp; 19647114Sbostic char *outp; 19747114Sbostic char c; 19847114Sbostic char *p; 19947114Sbostic char **ap; 20047114Sbostic char *newargs[NEWARGS]; 20147114Sbostic int i; 20247114Sbostic char **ap2; 20347114Sbostic char **new; 20447114Sbostic 20547114Sbostic n = parsenleft - 2; 20647114Sbostic inp = parsenextc + 2; 20747114Sbostic ap = newargs; 20847114Sbostic for (;;) { 20947114Sbostic while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 21047114Sbostic inp++; 21147114Sbostic if (n < 0) 21247114Sbostic goto bad; 21347114Sbostic if ((c = *inp++) == '\n') 21447114Sbostic break; 21547114Sbostic if (ap == &newargs[NEWARGS]) 21647114Sbostic bad: error("Bad #! line"); 21747114Sbostic STARTSTACKSTR(outp); 21847114Sbostic do { 21947114Sbostic STPUTC(c, outp); 22047114Sbostic } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 22147114Sbostic STPUTC('\0', outp); 22247114Sbostic n++, inp--; 22347114Sbostic *ap++ = grabstackstr(outp); 22447114Sbostic } 22547114Sbostic if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 22647114Sbostic p = newargs[0]; 22747114Sbostic for (;;) { 22847114Sbostic if (equal(p, "sh") || equal(p, "ash")) { 22947114Sbostic return; 23047114Sbostic } 23147114Sbostic while (*p != '/') { 23247114Sbostic if (*p == '\0') 23347114Sbostic goto break2; 23447114Sbostic p++; 23547114Sbostic } 23647114Sbostic p++; 23747114Sbostic } 23847114Sbostic break2:; 23947114Sbostic } 24047114Sbostic i = (char *)ap - (char *)newargs; /* size in bytes */ 24147114Sbostic if (i == 0) 24247114Sbostic error("Bad #! line"); 24347114Sbostic for (ap2 = argv ; *ap2++ != NULL ; ); 24447114Sbostic new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 24547114Sbostic ap = newargs, ap2 = new; 24647114Sbostic while ((i -= sizeof (char **)) >= 0) 24747114Sbostic *ap2++ = *ap++; 24847114Sbostic ap = argv; 24947114Sbostic while (*ap2++ = *ap++); 25047114Sbostic shellexec(new, envp, pathval(), 0); 25147114Sbostic } 25247114Sbostic #endif 25347114Sbostic 25447114Sbostic 25547114Sbostic 25647114Sbostic /* 25747114Sbostic * Do a path search. The variable path (passed by reference) should be 25847114Sbostic * set to the start of the path before the first call; padvance will update 25947114Sbostic * this value as it proceeds. Successive calls to padvance will return 26047114Sbostic * the possible path expansions in sequence. If an option (indicated by 26147114Sbostic * a percent sign) appears in the path entry then the global variable 26247114Sbostic * pathopt will be set to point to it; otherwise pathopt will be set to 26347114Sbostic * NULL. 26447114Sbostic */ 26547114Sbostic 26647114Sbostic char *pathopt; 26747114Sbostic 26847114Sbostic char * 26947114Sbostic padvance(path, name) 27047114Sbostic char **path; 27147114Sbostic char *name; 27247114Sbostic { 27347114Sbostic register char *p, *q; 27447114Sbostic char *start; 27547114Sbostic int len; 27647114Sbostic 27747114Sbostic if (*path == NULL) 27847114Sbostic return NULL; 27947114Sbostic start = *path; 28047114Sbostic for (p = start ; *p && *p != ':' && *p != '%' ; p++); 28147114Sbostic len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 28247114Sbostic while (stackblocksize() < len) 28347114Sbostic growstackblock(); 28447114Sbostic q = stackblock(); 28547114Sbostic if (p != start) { 28647114Sbostic bcopy(start, q, p - start); 28747114Sbostic q += p - start; 28847114Sbostic *q++ = '/'; 28947114Sbostic } 29047114Sbostic strcpy(q, name); 29147114Sbostic pathopt = NULL; 29247114Sbostic if (*p == '%') { 29347114Sbostic pathopt = ++p; 29447114Sbostic while (*p && *p != ':') p++; 29547114Sbostic } 29647114Sbostic if (*p == ':') 29747114Sbostic *path = p + 1; 29847114Sbostic else 29947114Sbostic *path = NULL; 30047114Sbostic return stalloc(len); 30147114Sbostic } 30247114Sbostic 30347114Sbostic 30447114Sbostic 30547114Sbostic /*** Command hashing code ***/ 30647114Sbostic 30747114Sbostic 30847114Sbostic hashcmd(argc, argv) char **argv; { 30947114Sbostic struct tblentry **pp; 31047114Sbostic struct tblentry *cmdp; 31147114Sbostic int c; 31247114Sbostic int verbose; 31347114Sbostic struct cmdentry entry; 31447114Sbostic char *name; 31547114Sbostic 31647114Sbostic if (argc <= 1) { 31747114Sbostic for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 31847114Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 31947114Sbostic printentry(cmdp); 32047114Sbostic } 32147114Sbostic } 32247114Sbostic return 0; 32347114Sbostic } 32447114Sbostic verbose = 0; 32547293Smarc while ((c = nextopt("rv")) != '\0') { 32647114Sbostic if (c == 'r') { 32747114Sbostic clearcmdentry(0); 32847114Sbostic } else if (c == 'v') { 32947114Sbostic verbose++; 33047114Sbostic } 33147114Sbostic } 33247114Sbostic while ((name = *argptr) != NULL) { 33347114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL 33447114Sbostic && (cmdp->cmdtype == CMDNORMAL 33547114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 33647114Sbostic delete_cmd_entry(); 33747114Sbostic find_command(name, &entry, 1); 33847114Sbostic if (verbose) { 33947114Sbostic if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 34047114Sbostic cmdp = cmdlookup(name, 0); 34147114Sbostic printentry(cmdp); 34247114Sbostic } 34347114Sbostic flushall(); 34447114Sbostic } 34547114Sbostic argptr++; 34647114Sbostic } 34747114Sbostic return 0; 34847114Sbostic } 34947114Sbostic 35047114Sbostic 35147114Sbostic STATIC void 35247114Sbostic printentry(cmdp) 35347114Sbostic struct tblentry *cmdp; 35447114Sbostic { 35547114Sbostic int index; 35647114Sbostic char *path; 35747114Sbostic char *name; 35847114Sbostic 35947114Sbostic if (cmdp->cmdtype == CMDNORMAL) { 36047114Sbostic index = cmdp->param.index; 36147114Sbostic path = pathval(); 36247114Sbostic do { 36347114Sbostic name = padvance(&path, cmdp->cmdname); 36447114Sbostic stunalloc(name); 36547114Sbostic } while (--index >= 0); 36647114Sbostic out1str(name); 36747114Sbostic } else if (cmdp->cmdtype == CMDBUILTIN) { 36847114Sbostic out1fmt("builtin %s", cmdp->cmdname); 36947114Sbostic } else if (cmdp->cmdtype == CMDFUNCTION) { 37047114Sbostic out1fmt("function %s", cmdp->cmdname); 37147114Sbostic #ifdef DEBUG 37247114Sbostic } else { 37347114Sbostic error("internal error: cmdtype %d", cmdp->cmdtype); 37447114Sbostic #endif 37547114Sbostic } 37647114Sbostic if (cmdp->rehash) 37747114Sbostic out1c('*'); 37847114Sbostic out1c('\n'); 37947114Sbostic } 38047114Sbostic 38147114Sbostic 38247114Sbostic 38347114Sbostic /* 38447114Sbostic * Resolve a command name. If you change this routine, you may have to 38547114Sbostic * change the shellexec routine as well. 38647114Sbostic */ 38747114Sbostic 38847114Sbostic void 38947114Sbostic find_command(name, entry, printerr) 39047114Sbostic char *name; 39147114Sbostic struct cmdentry *entry; 39247114Sbostic { 39347114Sbostic struct tblentry *cmdp; 39447114Sbostic int index; 39547114Sbostic int prev; 39647114Sbostic char *path; 39747114Sbostic char *fullname; 39847114Sbostic struct stat statb; 39947114Sbostic int e; 40047114Sbostic int i; 40147114Sbostic 40247114Sbostic /* If name contains a slash, don't use the hash table */ 40347114Sbostic if (strchr(name, '/') != NULL) { 40447114Sbostic entry->cmdtype = CMDNORMAL; 40547114Sbostic entry->u.index = 0; 40647114Sbostic return; 40747114Sbostic } 40847114Sbostic 40947114Sbostic /* If name is in the table, and not invalidated by cd, we're done */ 41047114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 41147114Sbostic goto success; 41247114Sbostic 41347114Sbostic /* If %builtin not in path, check for builtin next */ 41447114Sbostic if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 41547114Sbostic INTOFF; 41647114Sbostic cmdp = cmdlookup(name, 1); 41747114Sbostic cmdp->cmdtype = CMDBUILTIN; 41847114Sbostic cmdp->param.index = i; 41947114Sbostic INTON; 42047114Sbostic goto success; 42147114Sbostic } 42247114Sbostic 42347114Sbostic /* We have to search path. */ 42447114Sbostic prev = -1; /* where to start */ 42547114Sbostic if (cmdp) { /* doing a rehash */ 42647114Sbostic if (cmdp->cmdtype == CMDBUILTIN) 42747114Sbostic prev = builtinloc; 42847114Sbostic else 42947114Sbostic prev = cmdp->param.index; 43047114Sbostic } 43147114Sbostic 43247114Sbostic path = pathval(); 43347114Sbostic e = ENOENT; 43447114Sbostic index = -1; 43547114Sbostic loop: 43647114Sbostic while ((fullname = padvance(&path, name)) != NULL) { 43747114Sbostic stunalloc(fullname); 43847114Sbostic index++; 43947114Sbostic if (pathopt) { 44047114Sbostic if (prefix("builtin", pathopt)) { 44147114Sbostic if ((i = find_builtin(name)) < 0) 44247114Sbostic goto loop; 44347114Sbostic INTOFF; 44447114Sbostic cmdp = cmdlookup(name, 1); 44547114Sbostic cmdp->cmdtype = CMDBUILTIN; 44647114Sbostic cmdp->param.index = i; 44747114Sbostic INTON; 44847114Sbostic goto success; 44947114Sbostic } else if (prefix("func", pathopt)) { 45047114Sbostic /* handled below */ 45147114Sbostic } else { 45247114Sbostic goto loop; /* ignore unimplemented options */ 45347114Sbostic } 45447114Sbostic } 45547114Sbostic /* if rehash, don't redo absolute path names */ 45647114Sbostic if (fullname[0] == '/' && index <= prev) { 45747114Sbostic if (index < prev) 45847114Sbostic goto loop; 45947114Sbostic TRACE(("searchexec \"%s\": no change\n", name)); 46047114Sbostic goto success; 46147114Sbostic } 46247114Sbostic while (stat(fullname, &statb) < 0) { 46347114Sbostic #ifdef SYSV 46447114Sbostic if (errno == EINTR) 46547114Sbostic continue; 46647114Sbostic #endif 46747114Sbostic if (errno != ENOENT && errno != ENOTDIR) 46847114Sbostic e = errno; 46947114Sbostic goto loop; 47047114Sbostic } 47147114Sbostic e = EACCES; /* if we fail, this will be the error */ 47247114Sbostic if ((statb.st_mode & S_IFMT) != S_IFREG) 47347114Sbostic goto loop; 47447114Sbostic if (pathopt) { /* this is a %func directory */ 47547114Sbostic stalloc(strlen(fullname) + 1); 47647114Sbostic readcmdfile(fullname); 47747114Sbostic if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 47847114Sbostic error("%s not defined in %s", name, fullname); 47947114Sbostic stunalloc(fullname); 48047114Sbostic goto success; 48147114Sbostic } 48247114Sbostic if (statb.st_uid == geteuid()) { 48347114Sbostic if ((statb.st_mode & 0100) == 0) 48447114Sbostic goto loop; 48547114Sbostic } else if (statb.st_gid == getegid()) { 48647114Sbostic if ((statb.st_mode & 010) == 0) 48747114Sbostic goto loop; 48847114Sbostic } else { 48947114Sbostic if ((statb.st_mode & 01) == 0) 49047114Sbostic goto loop; 49147114Sbostic } 49247114Sbostic TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 49347114Sbostic INTOFF; 49447114Sbostic cmdp = cmdlookup(name, 1); 49547114Sbostic cmdp->cmdtype = CMDNORMAL; 49647114Sbostic cmdp->param.index = index; 49747114Sbostic INTON; 49847114Sbostic goto success; 49947114Sbostic } 50047114Sbostic 50147114Sbostic /* We failed. If there was an entry for this command, delete it */ 50247114Sbostic if (cmdp) 50347114Sbostic delete_cmd_entry(); 50447114Sbostic if (printerr) 50547114Sbostic outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 50647114Sbostic entry->cmdtype = CMDUNKNOWN; 50747114Sbostic return; 50847114Sbostic 50947114Sbostic success: 51047114Sbostic cmdp->rehash = 0; 51147114Sbostic entry->cmdtype = cmdp->cmdtype; 51247114Sbostic entry->u = cmdp->param; 51347114Sbostic } 51447114Sbostic 51547114Sbostic 51647114Sbostic 51747114Sbostic /* 51847114Sbostic * Search the table of builtin commands. 51947114Sbostic */ 52047114Sbostic 52147114Sbostic int 52247114Sbostic find_builtin(name) 52347114Sbostic char *name; 52447114Sbostic { 52547114Sbostic const register struct builtincmd *bp; 52647114Sbostic 52747114Sbostic for (bp = builtincmd ; bp->name ; bp++) { 52847114Sbostic if (*bp->name == *name && equal(bp->name, name)) 52947114Sbostic return bp->code; 53047114Sbostic } 53147114Sbostic return -1; 53247114Sbostic } 53347114Sbostic 53447114Sbostic 53547114Sbostic 53647114Sbostic /* 53747114Sbostic * Called when a cd is done. Marks all commands so the next time they 53847114Sbostic * are executed they will be rehashed. 53947114Sbostic */ 54047114Sbostic 54147114Sbostic void 54247114Sbostic hashcd() { 54347114Sbostic struct tblentry **pp; 54447114Sbostic struct tblentry *cmdp; 54547114Sbostic 54647114Sbostic for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 54747114Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 54847114Sbostic if (cmdp->cmdtype == CMDNORMAL 54947114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) 55047114Sbostic cmdp->rehash = 1; 55147114Sbostic } 55247114Sbostic } 55347114Sbostic } 55447114Sbostic 55547114Sbostic 55647114Sbostic 55747114Sbostic /* 55847114Sbostic * Called before PATH is changed. The argument is the new value of PATH; 55947114Sbostic * pathval() still returns the old value at this point. Called with 56047114Sbostic * interrupts off. 56147114Sbostic */ 56247114Sbostic 56347114Sbostic void 56447114Sbostic changepath(newval) 56547114Sbostic char *newval; 56647114Sbostic { 56747114Sbostic char *old, *new; 56847114Sbostic int index; 56947114Sbostic int firstchange; 57047114Sbostic int bltin; 57153180Smarc int hasdot; 57247114Sbostic 57347114Sbostic old = pathval(); 57447114Sbostic new = newval; 57547114Sbostic firstchange = 9999; /* assume no change */ 57653180Smarc index = hasdot = 0; 57747114Sbostic bltin = -1; 57853180Smarc if (*new == ':') 57953180Smarc hasdot++; 58047114Sbostic for (;;) { 58147114Sbostic if (*old != *new) { 58247114Sbostic firstchange = index; 58347114Sbostic if (*old == '\0' && *new == ':' 58447114Sbostic || *old == ':' && *new == '\0') 58547114Sbostic firstchange++; 58647114Sbostic old = new; /* ignore subsequent differences */ 58747114Sbostic } 58847114Sbostic if (*new == '\0') 58947114Sbostic break; 59047114Sbostic if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 59147114Sbostic bltin = index; 59247114Sbostic if (*new == ':') { 59353180Smarc char c = *(new+1); 59453180Smarc 59547114Sbostic index++; 59653180Smarc if (c == ':' || c == '\0' || (c == '.' && 59753180Smarc ((c = *(new+2)) == ':' || c == '\0'))) 59853180Smarc hasdot++; 59947114Sbostic } 60047114Sbostic new++, old++; 60147114Sbostic } 60253180Smarc if (hasdot && geteuid() == 0) 60353180Smarc out2str("sh: warning: running as root with dot in PATH\n"); 60447114Sbostic if (builtinloc < 0 && bltin >= 0) 60547114Sbostic builtinloc = bltin; /* zap builtins */ 60647114Sbostic if (builtinloc >= 0 && bltin < 0) 60747114Sbostic firstchange = 0; 60847114Sbostic clearcmdentry(firstchange); 60947114Sbostic builtinloc = bltin; 61047114Sbostic } 61147114Sbostic 61247114Sbostic 61347114Sbostic /* 61447114Sbostic * Clear out command entries. The argument specifies the first entry in 61547114Sbostic * PATH which has changed. 61647114Sbostic */ 61747114Sbostic 61847114Sbostic STATIC void 61947114Sbostic clearcmdentry(firstchange) { 62047114Sbostic struct tblentry **tblp; 62147114Sbostic struct tblentry **pp; 62247114Sbostic struct tblentry *cmdp; 62347114Sbostic 62447114Sbostic INTOFF; 62547114Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 62647114Sbostic pp = tblp; 62747114Sbostic while ((cmdp = *pp) != NULL) { 62847114Sbostic if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange 62947114Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { 63047114Sbostic *pp = cmdp->next; 63147114Sbostic ckfree(cmdp); 63247114Sbostic } else { 63347114Sbostic pp = &cmdp->next; 63447114Sbostic } 63547114Sbostic } 63647114Sbostic } 63747114Sbostic INTON; 63847114Sbostic } 63947114Sbostic 64047114Sbostic 64147114Sbostic /* 64247114Sbostic * Delete all functions. 64347114Sbostic */ 64447114Sbostic 64547114Sbostic #ifdef mkinit 64647114Sbostic MKINIT void deletefuncs(); 64747114Sbostic 64847114Sbostic SHELLPROC { 64947114Sbostic deletefuncs(); 65047114Sbostic } 65147114Sbostic #endif 65247114Sbostic 65347114Sbostic void 65447114Sbostic deletefuncs() { 65547114Sbostic struct tblentry **tblp; 65647114Sbostic struct tblentry **pp; 65747114Sbostic struct tblentry *cmdp; 65847114Sbostic 65947114Sbostic INTOFF; 66047114Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 66147114Sbostic pp = tblp; 66247114Sbostic while ((cmdp = *pp) != NULL) { 66347114Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 66447114Sbostic *pp = cmdp->next; 66547114Sbostic freefunc(cmdp->param.func); 66647114Sbostic ckfree(cmdp); 66747114Sbostic } else { 66847114Sbostic pp = &cmdp->next; 66947114Sbostic } 67047114Sbostic } 67147114Sbostic } 67247114Sbostic INTON; 67347114Sbostic } 67447114Sbostic 67547114Sbostic 67647114Sbostic 67747114Sbostic /* 67847114Sbostic * Locate a command in the command hash table. If "add" is nonzero, 67947114Sbostic * add the command to the table if it is not already present. The 68047114Sbostic * variable "lastcmdentry" is set to point to the address of the link 68147114Sbostic * pointing to the entry, so that delete_cmd_entry can delete the 68247114Sbostic * entry. 68347114Sbostic */ 68447114Sbostic 68547114Sbostic struct tblentry **lastcmdentry; 68647114Sbostic 68747114Sbostic 68847114Sbostic STATIC struct tblentry * 68947114Sbostic cmdlookup(name, add) 69047114Sbostic char *name; 69147114Sbostic { 69247114Sbostic int hashval; 69347114Sbostic register char *p; 69447114Sbostic struct tblentry *cmdp; 69547114Sbostic struct tblentry **pp; 69647114Sbostic 69747114Sbostic p = name; 69847114Sbostic hashval = *p << 4; 69947114Sbostic while (*p) 70047114Sbostic hashval += *p++; 70147114Sbostic hashval &= 0x7FFF; 70247114Sbostic pp = &cmdtable[hashval % CMDTABLESIZE]; 70347114Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 70447114Sbostic if (equal(cmdp->cmdname, name)) 70547114Sbostic break; 70647114Sbostic pp = &cmdp->next; 70747114Sbostic } 70847114Sbostic if (add && cmdp == NULL) { 70947114Sbostic INTOFF; 71047114Sbostic cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 71147114Sbostic + strlen(name) + 1); 71247114Sbostic cmdp->next = NULL; 71347114Sbostic cmdp->cmdtype = CMDUNKNOWN; 71447114Sbostic cmdp->rehash = 0; 71547114Sbostic strcpy(cmdp->cmdname, name); 71647114Sbostic INTON; 71747114Sbostic } 71847114Sbostic lastcmdentry = pp; 71947114Sbostic return cmdp; 72047114Sbostic } 72147114Sbostic 72247114Sbostic 72347114Sbostic /* 72447114Sbostic * Delete the command entry returned on the last lookup. 72547114Sbostic */ 72647114Sbostic 72747114Sbostic STATIC void 72847114Sbostic delete_cmd_entry() { 72947114Sbostic struct tblentry *cmdp; 73047114Sbostic 73147114Sbostic INTOFF; 73247114Sbostic cmdp = *lastcmdentry; 73347114Sbostic *lastcmdentry = cmdp->next; 73447114Sbostic ckfree(cmdp); 73547114Sbostic INTON; 73647114Sbostic } 73747114Sbostic 73847114Sbostic 73947114Sbostic 74047114Sbostic #ifdef notdef 74147114Sbostic void 74247114Sbostic getcmdentry(name, entry) 74347114Sbostic char *name; 74447114Sbostic struct cmdentry *entry; 74547114Sbostic { 74647114Sbostic struct tblentry *cmdp = cmdlookup(name, 0); 74747114Sbostic 74847114Sbostic if (cmdp) { 74947114Sbostic entry->u = cmdp->param; 75047114Sbostic entry->cmdtype = cmdp->cmdtype; 75147114Sbostic } else { 75247114Sbostic entry->cmdtype = CMDUNKNOWN; 75347114Sbostic entry->u.index = 0; 75447114Sbostic } 75547114Sbostic } 75647114Sbostic #endif 75747114Sbostic 75847114Sbostic 75947114Sbostic /* 76047114Sbostic * Add a new command entry, replacing any existing command entry for 76147114Sbostic * the same name. 76247114Sbostic */ 76347114Sbostic 76447114Sbostic void 76547114Sbostic addcmdentry(name, entry) 76647114Sbostic char *name; 76747114Sbostic struct cmdentry *entry; 76847114Sbostic { 76947114Sbostic struct tblentry *cmdp; 77047114Sbostic 77147114Sbostic INTOFF; 77247114Sbostic cmdp = cmdlookup(name, 1); 77347114Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 77447114Sbostic freefunc(cmdp->param.func); 77547114Sbostic } 77647114Sbostic cmdp->cmdtype = entry->cmdtype; 77747114Sbostic cmdp->param = entry->u; 77847114Sbostic INTON; 77947114Sbostic } 78047114Sbostic 78147114Sbostic 78247114Sbostic /* 78347114Sbostic * Define a shell function. 78447114Sbostic */ 78547114Sbostic 78647114Sbostic void 78747114Sbostic defun(name, func) 78847114Sbostic char *name; 78947114Sbostic union node *func; 79047114Sbostic { 79147114Sbostic struct cmdentry entry; 79247114Sbostic 79347114Sbostic INTOFF; 79447114Sbostic entry.cmdtype = CMDFUNCTION; 79547114Sbostic entry.u.func = copyfunc(func); 79647114Sbostic addcmdentry(name, &entry); 79747114Sbostic INTON; 79847114Sbostic } 79947114Sbostic 80047114Sbostic 80147114Sbostic /* 80247114Sbostic * Delete a function if it exists. 80347114Sbostic */ 80447114Sbostic 805*54328Smarc int 80647114Sbostic unsetfunc(name) 80747114Sbostic char *name; 80847114Sbostic { 80947114Sbostic struct tblentry *cmdp; 81047114Sbostic 81147114Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 81247114Sbostic freefunc(cmdp->param.func); 81347114Sbostic delete_cmd_entry(); 814*54328Smarc return (0); 81547114Sbostic } 816*54328Smarc return (1); 81747114Sbostic } 818