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