exec.c revision 94775
11590Srgrimes/*- 2103518Sjmallett * Copyright (c) 1991, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * Kenneth Almquist. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 3. All advertising materials mentioning features or use of this software 171590Srgrimes * must display the following acknowledgement: 181590Srgrimes * This product includes software developed by the University of 191590Srgrimes * California, Berkeley and its contributors. 201590Srgrimes * 4. Neither the name of the University nor the names of its contributors 211590Srgrimes * may be used to endorse or promote products derived from this software 221590Srgrimes * without specific prior written permission. 231590Srgrimes * 241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341590Srgrimes * SUCH DAMAGE. 3587696Smarkm */ 3687696Smarkm 3787696Smarkm#ifndef lint 3887696Smarkm#if 0 391590Srgrimesstatic char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 4087696Smarkm#endif 411590Srgrimesstatic const char rcsid[] = 421590Srgrimes "$FreeBSD: head/bin/sh/exec.c 94775 2002-04-15 15:49:30Z greid $"; 4387696Smarkm#endif /* not lint */ 441590Srgrimes 451590Srgrimes#include <sys/types.h> 4687696Smarkm#include <sys/stat.h> 4787696Smarkm#include <unistd.h> 481590Srgrimes#include <fcntl.h> 491590Srgrimes#include <errno.h> 501590Srgrimes#include <stdlib.h> 511590Srgrimes 521590Srgrimes/* 531590Srgrimes * When commands are first encountered, they are entered in a hash table. 541590Srgrimes * This ensures that a full path search will not have to be done for them 5523690Speter * on each invocation. 561590Srgrimes * 57258394Speter * We should investigate converting to a linear search, even though that 58258394Speter * would make the command name "hash" a misnomer. 591590Srgrimes */ 601590Srgrimes 6189346Snyan#include "shell.h" 6289346Snyan#include "main.h" 6389346Snyan#include "nodes.h" 6489346Snyan#include "parser.h" 65116085Sjmallett#include "redir.h" 66258394Speter#include "eval.h" 67258394Speter#include "exec.h" 68103518Sjmallett#include "builtins.h" 69103518Sjmallett#include "var.h" 70227191Sed#include "options.h" 71258394Speter#include "input.h" 72103518Sjmallett#include "output.h" 73227191Sed#include "syntax.h" 74227191Sed#include "memalloc.h" 75227191Sed#include "error.h" 76227191Sed#include "init.h" 77227191Sed#include "mystring.h" 78227191Sed#include "show.h" 79227191Sed#include "jobs.h" 80258394Speter#include "alias.h" 81258394Speter 82227191Sed 83227191Sed#define CMDTABLESIZE 31 /* should be prime */ 84227191Sed#define ARB 1 /* actual size determined at run time */ 85103518Sjmallett 86258394Speter 87227191Sed 88103518Sjmallettstruct tblentry { 89103518Sjmallett struct tblentry *next; /* next entry in hash chain */ 90103518Sjmallett union param param; /* definition of builtin function */ 91103518Sjmallett short cmdtype; /* index identifying command */ 921590Srgrimes char rehash; /* if set, cd done since entry created */ 93103518Sjmallett char cmdname[ARB]; /* name of command */ 941590Srgrimes}; 95103518Sjmallett 96103846Sjmallett 97103518SjmallettSTATIC struct tblentry *cmdtable[CMDTABLESIZE]; 98258394SpeterSTATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 991590Srgrimesint exerrno = 0; /* Last exec error */ 1001590Srgrimes 1011590Srgrimes 1021590SrgrimesSTATIC void tryexec(char *, char **, char **); 103116085Sjmallett#ifndef BSD 104116085SjmallettSTATIC void execinterp(char **, char **); 105116085Sjmallett#endif 106258394SpeterSTATIC void printentry(struct tblentry *, int); 107258394SpeterSTATIC struct tblentry *cmdlookup(char *, int); 108258394SpeterSTATIC void delete_cmd_entry(void); 1091590Srgrimes 1101590Srgrimes 1111590Srgrimes 1121590Srgrimes/* 1131590Srgrimes * Exec a program. Never returns. If you change this routine, you may 1141590Srgrimes * have to change the find_command routine as well. 11589346Snyan */ 11689346Snyan 11789346Snyanvoid 1181590Srgrimesshellexec(char **argv, char **envp, char *path, int index) 1191590Srgrimes{ 1201590Srgrimes char *cmdname; 1211590Srgrimes int e; 122203042Semaste 1231590Srgrimes if (strchr(argv[0], '/') != NULL) { 1241590Srgrimes tryexec(argv[0], argv, envp); 125258394Speter e = errno; 126258394Speter } else { 127258394Speter e = ENOENT; 1281590Srgrimes while ((cmdname = padvance(&path, argv[0])) != NULL) { 1291590Srgrimes if (--index < 0 && pathopt == NULL) { 1301590Srgrimes tryexec(cmdname, argv, envp); 1311590Srgrimes if (errno != ENOENT && errno != ENOTDIR) 1321590Srgrimes e = errno; 1331590Srgrimes } 1341590Srgrimes stunalloc(cmdname); 1351590Srgrimes } 1361590Srgrimes } 1371590Srgrimes 1381590Srgrimes /* Map to POSIX errors */ 1391590Srgrimes switch (e) { 1401590Srgrimes case EACCES: 1411590Srgrimes exerrno = 126; 1421590Srgrimes break; 1431590Srgrimes case ENOENT: 1441590Srgrimes exerrno = 127; 145103518Sjmallett break; 146103518Sjmallett default: 147103518Sjmallett exerrno = 2; 1481590Srgrimes break; 149103837Sjmallett } 150103837Sjmallett exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); 151103518Sjmallett} 152103837Sjmallett 153103837Sjmallett 154103837SjmallettSTATIC void 155103837Sjmalletttryexec(char *cmd, char **argv, char **envp) 156103518Sjmallett{ 157103518Sjmallett int e; 158227191Sed#ifndef BSD 159103518Sjmallett char *p; 160103518Sjmallett#endif 161103518Sjmallett 162103518Sjmallett#ifdef SYSV 163103518Sjmallett do { 164103518Sjmallett execve(cmd, argv, envp); 165103518Sjmallett } while (errno == EINTR); 166103518Sjmallett#else 167116085Sjmallett execve(cmd, argv, envp); 168258394Speter#endif 169258394Speter e = errno; 170103518Sjmallett if (e == ENOEXEC) { 171103518Sjmallett initshellproc(); 172103518Sjmallett setinputfile(cmd, 0); 173103518Sjmallett commandname = arg0 = savestr(argv[0]); 174103837Sjmallett#ifndef BSD 175103837Sjmallett pgetc(); pungetc(); /* fill up input buffer */ 176103837Sjmallett p = parsenextc; 177103837Sjmallett if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 178103518Sjmallett argv[0] = cmd; 179103518Sjmallett execinterp(argv, envp); 180103837Sjmallett } 1811590Srgrimes#endif 182103518Sjmallett setparam(argv + 1); 183227191Sed exraise(EXSHELLPROC); 184103518Sjmallett /*NOTREACHED*/ 185103518Sjmallett } 186103518Sjmallett errno = e; 187103518Sjmallett} 188103518Sjmallett 189103518Sjmallett 190103518Sjmallett#ifndef BSD 191103518Sjmallett/* 192116085Sjmallett * Execute an interpreter introduced by "#!", for systems where this 193258394Speter * feature has not been built into the kernel. If the interpreter is 194258394Speter * the shell, return (effectively ignoring the "#!"). If the execution 195103518Sjmallett * of the interpreter fails, exit. 1961590Srgrimes * 1971590Srgrimes * This code peeks inside the input buffer in order to avoid actually 198103846Sjmallett * reading any input. It would benefit from a rewrite. 199227191Sed */ 200103846Sjmallett 201103846Sjmallett#define NEWARGS 5 202103846Sjmallett 203103846SjmallettSTATIC void 204103846Sjmallettexecinterp(char **argv, char **envp) 205103846Sjmallett{ 206103846Sjmallett int n; 207103846Sjmallett char *inp; 208103846Sjmallett char *outp; 209103846Sjmallett char c; 210103518Sjmallett char *p; 211103518Sjmallett char **ap; 212116088Sjmallett char *newargs[NEWARGS]; 213227191Sed int i; 214116088Sjmallett char **ap2; 215116088Sjmallett char **new; 216116088Sjmallett 217116088Sjmallett n = parsenleft - 2; 218116088Sjmallett inp = parsenextc + 2; 219116088Sjmallett ap = newargs; 220117137Sjmallett for (;;) { 221116088Sjmallett while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 222116088Sjmallett inp++; 223116088Sjmallett if (n < 0) 224116088Sjmallett goto bad; 225103846Sjmallett if ((c = *inp++) == '\n') 226103846Sjmallett break; 227103846Sjmallett if (ap == &newargs[NEWARGS]) 228103846Sjmallettbad: error("Bad #! line"); 229103518Sjmallett STARTSTACKSTR(outp); 230103846Sjmallett do { 231103846Sjmallett STPUTC(c, outp); 232103518Sjmallett } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 233103846Sjmallett STPUTC('\0', outp); 234103846Sjmallett n++, inp--; 235103518Sjmallett *ap++ = grabstackstr(outp); 236103846Sjmallett } 237103846Sjmallett if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 238103518Sjmallett p = newargs[0]; 239103846Sjmallett for (;;) { 240103846Sjmallett if (equal(p, "sh") || equal(p, "ash")) { 241103846Sjmallett return; 242103846Sjmallett } 243103846Sjmallett while (*p != '/') { 244103518Sjmallett if (*p == '\0') 245103518Sjmallett goto break2; 246103846Sjmallett p++; 247103846Sjmallett } 248103846Sjmallett p++; 249103518Sjmallett } 250103518Sjmallettbreak2:; 251103846Sjmallett } 252103518Sjmallett i = (char *)ap - (char *)newargs; /* size in bytes */ 253103846Sjmallett if (i == 0) 254103846Sjmallett error("Bad #! line"); 255103518Sjmallett for (ap2 = argv ; *ap2++ != NULL ; ); 256103846Sjmallett new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 257103846Sjmallett ap = newargs, ap2 = new; 258103518Sjmallett while ((i -= sizeof (char **)) >= 0) 259116088Sjmallett *ap2++ = *ap++; 260116085Sjmallett ap = argv; 261116085Sjmallett while (*ap2++ = *ap++); 262227191Sed shellexec(new, envp, pathval(), 0); 263258394Speter} 264258394Speter#endif 265258394Speter 266258394Speter 267258394Speter 268258394Speter/* 269258394Speter * Do a path search. The variable path (passed by reference) should be 270258394Speter * set to the start of the path before the first call; padvance will update 271258394Speter * this value as it proceeds. Successive calls to padvance will return 272258394Speter * the possible path expansions in sequence. If an option (indicated by 273258394Speter * a percent sign) appears in the path entry then the global variable 274258394Speter * pathopt will be set to point to it; otherwise pathopt will be set to 275258394Speter * NULL. 276258394Speter */ 277258394Speter 278258394Speterchar *pathopt; 279258394Speter 280258394Speterchar * 281102944Sdwmalonepadvance(char **path, char *name) 2821590Srgrimes{ 283258394Speter char *p, *q; 2841590Srgrimes char *start; 2851590Srgrimes int len; 286 287 if (*path == NULL) 288 return NULL; 289 start = *path; 290 for (p = start ; *p && *p != ':' && *p != '%' ; p++); 291 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 292 while (stackblocksize() < len) 293 growstackblock(); 294 q = stackblock(); 295 if (p != start) { 296 memcpy(q, start, p - start); 297 q += p - start; 298 *q++ = '/'; 299 } 300 strcpy(q, name); 301 pathopt = NULL; 302 if (*p == '%') { 303 pathopt = ++p; 304 while (*p && *p != ':') p++; 305 } 306 if (*p == ':') 307 *path = p + 1; 308 else 309 *path = NULL; 310 return stalloc(len); 311} 312 313 314 315/*** Command hashing code ***/ 316 317 318int 319hashcmd(int argc __unused, char **argv __unused) 320{ 321 struct tblentry **pp; 322 struct tblentry *cmdp; 323 int c; 324 int verbose; 325 struct cmdentry entry; 326 char *name; 327 328 verbose = 0; 329 while ((c = nextopt("rv")) != '\0') { 330 if (c == 'r') { 331 clearcmdentry(0); 332 } else if (c == 'v') { 333 verbose++; 334 } 335 } 336 if (*argptr == NULL) { 337 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 338 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 339 printentry(cmdp, verbose); 340 } 341 } 342 return 0; 343 } 344 while ((name = *argptr) != NULL) { 345 if ((cmdp = cmdlookup(name, 0)) != NULL 346 && (cmdp->cmdtype == CMDNORMAL 347 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) 348 delete_cmd_entry(); 349 find_command(name, &entry, 1, pathval()); 350 if (verbose) { 351 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 352 cmdp = cmdlookup(name, 0); 353 if (cmdp != NULL) 354 printentry(cmdp, verbose); 355 else 356 outfmt(&errout, "%s: not found\n", name); 357 } 358 flushall(); 359 } 360 argptr++; 361 } 362 return 0; 363} 364 365 366STATIC void 367printentry(struct tblentry *cmdp, int verbose) 368{ 369 int index; 370 char *path; 371 char *name; 372 373 if (cmdp->cmdtype == CMDNORMAL) { 374 index = cmdp->param.index; 375 path = pathval(); 376 do { 377 name = padvance(&path, cmdp->cmdname); 378 stunalloc(name); 379 } while (--index >= 0); 380 out1str(name); 381 } else if (cmdp->cmdtype == CMDBUILTIN) { 382 out1fmt("builtin %s", cmdp->cmdname); 383 } else if (cmdp->cmdtype == CMDFUNCTION) { 384 out1fmt("function %s", cmdp->cmdname); 385 if (verbose) { 386 INTOFF; 387 name = commandtext(cmdp->param.func); 388 out1c(' '); 389 out1str(name); 390 ckfree(name); 391 INTON; 392 } 393#ifdef DEBUG 394 } else { 395 error("internal error: cmdtype %d", cmdp->cmdtype); 396#endif 397 } 398 if (cmdp->rehash) 399 out1c('*'); 400 out1c('\n'); 401} 402 403 404 405/* 406 * Resolve a command name. If you change this routine, you may have to 407 * change the shellexec routine as well. 408 */ 409 410void 411find_command(char *name, struct cmdentry *entry, int printerr, char *path) 412{ 413 struct tblentry *cmdp; 414 int index; 415 int prev; 416 char *fullname; 417 struct stat statb; 418 int e; 419 int i; 420 421 /* If name contains a slash, don't use the hash table */ 422 if (strchr(name, '/') != NULL) { 423 entry->cmdtype = CMDNORMAL; 424 entry->u.index = 0; 425 return; 426 } 427 428 /* If name is in the table, and not invalidated by cd, we're done */ 429 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 430 goto success; 431 432 /* If %builtin not in path, check for builtin next */ 433 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 434 INTOFF; 435 cmdp = cmdlookup(name, 1); 436 cmdp->cmdtype = CMDBUILTIN; 437 cmdp->param.index = i; 438 INTON; 439 goto success; 440 } 441 442 /* We have to search path. */ 443 prev = -1; /* where to start */ 444 if (cmdp) { /* doing a rehash */ 445 if (cmdp->cmdtype == CMDBUILTIN) 446 prev = builtinloc; 447 else 448 prev = cmdp->param.index; 449 } 450 451 e = ENOENT; 452 index = -1; 453loop: 454 while ((fullname = padvance(&path, name)) != NULL) { 455 stunalloc(fullname); 456 index++; 457 if (pathopt) { 458 if (prefix("builtin", pathopt)) { 459 if ((i = find_builtin(name)) < 0) 460 goto loop; 461 INTOFF; 462 cmdp = cmdlookup(name, 1); 463 cmdp->cmdtype = CMDBUILTIN; 464 cmdp->param.index = i; 465 INTON; 466 goto success; 467 } else if (prefix("func", pathopt)) { 468 /* handled below */ 469 } else { 470 goto loop; /* ignore unimplemented options */ 471 } 472 } 473 /* if rehash, don't redo absolute path names */ 474 if (fullname[0] == '/' && index <= prev) { 475 if (index < prev) 476 goto loop; 477 TRACE(("searchexec \"%s\": no change\n", name)); 478 goto success; 479 } 480 while (stat(fullname, &statb) < 0) { 481#ifdef SYSV 482 if (errno == EINTR) 483 continue; 484#endif 485 if (errno != ENOENT && errno != ENOTDIR) 486 e = errno; 487 goto loop; 488 } 489 e = EACCES; /* if we fail, this will be the error */ 490 if (!S_ISREG(statb.st_mode)) 491 goto loop; 492 if (pathopt) { /* this is a %func directory */ 493 stalloc(strlen(fullname) + 1); 494 readcmdfile(fullname); 495 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 496 error("%s not defined in %s", name, fullname); 497 stunalloc(fullname); 498 goto success; 499 } 500#ifdef notdef 501 if (statb.st_uid == geteuid()) { 502 if ((statb.st_mode & 0100) == 0) 503 goto loop; 504 } else if (statb.st_gid == getegid()) { 505 if ((statb.st_mode & 010) == 0) 506 goto loop; 507 } else { 508 if ((statb.st_mode & 01) == 0) 509 goto loop; 510 } 511#endif 512 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 513 INTOFF; 514 cmdp = cmdlookup(name, 1); 515 cmdp->cmdtype = CMDNORMAL; 516 cmdp->param.index = index; 517 INTON; 518 goto success; 519 } 520 521 /* We failed. If there was an entry for this command, delete it */ 522 if (cmdp) 523 delete_cmd_entry(); 524 if (printerr) 525 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 526 entry->cmdtype = CMDUNKNOWN; 527 return; 528 529success: 530 cmdp->rehash = 0; 531 entry->cmdtype = cmdp->cmdtype; 532 entry->u = cmdp->param; 533} 534 535 536 537/* 538 * Search the table of builtin commands. 539 */ 540 541int 542find_builtin(char *name) 543{ 544 const struct builtincmd *bp; 545 546 for (bp = builtincmd ; bp->name ; bp++) { 547 if (*bp->name == *name && equal(bp->name, name)) 548 return bp->code; 549 } 550 return -1; 551} 552 553 554 555/* 556 * Called when a cd is done. Marks all commands so the next time they 557 * are executed they will be rehashed. 558 */ 559 560void 561hashcd(void) 562{ 563 struct tblentry **pp; 564 struct tblentry *cmdp; 565 566 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 567 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 568 if (cmdp->cmdtype == CMDNORMAL 569 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 570 cmdp->rehash = 1; 571 } 572 } 573} 574 575 576 577/* 578 * Called before PATH is changed. The argument is the new value of PATH; 579 * pathval() still returns the old value at this point. Called with 580 * interrupts off. 581 */ 582 583void 584changepath(const char *newval) 585{ 586 const char *old, *new; 587 int index; 588 int firstchange; 589 int bltin; 590 591 old = pathval(); 592 new = newval; 593 firstchange = 9999; /* assume no change */ 594 index = 0; 595 bltin = -1; 596 for (;;) { 597 if (*old != *new) { 598 firstchange = index; 599 if ((*old == '\0' && *new == ':') 600 || (*old == ':' && *new == '\0')) 601 firstchange++; 602 old = new; /* ignore subsequent differences */ 603 } 604 if (*new == '\0') 605 break; 606 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 607 bltin = index; 608 if (*new == ':') { 609 index++; 610 } 611 new++, old++; 612 } 613 if (builtinloc < 0 && bltin >= 0) 614 builtinloc = bltin; /* zap builtins */ 615 if (builtinloc >= 0 && bltin < 0) 616 firstchange = 0; 617 clearcmdentry(firstchange); 618 builtinloc = bltin; 619} 620 621 622/* 623 * Clear out command entries. The argument specifies the first entry in 624 * PATH which has changed. 625 */ 626 627void 628clearcmdentry(int firstchange) 629{ 630 struct tblentry **tblp; 631 struct tblentry **pp; 632 struct tblentry *cmdp; 633 634 INTOFF; 635 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 636 pp = tblp; 637 while ((cmdp = *pp) != NULL) { 638 if ((cmdp->cmdtype == CMDNORMAL && 639 cmdp->param.index >= firstchange) 640 || (cmdp->cmdtype == CMDBUILTIN && 641 builtinloc >= firstchange)) { 642 *pp = cmdp->next; 643 ckfree(cmdp); 644 } else { 645 pp = &cmdp->next; 646 } 647 } 648 } 649 INTON; 650} 651 652 653/* 654 * Delete all functions. 655 */ 656 657#ifdef mkinit 658MKINIT void deletefuncs(); 659 660SHELLPROC { 661 deletefuncs(); 662} 663#endif 664 665void 666deletefuncs(void) 667{ 668 struct tblentry **tblp; 669 struct tblentry **pp; 670 struct tblentry *cmdp; 671 672 INTOFF; 673 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 674 pp = tblp; 675 while ((cmdp = *pp) != NULL) { 676 if (cmdp->cmdtype == CMDFUNCTION) { 677 *pp = cmdp->next; 678 freefunc(cmdp->param.func); 679 ckfree(cmdp); 680 } else { 681 pp = &cmdp->next; 682 } 683 } 684 } 685 INTON; 686} 687 688 689 690/* 691 * Locate a command in the command hash table. If "add" is nonzero, 692 * add the command to the table if it is not already present. The 693 * variable "lastcmdentry" is set to point to the address of the link 694 * pointing to the entry, so that delete_cmd_entry can delete the 695 * entry. 696 */ 697 698struct tblentry **lastcmdentry; 699 700 701STATIC struct tblentry * 702cmdlookup(char *name, int add) 703{ 704 int hashval; 705 char *p; 706 struct tblentry *cmdp; 707 struct tblentry **pp; 708 709 p = name; 710 hashval = *p << 4; 711 while (*p) 712 hashval += *p++; 713 hashval &= 0x7FFF; 714 pp = &cmdtable[hashval % CMDTABLESIZE]; 715 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 716 if (equal(cmdp->cmdname, name)) 717 break; 718 pp = &cmdp->next; 719 } 720 if (add && cmdp == NULL) { 721 INTOFF; 722 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 723 + strlen(name) + 1); 724 cmdp->next = NULL; 725 cmdp->cmdtype = CMDUNKNOWN; 726 cmdp->rehash = 0; 727 strcpy(cmdp->cmdname, name); 728 INTON; 729 } 730 lastcmdentry = pp; 731 return cmdp; 732} 733 734/* 735 * Delete the command entry returned on the last lookup. 736 */ 737 738STATIC void 739delete_cmd_entry(void) 740{ 741 struct tblentry *cmdp; 742 743 INTOFF; 744 cmdp = *lastcmdentry; 745 *lastcmdentry = cmdp->next; 746 ckfree(cmdp); 747 INTON; 748} 749 750 751 752/* 753 * Add a new command entry, replacing any existing command entry for 754 * the same name. 755 */ 756 757void 758addcmdentry(char *name, struct cmdentry *entry) 759{ 760 struct tblentry *cmdp; 761 762 INTOFF; 763 cmdp = cmdlookup(name, 1); 764 if (cmdp->cmdtype == CMDFUNCTION) { 765 freefunc(cmdp->param.func); 766 } 767 cmdp->cmdtype = entry->cmdtype; 768 cmdp->param = entry->u; 769 INTON; 770} 771 772 773/* 774 * Define a shell function. 775 */ 776 777void 778defun(char *name, union node *func) 779{ 780 struct cmdentry entry; 781 782 INTOFF; 783 entry.cmdtype = CMDFUNCTION; 784 entry.u.func = copyfunc(func); 785 addcmdentry(name, &entry); 786 INTON; 787} 788 789 790/* 791 * Delete a function if it exists. 792 */ 793 794int 795unsetfunc(char *name) 796{ 797 struct tblentry *cmdp; 798 799 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 800 freefunc(cmdp->param.func); 801 delete_cmd_entry(); 802 return (0); 803 } 804 return (1); 805} 806 807/* 808 * Locate and print what a word is... 809 */ 810 811int 812typecmd(int argc, char **argv) 813{ 814 struct cmdentry entry; 815 struct tblentry *cmdp; 816 char **pp; 817 struct alias *ap; 818 int i; 819 int error = 0; 820 extern char *const parsekwd[]; 821 822 for (i = 1; i < argc; i++) { 823 out1str(argv[i]); 824 /* First look at the keywords */ 825 for (pp = (char **)parsekwd; *pp; pp++) 826 if (**pp == *argv[i] && equal(*pp, argv[i])) 827 break; 828 829 if (*pp) { 830 out1str(" is a shell keyword\n"); 831 continue; 832 } 833 834 /* Then look at the aliases */ 835 if ((ap = lookupalias(argv[i], 1)) != NULL) { 836 out1fmt(" is an alias for %s\n", ap->val); 837 continue; 838 } 839 840 /* Then check if it is a tracked alias */ 841 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 842 entry.cmdtype = cmdp->cmdtype; 843 entry.u = cmdp->param; 844 } 845 else { 846 /* Finally use brute force */ 847 find_command(argv[i], &entry, 0, pathval()); 848 } 849 850 switch (entry.cmdtype) { 851 case CMDNORMAL: { 852 if (strchr(argv[i], '/') == NULL) { 853 char *path = pathval(), *name; 854 int j = entry.u.index; 855 do { 856 name = padvance(&path, argv[i]); 857 stunalloc(name); 858 } while (--j >= 0); 859 out1fmt(" is%s %s\n", 860 cmdp ? " a tracked alias for" : "", name); 861 } else { 862 if (access(argv[i], X_OK) == 0) 863 out1fmt(" is %s\n", argv[i]); 864 else 865 out1fmt(": %s\n", strerror(errno)); 866 } 867 break; 868 } 869 case CMDFUNCTION: 870 out1str(" is a shell function\n"); 871 break; 872 873 case CMDBUILTIN: 874 out1str(" is a shell builtin\n"); 875 break; 876 877 default: 878 out1str(": not found\n"); 879 error |= 127; 880 break; 881 } 882 } 883 return error; 884} 885