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