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