1f2a20ce3Sbostic /*- 2f2a20ce3Sbostic * Copyright (c) 1991 The Regents of the University of California. 3f2a20ce3Sbostic * All rights reserved. 4f2a20ce3Sbostic * 5f2a20ce3Sbostic * This code is derived from software contributed to Berkeley by 6f2a20ce3Sbostic * Kenneth Almquist. 7f2a20ce3Sbostic * 8598f03dfSmarc * Redistribution and use in source and binary forms, with or without 9598f03dfSmarc * modification, are permitted provided that the following conditions 10598f03dfSmarc * are met: 11598f03dfSmarc * 1. Redistributions of source code must retain the above copyright 12598f03dfSmarc * notice, this list of conditions and the following disclaimer. 13598f03dfSmarc * 2. Redistributions in binary form must reproduce the above copyright 14598f03dfSmarc * notice, this list of conditions and the following disclaimer in the 15598f03dfSmarc * documentation and/or other materials provided with the distribution. 16598f03dfSmarc * 3. All advertising materials mentioning features or use of this software 17598f03dfSmarc * must display the following acknowledgement: 18598f03dfSmarc * This product includes software developed by the University of 19598f03dfSmarc * California, Berkeley and its contributors. 20598f03dfSmarc * 4. Neither the name of the University nor the names of its contributors 21598f03dfSmarc * may be used to endorse or promote products derived from this software 22598f03dfSmarc * without specific prior written permission. 23598f03dfSmarc * 24598f03dfSmarc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25598f03dfSmarc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26598f03dfSmarc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27598f03dfSmarc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28598f03dfSmarc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29598f03dfSmarc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30598f03dfSmarc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31598f03dfSmarc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32598f03dfSmarc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33598f03dfSmarc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34598f03dfSmarc * SUCH DAMAGE. 35f2a20ce3Sbostic */ 36f2a20ce3Sbostic 37f2a20ce3Sbostic #ifndef lint 38598f03dfSmarc static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91"; 39f2a20ce3Sbostic #endif /* not lint */ 40f2a20ce3Sbostic 41f2a20ce3Sbostic /* 42f2a20ce3Sbostic * When commands are first encountered, they are entered in a hash table. 43f2a20ce3Sbostic * This ensures that a full path search will not have to be done for them 44f2a20ce3Sbostic * on each invocation. 45f2a20ce3Sbostic * 46f2a20ce3Sbostic * We should investigate converting to a linear search, even though that 47f2a20ce3Sbostic * would make the command name "hash" a misnomer. 48f2a20ce3Sbostic */ 49f2a20ce3Sbostic 50f2a20ce3Sbostic #include "shell.h" 51f2a20ce3Sbostic #include "main.h" 52f2a20ce3Sbostic #include "nodes.h" 53f2a20ce3Sbostic #include "parser.h" 54f2a20ce3Sbostic #include "redir.h" 55f2a20ce3Sbostic #include "eval.h" 56f2a20ce3Sbostic #include "exec.h" 57f2a20ce3Sbostic #include "builtins.h" 58f2a20ce3Sbostic #include "var.h" 59f2a20ce3Sbostic #include "options.h" 60f2a20ce3Sbostic #include "input.h" 61f2a20ce3Sbostic #include "output.h" 62f2a20ce3Sbostic #include "syntax.h" 63f2a20ce3Sbostic #include "memalloc.h" 64f2a20ce3Sbostic #include "error.h" 65f2a20ce3Sbostic #include "init.h" 66f2a20ce3Sbostic #include "mystring.h" 67f2a20ce3Sbostic #include <sys/types.h> 68f2a20ce3Sbostic #include <sys/stat.h> 69f2a20ce3Sbostic #include <fcntl.h> 70f2a20ce3Sbostic #include <errno.h> 71f2a20ce3Sbostic 72f2a20ce3Sbostic 73f2a20ce3Sbostic #define CMDTABLESIZE 31 /* should be prime */ 74f2a20ce3Sbostic #define ARB 1 /* actual size determined at run time */ 75f2a20ce3Sbostic 76f2a20ce3Sbostic 77f2a20ce3Sbostic 78f2a20ce3Sbostic struct tblentry { 79f2a20ce3Sbostic struct tblentry *next; /* next entry in hash chain */ 80f2a20ce3Sbostic union param param; /* definition of builtin function */ 81f2a20ce3Sbostic short cmdtype; /* index identifying command */ 82f2a20ce3Sbostic char rehash; /* if set, cd done since entry created */ 83f2a20ce3Sbostic char cmdname[ARB]; /* name of command */ 84f2a20ce3Sbostic }; 85f2a20ce3Sbostic 86f2a20ce3Sbostic 87f2a20ce3Sbostic STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 880e183414Smarc STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 89f2a20ce3Sbostic 90f2a20ce3Sbostic 91f2a20ce3Sbostic #ifdef __STDC__ 92f2a20ce3Sbostic STATIC void tryexec(char *, char **, char **); 93f2a20ce3Sbostic STATIC void execinterp(char **, char **); 94f2a20ce3Sbostic STATIC void printentry(struct tblentry *); 95f2a20ce3Sbostic STATIC void clearcmdentry(int); 96f2a20ce3Sbostic STATIC struct tblentry *cmdlookup(char *, int); 97f2a20ce3Sbostic STATIC void delete_cmd_entry(void); 98f2a20ce3Sbostic #else 99f2a20ce3Sbostic STATIC void tryexec(); 100f2a20ce3Sbostic STATIC void execinterp(); 101f2a20ce3Sbostic STATIC void printentry(); 102f2a20ce3Sbostic STATIC void clearcmdentry(); 103f2a20ce3Sbostic STATIC struct tblentry *cmdlookup(); 104f2a20ce3Sbostic STATIC void delete_cmd_entry(); 105f2a20ce3Sbostic #endif 106f2a20ce3Sbostic 107f2a20ce3Sbostic 108f2a20ce3Sbostic 109f2a20ce3Sbostic /* 110f2a20ce3Sbostic * Exec a program. Never returns. If you change this routine, you may 111f2a20ce3Sbostic * have to change the find_command routine as well. 112f2a20ce3Sbostic */ 113f2a20ce3Sbostic 114f2a20ce3Sbostic void 115f2a20ce3Sbostic shellexec(argv, envp, path, index) 116f2a20ce3Sbostic char **argv, **envp; 117f2a20ce3Sbostic char *path; 118f2a20ce3Sbostic { 119f2a20ce3Sbostic char *cmdname; 120f2a20ce3Sbostic int e; 121f2a20ce3Sbostic 122f2a20ce3Sbostic if (strchr(argv[0], '/') != NULL) { 123f2a20ce3Sbostic tryexec(argv[0], argv, envp); 124f2a20ce3Sbostic e = errno; 125f2a20ce3Sbostic } else { 126f2a20ce3Sbostic e = ENOENT; 127f2a20ce3Sbostic while ((cmdname = padvance(&path, argv[0])) != NULL) { 128f2a20ce3Sbostic if (--index < 0 && pathopt == NULL) { 129f2a20ce3Sbostic tryexec(cmdname, argv, envp); 130f2a20ce3Sbostic if (errno != ENOENT && errno != ENOTDIR) 131f2a20ce3Sbostic e = errno; 132f2a20ce3Sbostic } 133f2a20ce3Sbostic stunalloc(cmdname); 134f2a20ce3Sbostic } 135f2a20ce3Sbostic } 136f2a20ce3Sbostic error2(argv[0], errmsg(e, E_EXEC)); 137f2a20ce3Sbostic } 138f2a20ce3Sbostic 139f2a20ce3Sbostic 140f2a20ce3Sbostic STATIC void 141f2a20ce3Sbostic tryexec(cmd, argv, envp) 142f2a20ce3Sbostic char *cmd; 143f2a20ce3Sbostic char **argv; 144f2a20ce3Sbostic char **envp; 145f2a20ce3Sbostic { 146f2a20ce3Sbostic int e; 147f2a20ce3Sbostic char *p; 148f2a20ce3Sbostic 149f2a20ce3Sbostic #ifdef SYSV 150f2a20ce3Sbostic do { 151f2a20ce3Sbostic execve(cmd, argv, envp); 152f2a20ce3Sbostic } while (errno == EINTR); 153f2a20ce3Sbostic #else 154f2a20ce3Sbostic execve(cmd, argv, envp); 155f2a20ce3Sbostic #endif 156f2a20ce3Sbostic e = errno; 157f2a20ce3Sbostic if (e == ENOEXEC) { 158f2a20ce3Sbostic initshellproc(); 159f2a20ce3Sbostic setinputfile(cmd, 0); 160f2a20ce3Sbostic commandname = arg0 = savestr(argv[0]); 161f2a20ce3Sbostic #ifndef BSD 162f2a20ce3Sbostic pgetc(); pungetc(); /* fill up input buffer */ 163f2a20ce3Sbostic p = parsenextc; 164f2a20ce3Sbostic if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 165f2a20ce3Sbostic argv[0] = cmd; 166f2a20ce3Sbostic execinterp(argv, envp); 167f2a20ce3Sbostic } 168f2a20ce3Sbostic #endif 169f2a20ce3Sbostic setparam(argv + 1); 170f2a20ce3Sbostic exraise(EXSHELLPROC); 171f2a20ce3Sbostic /*NOTREACHED*/ 172f2a20ce3Sbostic } 173f2a20ce3Sbostic errno = e; 174f2a20ce3Sbostic } 175f2a20ce3Sbostic 176f2a20ce3Sbostic 177f2a20ce3Sbostic #ifndef BSD 178f2a20ce3Sbostic /* 179f2a20ce3Sbostic * Execute an interpreter introduced by "#!", for systems where this 180f2a20ce3Sbostic * feature has not been built into the kernel. If the interpreter is 181f2a20ce3Sbostic * the shell, return (effectively ignoring the "#!"). If the execution 182f2a20ce3Sbostic * of the interpreter fails, exit. 183f2a20ce3Sbostic * 184f2a20ce3Sbostic * This code peeks inside the input buffer in order to avoid actually 185f2a20ce3Sbostic * reading any input. It would benefit from a rewrite. 186f2a20ce3Sbostic */ 187f2a20ce3Sbostic 188f2a20ce3Sbostic #define NEWARGS 5 189f2a20ce3Sbostic 190f2a20ce3Sbostic STATIC void 191f2a20ce3Sbostic execinterp(argv, envp) 192f2a20ce3Sbostic char **argv, **envp; 193f2a20ce3Sbostic { 194f2a20ce3Sbostic int n; 195f2a20ce3Sbostic char *inp; 196f2a20ce3Sbostic char *outp; 197f2a20ce3Sbostic char c; 198f2a20ce3Sbostic char *p; 199f2a20ce3Sbostic char **ap; 200f2a20ce3Sbostic char *newargs[NEWARGS]; 201f2a20ce3Sbostic int i; 202f2a20ce3Sbostic char **ap2; 203f2a20ce3Sbostic char **new; 204f2a20ce3Sbostic 205f2a20ce3Sbostic n = parsenleft - 2; 206f2a20ce3Sbostic inp = parsenextc + 2; 207f2a20ce3Sbostic ap = newargs; 208f2a20ce3Sbostic for (;;) { 209f2a20ce3Sbostic while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 210f2a20ce3Sbostic inp++; 211f2a20ce3Sbostic if (n < 0) 212f2a20ce3Sbostic goto bad; 213f2a20ce3Sbostic if ((c = *inp++) == '\n') 214f2a20ce3Sbostic break; 215f2a20ce3Sbostic if (ap == &newargs[NEWARGS]) 216f2a20ce3Sbostic bad: error("Bad #! line"); 217f2a20ce3Sbostic STARTSTACKSTR(outp); 218f2a20ce3Sbostic do { 219f2a20ce3Sbostic STPUTC(c, outp); 220f2a20ce3Sbostic } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 221f2a20ce3Sbostic STPUTC('\0', outp); 222f2a20ce3Sbostic n++, inp--; 223f2a20ce3Sbostic *ap++ = grabstackstr(outp); 224f2a20ce3Sbostic } 225f2a20ce3Sbostic if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 226f2a20ce3Sbostic p = newargs[0]; 227f2a20ce3Sbostic for (;;) { 228f2a20ce3Sbostic if (equal(p, "sh") || equal(p, "ash")) { 229f2a20ce3Sbostic return; 230f2a20ce3Sbostic } 231f2a20ce3Sbostic while (*p != '/') { 232f2a20ce3Sbostic if (*p == '\0') 233f2a20ce3Sbostic goto break2; 234f2a20ce3Sbostic p++; 235f2a20ce3Sbostic } 236f2a20ce3Sbostic p++; 237f2a20ce3Sbostic } 238f2a20ce3Sbostic break2:; 239f2a20ce3Sbostic } 240f2a20ce3Sbostic i = (char *)ap - (char *)newargs; /* size in bytes */ 241f2a20ce3Sbostic if (i == 0) 242f2a20ce3Sbostic error("Bad #! line"); 243f2a20ce3Sbostic for (ap2 = argv ; *ap2++ != NULL ; ); 244f2a20ce3Sbostic new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 245f2a20ce3Sbostic ap = newargs, ap2 = new; 246f2a20ce3Sbostic while ((i -= sizeof (char **)) >= 0) 247f2a20ce3Sbostic *ap2++ = *ap++; 248f2a20ce3Sbostic ap = argv; 249f2a20ce3Sbostic while (*ap2++ = *ap++); 250f2a20ce3Sbostic shellexec(new, envp, pathval(), 0); 251f2a20ce3Sbostic } 252f2a20ce3Sbostic #endif 253f2a20ce3Sbostic 254f2a20ce3Sbostic 255f2a20ce3Sbostic 256f2a20ce3Sbostic /* 257f2a20ce3Sbostic * Do a path search. The variable path (passed by reference) should be 258f2a20ce3Sbostic * set to the start of the path before the first call; padvance will update 259f2a20ce3Sbostic * this value as it proceeds. Successive calls to padvance will return 260f2a20ce3Sbostic * the possible path expansions in sequence. If an option (indicated by 261f2a20ce3Sbostic * a percent sign) appears in the path entry then the global variable 262f2a20ce3Sbostic * pathopt will be set to point to it; otherwise pathopt will be set to 263f2a20ce3Sbostic * NULL. 264f2a20ce3Sbostic */ 265f2a20ce3Sbostic 266f2a20ce3Sbostic char *pathopt; 267f2a20ce3Sbostic 268f2a20ce3Sbostic char * 269f2a20ce3Sbostic padvance(path, name) 270f2a20ce3Sbostic char **path; 271f2a20ce3Sbostic char *name; 272f2a20ce3Sbostic { 273f2a20ce3Sbostic register char *p, *q; 274f2a20ce3Sbostic char *start; 275f2a20ce3Sbostic int len; 276f2a20ce3Sbostic 277f2a20ce3Sbostic if (*path == NULL) 278f2a20ce3Sbostic return NULL; 279f2a20ce3Sbostic start = *path; 280f2a20ce3Sbostic for (p = start ; *p && *p != ':' && *p != '%' ; p++); 281f2a20ce3Sbostic len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 282f2a20ce3Sbostic while (stackblocksize() < len) 283f2a20ce3Sbostic growstackblock(); 284f2a20ce3Sbostic q = stackblock(); 285f2a20ce3Sbostic if (p != start) { 286f2a20ce3Sbostic bcopy(start, q, p - start); 287f2a20ce3Sbostic q += p - start; 288f2a20ce3Sbostic *q++ = '/'; 289f2a20ce3Sbostic } 290f2a20ce3Sbostic strcpy(q, name); 291f2a20ce3Sbostic pathopt = NULL; 292f2a20ce3Sbostic if (*p == '%') { 293f2a20ce3Sbostic pathopt = ++p; 294f2a20ce3Sbostic while (*p && *p != ':') p++; 295f2a20ce3Sbostic } 296f2a20ce3Sbostic if (*p == ':') 297f2a20ce3Sbostic *path = p + 1; 298f2a20ce3Sbostic else 299f2a20ce3Sbostic *path = NULL; 300f2a20ce3Sbostic return stalloc(len); 301f2a20ce3Sbostic } 302f2a20ce3Sbostic 303f2a20ce3Sbostic 304f2a20ce3Sbostic 305f2a20ce3Sbostic /*** Command hashing code ***/ 306f2a20ce3Sbostic 307f2a20ce3Sbostic 308f2a20ce3Sbostic hashcmd(argc, argv) char **argv; { 309f2a20ce3Sbostic struct tblentry **pp; 310f2a20ce3Sbostic struct tblentry *cmdp; 311f2a20ce3Sbostic int c; 312f2a20ce3Sbostic int verbose; 313f2a20ce3Sbostic struct cmdentry entry; 314f2a20ce3Sbostic char *name; 315f2a20ce3Sbostic 316f2a20ce3Sbostic if (argc <= 1) { 317f2a20ce3Sbostic for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 318f2a20ce3Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 319f2a20ce3Sbostic printentry(cmdp); 320f2a20ce3Sbostic } 321f2a20ce3Sbostic } 322f2a20ce3Sbostic return 0; 323f2a20ce3Sbostic } 324f2a20ce3Sbostic verbose = 0; 3250e183414Smarc while ((c = nextopt("rv")) != '\0') { 326f2a20ce3Sbostic if (c == 'r') { 327f2a20ce3Sbostic clearcmdentry(0); 328f2a20ce3Sbostic } else if (c == 'v') { 329f2a20ce3Sbostic verbose++; 330f2a20ce3Sbostic } 331f2a20ce3Sbostic } 332f2a20ce3Sbostic while ((name = *argptr) != NULL) { 333f2a20ce3Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL 334f2a20ce3Sbostic && (cmdp->cmdtype == CMDNORMAL 335f2a20ce3Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 336f2a20ce3Sbostic delete_cmd_entry(); 337f2a20ce3Sbostic find_command(name, &entry, 1); 338f2a20ce3Sbostic if (verbose) { 339f2a20ce3Sbostic if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 340f2a20ce3Sbostic cmdp = cmdlookup(name, 0); 341f2a20ce3Sbostic printentry(cmdp); 342f2a20ce3Sbostic } 343f2a20ce3Sbostic flushall(); 344f2a20ce3Sbostic } 345f2a20ce3Sbostic argptr++; 346f2a20ce3Sbostic } 347f2a20ce3Sbostic return 0; 348f2a20ce3Sbostic } 349f2a20ce3Sbostic 350f2a20ce3Sbostic 351f2a20ce3Sbostic STATIC void 352f2a20ce3Sbostic printentry(cmdp) 353f2a20ce3Sbostic struct tblentry *cmdp; 354f2a20ce3Sbostic { 355f2a20ce3Sbostic int index; 356f2a20ce3Sbostic char *path; 357f2a20ce3Sbostic char *name; 358f2a20ce3Sbostic 359f2a20ce3Sbostic if (cmdp->cmdtype == CMDNORMAL) { 360f2a20ce3Sbostic index = cmdp->param.index; 361f2a20ce3Sbostic path = pathval(); 362f2a20ce3Sbostic do { 363f2a20ce3Sbostic name = padvance(&path, cmdp->cmdname); 364f2a20ce3Sbostic stunalloc(name); 365f2a20ce3Sbostic } while (--index >= 0); 366f2a20ce3Sbostic out1str(name); 367f2a20ce3Sbostic } else if (cmdp->cmdtype == CMDBUILTIN) { 368f2a20ce3Sbostic out1fmt("builtin %s", cmdp->cmdname); 369f2a20ce3Sbostic } else if (cmdp->cmdtype == CMDFUNCTION) { 370f2a20ce3Sbostic out1fmt("function %s", cmdp->cmdname); 371f2a20ce3Sbostic #ifdef DEBUG 372f2a20ce3Sbostic } else { 373f2a20ce3Sbostic error("internal error: cmdtype %d", cmdp->cmdtype); 374f2a20ce3Sbostic #endif 375f2a20ce3Sbostic } 376f2a20ce3Sbostic if (cmdp->rehash) 377f2a20ce3Sbostic out1c('*'); 378f2a20ce3Sbostic out1c('\n'); 379f2a20ce3Sbostic } 380f2a20ce3Sbostic 381f2a20ce3Sbostic 382f2a20ce3Sbostic 383f2a20ce3Sbostic /* 384f2a20ce3Sbostic * Resolve a command name. If you change this routine, you may have to 385f2a20ce3Sbostic * change the shellexec routine as well. 386f2a20ce3Sbostic */ 387f2a20ce3Sbostic 388f2a20ce3Sbostic void 389f2a20ce3Sbostic find_command(name, entry, printerr) 390f2a20ce3Sbostic char *name; 391f2a20ce3Sbostic struct cmdentry *entry; 392f2a20ce3Sbostic { 393f2a20ce3Sbostic struct tblentry *cmdp; 394f2a20ce3Sbostic int index; 395f2a20ce3Sbostic int prev; 396f2a20ce3Sbostic char *path; 397f2a20ce3Sbostic char *fullname; 398f2a20ce3Sbostic struct stat statb; 399f2a20ce3Sbostic int e; 400f2a20ce3Sbostic int i; 401f2a20ce3Sbostic 402f2a20ce3Sbostic /* If name contains a slash, don't use the hash table */ 403f2a20ce3Sbostic if (strchr(name, '/') != NULL) { 404f2a20ce3Sbostic entry->cmdtype = CMDNORMAL; 405f2a20ce3Sbostic entry->u.index = 0; 406f2a20ce3Sbostic return; 407f2a20ce3Sbostic } 408f2a20ce3Sbostic 409f2a20ce3Sbostic /* If name is in the table, and not invalidated by cd, we're done */ 410f2a20ce3Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 411f2a20ce3Sbostic goto success; 412f2a20ce3Sbostic 413f2a20ce3Sbostic /* If %builtin not in path, check for builtin next */ 414f2a20ce3Sbostic if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 415f2a20ce3Sbostic INTOFF; 416f2a20ce3Sbostic cmdp = cmdlookup(name, 1); 417f2a20ce3Sbostic cmdp->cmdtype = CMDBUILTIN; 418f2a20ce3Sbostic cmdp->param.index = i; 419f2a20ce3Sbostic INTON; 420f2a20ce3Sbostic goto success; 421f2a20ce3Sbostic } 422f2a20ce3Sbostic 423f2a20ce3Sbostic /* We have to search path. */ 424f2a20ce3Sbostic prev = -1; /* where to start */ 425f2a20ce3Sbostic if (cmdp) { /* doing a rehash */ 426f2a20ce3Sbostic if (cmdp->cmdtype == CMDBUILTIN) 427f2a20ce3Sbostic prev = builtinloc; 428f2a20ce3Sbostic else 429f2a20ce3Sbostic prev = cmdp->param.index; 430f2a20ce3Sbostic } 431f2a20ce3Sbostic 432f2a20ce3Sbostic path = pathval(); 433f2a20ce3Sbostic e = ENOENT; 434f2a20ce3Sbostic index = -1; 435f2a20ce3Sbostic loop: 436f2a20ce3Sbostic while ((fullname = padvance(&path, name)) != NULL) { 437f2a20ce3Sbostic stunalloc(fullname); 438f2a20ce3Sbostic index++; 439f2a20ce3Sbostic if (pathopt) { 440f2a20ce3Sbostic if (prefix("builtin", pathopt)) { 441f2a20ce3Sbostic if ((i = find_builtin(name)) < 0) 442f2a20ce3Sbostic goto loop; 443f2a20ce3Sbostic INTOFF; 444f2a20ce3Sbostic cmdp = cmdlookup(name, 1); 445f2a20ce3Sbostic cmdp->cmdtype = CMDBUILTIN; 446f2a20ce3Sbostic cmdp->param.index = i; 447f2a20ce3Sbostic INTON; 448f2a20ce3Sbostic goto success; 449f2a20ce3Sbostic } else if (prefix("func", pathopt)) { 450f2a20ce3Sbostic /* handled below */ 451f2a20ce3Sbostic } else { 452f2a20ce3Sbostic goto loop; /* ignore unimplemented options */ 453f2a20ce3Sbostic } 454f2a20ce3Sbostic } 455f2a20ce3Sbostic /* if rehash, don't redo absolute path names */ 456f2a20ce3Sbostic if (fullname[0] == '/' && index <= prev) { 457f2a20ce3Sbostic if (index < prev) 458f2a20ce3Sbostic goto loop; 459f2a20ce3Sbostic TRACE(("searchexec \"%s\": no change\n", name)); 460f2a20ce3Sbostic goto success; 461f2a20ce3Sbostic } 462f2a20ce3Sbostic while (stat(fullname, &statb) < 0) { 463f2a20ce3Sbostic #ifdef SYSV 464f2a20ce3Sbostic if (errno == EINTR) 465f2a20ce3Sbostic continue; 466f2a20ce3Sbostic #endif 467f2a20ce3Sbostic if (errno != ENOENT && errno != ENOTDIR) 468f2a20ce3Sbostic e = errno; 469f2a20ce3Sbostic goto loop; 470f2a20ce3Sbostic } 471f2a20ce3Sbostic e = EACCES; /* if we fail, this will be the error */ 472f2a20ce3Sbostic if ((statb.st_mode & S_IFMT) != S_IFREG) 473f2a20ce3Sbostic goto loop; 474f2a20ce3Sbostic if (pathopt) { /* this is a %func directory */ 475f2a20ce3Sbostic stalloc(strlen(fullname) + 1); 476f2a20ce3Sbostic readcmdfile(fullname); 477f2a20ce3Sbostic if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 478f2a20ce3Sbostic error("%s not defined in %s", name, fullname); 479f2a20ce3Sbostic stunalloc(fullname); 480f2a20ce3Sbostic goto success; 481f2a20ce3Sbostic } 482f2a20ce3Sbostic if (statb.st_uid == geteuid()) { 483f2a20ce3Sbostic if ((statb.st_mode & 0100) == 0) 484f2a20ce3Sbostic goto loop; 485f2a20ce3Sbostic } else if (statb.st_gid == getegid()) { 486f2a20ce3Sbostic if ((statb.st_mode & 010) == 0) 487f2a20ce3Sbostic goto loop; 488f2a20ce3Sbostic } else { 489f2a20ce3Sbostic if ((statb.st_mode & 01) == 0) 490f2a20ce3Sbostic goto loop; 491f2a20ce3Sbostic } 492f2a20ce3Sbostic TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 493f2a20ce3Sbostic INTOFF; 494f2a20ce3Sbostic cmdp = cmdlookup(name, 1); 495f2a20ce3Sbostic cmdp->cmdtype = CMDNORMAL; 496f2a20ce3Sbostic cmdp->param.index = index; 497f2a20ce3Sbostic INTON; 498f2a20ce3Sbostic goto success; 499f2a20ce3Sbostic } 500f2a20ce3Sbostic 501f2a20ce3Sbostic /* We failed. If there was an entry for this command, delete it */ 502f2a20ce3Sbostic if (cmdp) 503f2a20ce3Sbostic delete_cmd_entry(); 504f2a20ce3Sbostic if (printerr) 505f2a20ce3Sbostic outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 506f2a20ce3Sbostic entry->cmdtype = CMDUNKNOWN; 507f2a20ce3Sbostic return; 508f2a20ce3Sbostic 509f2a20ce3Sbostic success: 510f2a20ce3Sbostic cmdp->rehash = 0; 511f2a20ce3Sbostic entry->cmdtype = cmdp->cmdtype; 512f2a20ce3Sbostic entry->u = cmdp->param; 513f2a20ce3Sbostic } 514f2a20ce3Sbostic 515f2a20ce3Sbostic 516f2a20ce3Sbostic 517f2a20ce3Sbostic /* 518f2a20ce3Sbostic * Search the table of builtin commands. 519f2a20ce3Sbostic */ 520f2a20ce3Sbostic 521f2a20ce3Sbostic int 522f2a20ce3Sbostic find_builtin(name) 523f2a20ce3Sbostic char *name; 524f2a20ce3Sbostic { 525f2a20ce3Sbostic const register struct builtincmd *bp; 526f2a20ce3Sbostic 527f2a20ce3Sbostic for (bp = builtincmd ; bp->name ; bp++) { 528f2a20ce3Sbostic if (*bp->name == *name && equal(bp->name, name)) 529f2a20ce3Sbostic return bp->code; 530f2a20ce3Sbostic } 531f2a20ce3Sbostic return -1; 532f2a20ce3Sbostic } 533f2a20ce3Sbostic 534f2a20ce3Sbostic 535f2a20ce3Sbostic 536f2a20ce3Sbostic /* 537f2a20ce3Sbostic * Called when a cd is done. Marks all commands so the next time they 538f2a20ce3Sbostic * are executed they will be rehashed. 539f2a20ce3Sbostic */ 540f2a20ce3Sbostic 541f2a20ce3Sbostic void 542f2a20ce3Sbostic hashcd() { 543f2a20ce3Sbostic struct tblentry **pp; 544f2a20ce3Sbostic struct tblentry *cmdp; 545f2a20ce3Sbostic 546f2a20ce3Sbostic for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 547f2a20ce3Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 548f2a20ce3Sbostic if (cmdp->cmdtype == CMDNORMAL 549f2a20ce3Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) 550f2a20ce3Sbostic cmdp->rehash = 1; 551f2a20ce3Sbostic } 552f2a20ce3Sbostic } 553f2a20ce3Sbostic } 554f2a20ce3Sbostic 555f2a20ce3Sbostic 556f2a20ce3Sbostic 557f2a20ce3Sbostic /* 558f2a20ce3Sbostic * Called before PATH is changed. The argument is the new value of PATH; 559f2a20ce3Sbostic * pathval() still returns the old value at this point. Called with 560f2a20ce3Sbostic * interrupts off. 561f2a20ce3Sbostic */ 562f2a20ce3Sbostic 563f2a20ce3Sbostic void 564f2a20ce3Sbostic changepath(newval) 565f2a20ce3Sbostic char *newval; 566f2a20ce3Sbostic { 567f2a20ce3Sbostic char *old, *new; 568f2a20ce3Sbostic int index; 569f2a20ce3Sbostic int firstchange; 570f2a20ce3Sbostic int bltin; 571598f03dfSmarc int hasdot; 572f2a20ce3Sbostic 573f2a20ce3Sbostic old = pathval(); 574f2a20ce3Sbostic new = newval; 575f2a20ce3Sbostic firstchange = 9999; /* assume no change */ 576598f03dfSmarc index = hasdot = 0; 577f2a20ce3Sbostic bltin = -1; 578598f03dfSmarc if (*new == ':') 579598f03dfSmarc hasdot++; 580f2a20ce3Sbostic for (;;) { 581f2a20ce3Sbostic if (*old != *new) { 582f2a20ce3Sbostic firstchange = index; 583f2a20ce3Sbostic if (*old == '\0' && *new == ':' 584f2a20ce3Sbostic || *old == ':' && *new == '\0') 585f2a20ce3Sbostic firstchange++; 586f2a20ce3Sbostic old = new; /* ignore subsequent differences */ 587f2a20ce3Sbostic } 588f2a20ce3Sbostic if (*new == '\0') 589f2a20ce3Sbostic break; 590f2a20ce3Sbostic if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 591f2a20ce3Sbostic bltin = index; 592f2a20ce3Sbostic if (*new == ':') { 593598f03dfSmarc char c = *(new+1); 594598f03dfSmarc 595f2a20ce3Sbostic index++; 596598f03dfSmarc if (c == ':' || c == '\0' || (c == '.' && 597598f03dfSmarc ((c = *(new+2)) == ':' || c == '\0'))) 598598f03dfSmarc hasdot++; 599f2a20ce3Sbostic } 600f2a20ce3Sbostic new++, old++; 601f2a20ce3Sbostic } 602598f03dfSmarc if (hasdot && geteuid() == 0) 603598f03dfSmarc out2str("sh: warning: running as root with dot in PATH\n"); 604f2a20ce3Sbostic if (builtinloc < 0 && bltin >= 0) 605f2a20ce3Sbostic builtinloc = bltin; /* zap builtins */ 606f2a20ce3Sbostic if (builtinloc >= 0 && bltin < 0) 607f2a20ce3Sbostic firstchange = 0; 608f2a20ce3Sbostic clearcmdentry(firstchange); 609f2a20ce3Sbostic builtinloc = bltin; 610f2a20ce3Sbostic } 611f2a20ce3Sbostic 612f2a20ce3Sbostic 613f2a20ce3Sbostic /* 614f2a20ce3Sbostic * Clear out command entries. The argument specifies the first entry in 615f2a20ce3Sbostic * PATH which has changed. 616f2a20ce3Sbostic */ 617f2a20ce3Sbostic 618f2a20ce3Sbostic STATIC void 619f2a20ce3Sbostic clearcmdentry(firstchange) { 620f2a20ce3Sbostic struct tblentry **tblp; 621f2a20ce3Sbostic struct tblentry **pp; 622f2a20ce3Sbostic struct tblentry *cmdp; 623f2a20ce3Sbostic 624f2a20ce3Sbostic INTOFF; 625f2a20ce3Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 626f2a20ce3Sbostic pp = tblp; 627f2a20ce3Sbostic while ((cmdp = *pp) != NULL) { 628f2a20ce3Sbostic if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange 629f2a20ce3Sbostic || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { 630f2a20ce3Sbostic *pp = cmdp->next; 631f2a20ce3Sbostic ckfree(cmdp); 632f2a20ce3Sbostic } else { 633f2a20ce3Sbostic pp = &cmdp->next; 634f2a20ce3Sbostic } 635f2a20ce3Sbostic } 636f2a20ce3Sbostic } 637f2a20ce3Sbostic INTON; 638f2a20ce3Sbostic } 639f2a20ce3Sbostic 640f2a20ce3Sbostic 641f2a20ce3Sbostic /* 642f2a20ce3Sbostic * Delete all functions. 643f2a20ce3Sbostic */ 644f2a20ce3Sbostic 645f2a20ce3Sbostic #ifdef mkinit 646f2a20ce3Sbostic MKINIT void deletefuncs(); 647f2a20ce3Sbostic 648f2a20ce3Sbostic SHELLPROC { 649f2a20ce3Sbostic deletefuncs(); 650f2a20ce3Sbostic } 651f2a20ce3Sbostic #endif 652f2a20ce3Sbostic 653f2a20ce3Sbostic void 654f2a20ce3Sbostic deletefuncs() { 655f2a20ce3Sbostic struct tblentry **tblp; 656f2a20ce3Sbostic struct tblentry **pp; 657f2a20ce3Sbostic struct tblentry *cmdp; 658f2a20ce3Sbostic 659f2a20ce3Sbostic INTOFF; 660f2a20ce3Sbostic for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 661f2a20ce3Sbostic pp = tblp; 662f2a20ce3Sbostic while ((cmdp = *pp) != NULL) { 663f2a20ce3Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 664f2a20ce3Sbostic *pp = cmdp->next; 665f2a20ce3Sbostic freefunc(cmdp->param.func); 666f2a20ce3Sbostic ckfree(cmdp); 667f2a20ce3Sbostic } else { 668f2a20ce3Sbostic pp = &cmdp->next; 669f2a20ce3Sbostic } 670f2a20ce3Sbostic } 671f2a20ce3Sbostic } 672f2a20ce3Sbostic INTON; 673f2a20ce3Sbostic } 674f2a20ce3Sbostic 675f2a20ce3Sbostic 676f2a20ce3Sbostic 677f2a20ce3Sbostic /* 678f2a20ce3Sbostic * Locate a command in the command hash table. If "add" is nonzero, 679f2a20ce3Sbostic * add the command to the table if it is not already present. The 680f2a20ce3Sbostic * variable "lastcmdentry" is set to point to the address of the link 681f2a20ce3Sbostic * pointing to the entry, so that delete_cmd_entry can delete the 682f2a20ce3Sbostic * entry. 683f2a20ce3Sbostic */ 684f2a20ce3Sbostic 685f2a20ce3Sbostic struct tblentry **lastcmdentry; 686f2a20ce3Sbostic 687f2a20ce3Sbostic 688f2a20ce3Sbostic STATIC struct tblentry * 689f2a20ce3Sbostic cmdlookup(name, add) 690f2a20ce3Sbostic char *name; 691f2a20ce3Sbostic { 692f2a20ce3Sbostic int hashval; 693f2a20ce3Sbostic register char *p; 694f2a20ce3Sbostic struct tblentry *cmdp; 695f2a20ce3Sbostic struct tblentry **pp; 696f2a20ce3Sbostic 697f2a20ce3Sbostic p = name; 698f2a20ce3Sbostic hashval = *p << 4; 699f2a20ce3Sbostic while (*p) 700f2a20ce3Sbostic hashval += *p++; 701f2a20ce3Sbostic hashval &= 0x7FFF; 702f2a20ce3Sbostic pp = &cmdtable[hashval % CMDTABLESIZE]; 703f2a20ce3Sbostic for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 704f2a20ce3Sbostic if (equal(cmdp->cmdname, name)) 705f2a20ce3Sbostic break; 706f2a20ce3Sbostic pp = &cmdp->next; 707f2a20ce3Sbostic } 708f2a20ce3Sbostic if (add && cmdp == NULL) { 709f2a20ce3Sbostic INTOFF; 710f2a20ce3Sbostic cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 711f2a20ce3Sbostic + strlen(name) + 1); 712f2a20ce3Sbostic cmdp->next = NULL; 713f2a20ce3Sbostic cmdp->cmdtype = CMDUNKNOWN; 714f2a20ce3Sbostic cmdp->rehash = 0; 715f2a20ce3Sbostic strcpy(cmdp->cmdname, name); 716f2a20ce3Sbostic INTON; 717f2a20ce3Sbostic } 718f2a20ce3Sbostic lastcmdentry = pp; 719f2a20ce3Sbostic return cmdp; 720f2a20ce3Sbostic } 721f2a20ce3Sbostic 722f2a20ce3Sbostic 723f2a20ce3Sbostic /* 724f2a20ce3Sbostic * Delete the command entry returned on the last lookup. 725f2a20ce3Sbostic */ 726f2a20ce3Sbostic 727f2a20ce3Sbostic STATIC void 728f2a20ce3Sbostic delete_cmd_entry() { 729f2a20ce3Sbostic struct tblentry *cmdp; 730f2a20ce3Sbostic 731f2a20ce3Sbostic INTOFF; 732f2a20ce3Sbostic cmdp = *lastcmdentry; 733f2a20ce3Sbostic *lastcmdentry = cmdp->next; 734f2a20ce3Sbostic ckfree(cmdp); 735f2a20ce3Sbostic INTON; 736f2a20ce3Sbostic } 737f2a20ce3Sbostic 738f2a20ce3Sbostic 739f2a20ce3Sbostic 740f2a20ce3Sbostic #ifdef notdef 741f2a20ce3Sbostic void 742f2a20ce3Sbostic getcmdentry(name, entry) 743f2a20ce3Sbostic char *name; 744f2a20ce3Sbostic struct cmdentry *entry; 745f2a20ce3Sbostic { 746f2a20ce3Sbostic struct tblentry *cmdp = cmdlookup(name, 0); 747f2a20ce3Sbostic 748f2a20ce3Sbostic if (cmdp) { 749f2a20ce3Sbostic entry->u = cmdp->param; 750f2a20ce3Sbostic entry->cmdtype = cmdp->cmdtype; 751f2a20ce3Sbostic } else { 752f2a20ce3Sbostic entry->cmdtype = CMDUNKNOWN; 753f2a20ce3Sbostic entry->u.index = 0; 754f2a20ce3Sbostic } 755f2a20ce3Sbostic } 756f2a20ce3Sbostic #endif 757f2a20ce3Sbostic 758f2a20ce3Sbostic 759f2a20ce3Sbostic /* 760f2a20ce3Sbostic * Add a new command entry, replacing any existing command entry for 761f2a20ce3Sbostic * the same name. 762f2a20ce3Sbostic */ 763f2a20ce3Sbostic 764f2a20ce3Sbostic void 765f2a20ce3Sbostic addcmdentry(name, entry) 766f2a20ce3Sbostic char *name; 767f2a20ce3Sbostic struct cmdentry *entry; 768f2a20ce3Sbostic { 769f2a20ce3Sbostic struct tblentry *cmdp; 770f2a20ce3Sbostic 771f2a20ce3Sbostic INTOFF; 772f2a20ce3Sbostic cmdp = cmdlookup(name, 1); 773f2a20ce3Sbostic if (cmdp->cmdtype == CMDFUNCTION) { 774f2a20ce3Sbostic freefunc(cmdp->param.func); 775f2a20ce3Sbostic } 776f2a20ce3Sbostic cmdp->cmdtype = entry->cmdtype; 777f2a20ce3Sbostic cmdp->param = entry->u; 778f2a20ce3Sbostic INTON; 779f2a20ce3Sbostic } 780f2a20ce3Sbostic 781f2a20ce3Sbostic 782f2a20ce3Sbostic /* 783f2a20ce3Sbostic * Define a shell function. 784f2a20ce3Sbostic */ 785f2a20ce3Sbostic 786f2a20ce3Sbostic void 787f2a20ce3Sbostic defun(name, func) 788f2a20ce3Sbostic char *name; 789f2a20ce3Sbostic union node *func; 790f2a20ce3Sbostic { 791f2a20ce3Sbostic struct cmdentry entry; 792f2a20ce3Sbostic 793f2a20ce3Sbostic INTOFF; 794f2a20ce3Sbostic entry.cmdtype = CMDFUNCTION; 795f2a20ce3Sbostic entry.u.func = copyfunc(func); 796f2a20ce3Sbostic addcmdentry(name, &entry); 797f2a20ce3Sbostic INTON; 798f2a20ce3Sbostic } 799f2a20ce3Sbostic 800f2a20ce3Sbostic 801f2a20ce3Sbostic /* 802f2a20ce3Sbostic * Delete a function if it exists. 803f2a20ce3Sbostic */ 804f2a20ce3Sbostic 805*22767f1aSmarc int 806f2a20ce3Sbostic unsetfunc(name) 807f2a20ce3Sbostic char *name; 808f2a20ce3Sbostic { 809f2a20ce3Sbostic struct tblentry *cmdp; 810f2a20ce3Sbostic 811f2a20ce3Sbostic if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 812f2a20ce3Sbostic freefunc(cmdp->param.func); 813f2a20ce3Sbostic delete_cmd_entry(); 814*22767f1aSmarc return (0); 815f2a20ce3Sbostic } 816*22767f1aSmarc return (1); 817f2a20ce3Sbostic } 818