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