1 /* $NetBSD: exec.c,v 1.45 2013/11/01 16:49:02 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 39 #else 40 __RCSID("$NetBSD: exec.c,v 1.45 2013/11/01 16:49:02 christos Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <sys/wait.h> 47 #include <unistd.h> 48 #include <fcntl.h> 49 #include <errno.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 53 /* 54 * When commands are first encountered, they are entered in a hash table. 55 * This ensures that a full path search will not have to be done for them 56 * on each invocation. 57 * 58 * We should investigate converting to a linear search, even though that 59 * would make the command name "hash" a misnomer. 60 */ 61 62 #include "shell.h" 63 #include "main.h" 64 #include "nodes.h" 65 #include "parser.h" 66 #include "redir.h" 67 #include "eval.h" 68 #include "exec.h" 69 #include "builtins.h" 70 #include "var.h" 71 #include "options.h" 72 #include "input.h" 73 #include "output.h" 74 #include "syntax.h" 75 #include "memalloc.h" 76 #include "error.h" 77 #include "init.h" 78 #include "mystring.h" 79 #include "show.h" 80 #include "jobs.h" 81 #include "alias.h" 82 83 84 #define CMDTABLESIZE 31 /* should be prime */ 85 #define ARB 1 /* actual size determined at run time */ 86 87 88 89 struct tblentry { 90 struct tblentry *next; /* next entry in hash chain */ 91 union param param; /* definition of builtin function */ 92 short cmdtype; /* index identifying command */ 93 char rehash; /* if set, cd done since entry created */ 94 char cmdname[ARB]; /* name of command */ 95 }; 96 97 98 STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 99 STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 100 int exerrno = 0; /* Last exec error */ 101 102 103 STATIC void tryexec(char *, char **, char **, int); 104 STATIC void printentry(struct tblentry *, int); 105 STATIC void clearcmdentry(int); 106 STATIC struct tblentry *cmdlookup(const char *, int); 107 STATIC void delete_cmd_entry(void); 108 109 #ifndef BSD 110 STATIC void execinterp(char **, char **); 111 #endif 112 113 114 extern const char *const parsekwd[]; 115 116 /* 117 * Exec a program. Never returns. If you change this routine, you may 118 * have to change the find_command routine as well. 119 */ 120 121 void 122 shellexec(char **argv, char **envp, const char *path, int idx, int vforked) 123 { 124 char *cmdname; 125 int e; 126 127 if (strchr(argv[0], '/') != NULL) { 128 tryexec(argv[0], argv, envp, vforked); 129 e = errno; 130 } else { 131 e = ENOENT; 132 while ((cmdname = padvance(&path, argv[0])) != NULL) { 133 if (--idx < 0 && pathopt == NULL) { 134 tryexec(cmdname, argv, envp, vforked); 135 if (errno != ENOENT && errno != ENOTDIR) 136 e = errno; 137 } 138 stunalloc(cmdname); 139 } 140 } 141 142 /* Map to POSIX errors */ 143 switch (e) { 144 case EACCES: 145 exerrno = 126; 146 break; 147 case ENOENT: 148 exerrno = 127; 149 break; 150 default: 151 exerrno = 2; 152 break; 153 } 154 TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", 155 argv[0], e, vforked, suppressint )); 156 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); 157 /* NOTREACHED */ 158 } 159 160 161 STATIC void 162 tryexec(char *cmd, char **argv, char **envp, int vforked) 163 { 164 int e; 165 #ifndef BSD 166 char *p; 167 #endif 168 169 #ifdef SYSV 170 do { 171 execve(cmd, argv, envp); 172 } while (errno == EINTR); 173 #else 174 execve(cmd, argv, envp); 175 #endif 176 e = errno; 177 if (e == ENOEXEC) { 178 if (vforked) { 179 /* We are currently vfork(2)ed, so raise an 180 * exception, and evalcommand will try again 181 * with a normal fork(2). 182 */ 183 exraise(EXSHELLPROC); 184 } 185 #ifdef DEBUG 186 TRACE(("execve(cmd=%s) returned ENOEXEC\n", cmd)); 187 #endif 188 initshellproc(); 189 setinputfile(cmd, 0); 190 commandname = arg0 = savestr(argv[0]); 191 #ifndef BSD 192 pgetc(); pungetc(); /* fill up input buffer */ 193 p = parsenextc; 194 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 195 argv[0] = cmd; 196 execinterp(argv, envp); 197 } 198 #endif 199 setparam(argv + 1); 200 exraise(EXSHELLPROC); 201 } 202 errno = e; 203 } 204 205 206 #ifndef BSD 207 /* 208 * Execute an interpreter introduced by "#!", for systems where this 209 * feature has not been built into the kernel. If the interpreter is 210 * the shell, return (effectively ignoring the "#!"). If the execution 211 * of the interpreter fails, exit. 212 * 213 * This code peeks inside the input buffer in order to avoid actually 214 * reading any input. It would benefit from a rewrite. 215 */ 216 217 #define NEWARGS 5 218 219 STATIC void 220 execinterp(char **argv, char **envp) 221 { 222 int n; 223 char *inp; 224 char *outp; 225 char c; 226 char *p; 227 char **ap; 228 char *newargs[NEWARGS]; 229 int i; 230 char **ap2; 231 char **new; 232 233 n = parsenleft - 2; 234 inp = parsenextc + 2; 235 ap = newargs; 236 for (;;) { 237 while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 238 inp++; 239 if (n < 0) 240 goto bad; 241 if ((c = *inp++) == '\n') 242 break; 243 if (ap == &newargs[NEWARGS]) 244 bad: error("Bad #! line"); 245 STARTSTACKSTR(outp); 246 do { 247 STPUTC(c, outp); 248 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 249 STPUTC('\0', outp); 250 n++, inp--; 251 *ap++ = grabstackstr(outp); 252 } 253 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 254 p = newargs[0]; 255 for (;;) { 256 if (equal(p, "sh") || equal(p, "ash")) { 257 return; 258 } 259 while (*p != '/') { 260 if (*p == '\0') 261 goto break2; 262 p++; 263 } 264 p++; 265 } 266 break2:; 267 } 268 i = (char *)ap - (char *)newargs; /* size in bytes */ 269 if (i == 0) 270 error("Bad #! line"); 271 for (ap2 = argv ; *ap2++ != NULL ; ); 272 new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 273 ap = newargs, ap2 = new; 274 while ((i -= sizeof (char **)) >= 0) 275 *ap2++ = *ap++; 276 ap = argv; 277 while (*ap2++ = *ap++); 278 shellexec(new, envp, pathval(), 0); 279 /* NOTREACHED */ 280 } 281 #endif 282 283 284 285 /* 286 * Do a path search. The variable path (passed by reference) should be 287 * set to the start of the path before the first call; padvance will update 288 * this value as it proceeds. Successive calls to padvance will return 289 * the possible path expansions in sequence. If an option (indicated by 290 * a percent sign) appears in the path entry then the global variable 291 * pathopt will be set to point to it; otherwise pathopt will be set to 292 * NULL. 293 */ 294 295 const char *pathopt; 296 297 char * 298 padvance(const char **path, const char *name) 299 { 300 const char *p; 301 char *q; 302 const char *start; 303 int len; 304 305 if (*path == NULL) 306 return NULL; 307 start = *path; 308 for (p = start ; *p && *p != ':' && *p != '%' ; p++); 309 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 310 while (stackblocksize() < len) 311 growstackblock(); 312 q = stackblock(); 313 if (p != start) { 314 memcpy(q, start, p - start); 315 q += p - start; 316 *q++ = '/'; 317 } 318 strcpy(q, name); 319 pathopt = NULL; 320 if (*p == '%') { 321 pathopt = ++p; 322 while (*p && *p != ':') p++; 323 } 324 if (*p == ':') 325 *path = p + 1; 326 else 327 *path = NULL; 328 return stalloc(len); 329 } 330 331 332 333 /*** Command hashing code ***/ 334 335 336 int 337 hashcmd(int argc, char **argv) 338 { 339 struct tblentry **pp; 340 struct tblentry *cmdp; 341 int c; 342 int verbose; 343 struct cmdentry entry; 344 char *name; 345 346 verbose = 0; 347 while ((c = nextopt("rv")) != '\0') { 348 if (c == 'r') { 349 clearcmdentry(0); 350 } else if (c == 'v') { 351 verbose++; 352 } 353 } 354 if (*argptr == NULL) { 355 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 356 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 357 if (verbose || cmdp->cmdtype == CMDNORMAL) 358 printentry(cmdp, verbose); 359 } 360 } 361 return 0; 362 } 363 while ((name = *argptr) != NULL) { 364 if ((cmdp = cmdlookup(name, 0)) != NULL 365 && (cmdp->cmdtype == CMDNORMAL 366 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) 367 delete_cmd_entry(); 368 find_command(name, &entry, DO_ERR, pathval()); 369 if (verbose) { 370 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 371 cmdp = cmdlookup(name, 0); 372 if (cmdp != NULL) 373 printentry(cmdp, verbose); 374 } 375 flushall(); 376 } 377 argptr++; 378 } 379 return 0; 380 } 381 382 383 STATIC void 384 printentry(struct tblentry *cmdp, int verbose) 385 { 386 int idx; 387 const char *path; 388 char *name; 389 390 switch (cmdp->cmdtype) { 391 case CMDNORMAL: 392 idx = cmdp->param.index; 393 path = pathval(); 394 do { 395 name = padvance(&path, cmdp->cmdname); 396 stunalloc(name); 397 } while (--idx >= 0); 398 out1str(name); 399 break; 400 case CMDSPLBLTIN: 401 out1fmt("special builtin %s", cmdp->cmdname); 402 break; 403 case CMDBUILTIN: 404 out1fmt("builtin %s", cmdp->cmdname); 405 break; 406 case CMDFUNCTION: 407 out1fmt("function %s", cmdp->cmdname); 408 if (verbose) { 409 struct procstat ps; 410 INTOFF; 411 commandtext(&ps, cmdp->param.func); 412 INTON; 413 out1str("() { "); 414 out1str(ps.cmd); 415 out1str("; }"); 416 } 417 break; 418 default: 419 error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype); 420 } 421 if (cmdp->rehash) 422 out1c('*'); 423 out1c('\n'); 424 } 425 426 427 428 /* 429 * Resolve a command name. If you change this routine, you may have to 430 * change the shellexec routine as well. 431 */ 432 433 void 434 find_command(char *name, struct cmdentry *entry, int act, const char *path) 435 { 436 struct tblentry *cmdp, loc_cmd; 437 int idx; 438 int prev; 439 char *fullname; 440 struct stat statb; 441 int e; 442 int (*bltin)(int,char **); 443 444 /* If name contains a slash, don't use PATH or hash table */ 445 if (strchr(name, '/') != NULL) { 446 if (act & DO_ABS) { 447 while (stat(name, &statb) < 0) { 448 #ifdef SYSV 449 if (errno == EINTR) 450 continue; 451 #endif 452 if (errno != ENOENT && errno != ENOTDIR) 453 e = errno; 454 entry->cmdtype = CMDUNKNOWN; 455 entry->u.index = -1; 456 return; 457 } 458 entry->cmdtype = CMDNORMAL; 459 entry->u.index = -1; 460 return; 461 } 462 entry->cmdtype = CMDNORMAL; 463 entry->u.index = 0; 464 return; 465 } 466 467 if (path != pathval()) 468 act |= DO_ALTPATH; 469 470 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) 471 act |= DO_ALTBLTIN; 472 473 /* If name is in the table, check answer will be ok */ 474 if ((cmdp = cmdlookup(name, 0)) != NULL) { 475 do { 476 switch (cmdp->cmdtype) { 477 case CMDNORMAL: 478 if (act & DO_ALTPATH) { 479 cmdp = NULL; 480 continue; 481 } 482 break; 483 case CMDFUNCTION: 484 if (act & DO_NOFUNC) { 485 cmdp = NULL; 486 continue; 487 } 488 break; 489 case CMDBUILTIN: 490 if ((act & DO_ALTBLTIN) || builtinloc >= 0) { 491 cmdp = NULL; 492 continue; 493 } 494 break; 495 } 496 /* if not invalidated by cd, we're done */ 497 if (cmdp->rehash == 0) 498 goto success; 499 } while (0); 500 } 501 502 /* If %builtin not in path, check for builtin next */ 503 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && 504 (bltin = find_builtin(name)) != 0) 505 goto builtin_success; 506 507 /* We have to search path. */ 508 prev = -1; /* where to start */ 509 if (cmdp) { /* doing a rehash */ 510 if (cmdp->cmdtype == CMDBUILTIN) 511 prev = builtinloc; 512 else 513 prev = cmdp->param.index; 514 } 515 516 e = ENOENT; 517 idx = -1; 518 loop: 519 while ((fullname = padvance(&path, name)) != NULL) { 520 stunalloc(fullname); 521 idx++; 522 if (pathopt) { 523 if (prefix("builtin", pathopt)) { 524 if ((bltin = find_builtin(name)) == 0) 525 goto loop; 526 goto builtin_success; 527 } else if (prefix("func", pathopt)) { 528 /* handled below */ 529 } else { 530 /* ignore unimplemented options */ 531 goto loop; 532 } 533 } 534 /* if rehash, don't redo absolute path names */ 535 if (fullname[0] == '/' && idx <= prev) { 536 if (idx < prev) 537 goto loop; 538 TRACE(("searchexec \"%s\": no change\n", name)); 539 goto success; 540 } 541 while (stat(fullname, &statb) < 0) { 542 #ifdef SYSV 543 if (errno == EINTR) 544 continue; 545 #endif 546 if (errno != ENOENT && errno != ENOTDIR) 547 e = errno; 548 goto loop; 549 } 550 e = EACCES; /* if we fail, this will be the error */ 551 if (!S_ISREG(statb.st_mode)) 552 goto loop; 553 if (pathopt) { /* this is a %func directory */ 554 if (act & DO_NOFUNC) 555 goto loop; 556 stalloc(strlen(fullname) + 1); 557 readcmdfile(fullname); 558 if ((cmdp = cmdlookup(name, 0)) == NULL || 559 cmdp->cmdtype != CMDFUNCTION) 560 error("%s not defined in %s", name, fullname); 561 stunalloc(fullname); 562 goto success; 563 } 564 #ifdef notdef 565 /* XXX this code stops root executing stuff, and is buggy 566 if you need a group from the group list. */ 567 if (statb.st_uid == geteuid()) { 568 if ((statb.st_mode & 0100) == 0) 569 goto loop; 570 } else if (statb.st_gid == getegid()) { 571 if ((statb.st_mode & 010) == 0) 572 goto loop; 573 } else { 574 if ((statb.st_mode & 01) == 0) 575 goto loop; 576 } 577 #endif 578 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 579 INTOFF; 580 if (act & DO_ALTPATH) { 581 stalloc(strlen(fullname) + 1); 582 cmdp = &loc_cmd; 583 } else 584 cmdp = cmdlookup(name, 1); 585 cmdp->cmdtype = CMDNORMAL; 586 cmdp->param.index = idx; 587 INTON; 588 goto success; 589 } 590 591 /* We failed. If there was an entry for this command, delete it */ 592 if (cmdp) 593 delete_cmd_entry(); 594 if (act & DO_ERR) 595 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 596 entry->cmdtype = CMDUNKNOWN; 597 return; 598 599 builtin_success: 600 INTOFF; 601 if (act & DO_ALTPATH) 602 cmdp = &loc_cmd; 603 else 604 cmdp = cmdlookup(name, 1); 605 if (cmdp->cmdtype == CMDFUNCTION) 606 /* DO_NOFUNC must have been set */ 607 cmdp = &loc_cmd; 608 cmdp->cmdtype = CMDBUILTIN; 609 cmdp->param.bltin = bltin; 610 INTON; 611 success: 612 if (cmdp) { 613 cmdp->rehash = 0; 614 entry->cmdtype = cmdp->cmdtype; 615 entry->u = cmdp->param; 616 } else 617 entry->cmdtype = CMDUNKNOWN; 618 } 619 620 621 622 /* 623 * Search the table of builtin commands. 624 */ 625 626 int 627 (*find_builtin(char *name))(int, char **) 628 { 629 const struct builtincmd *bp; 630 631 for (bp = builtincmd ; bp->name ; bp++) { 632 if (*bp->name == *name 633 && (*name == '%' || equal(bp->name, name))) 634 return bp->builtin; 635 } 636 return 0; 637 } 638 639 int 640 (*find_splbltin(char *name))(int, char **) 641 { 642 const struct builtincmd *bp; 643 644 for (bp = splbltincmd ; bp->name ; bp++) { 645 if (*bp->name == *name && equal(bp->name, name)) 646 return bp->builtin; 647 } 648 return 0; 649 } 650 651 /* 652 * At shell startup put special builtins into hash table. 653 * ensures they are executed first (see posix). 654 * We stop functions being added with the same name 655 * (as they are impossible to call) 656 */ 657 658 void 659 hash_special_builtins(void) 660 { 661 const struct builtincmd *bp; 662 struct tblentry *cmdp; 663 664 for (bp = splbltincmd ; bp->name ; bp++) { 665 cmdp = cmdlookup(bp->name, 1); 666 cmdp->cmdtype = CMDSPLBLTIN; 667 cmdp->param.bltin = bp->builtin; 668 } 669 } 670 671 672 673 /* 674 * Called when a cd is done. Marks all commands so the next time they 675 * are executed they will be rehashed. 676 */ 677 678 void 679 hashcd(void) 680 { 681 struct tblentry **pp; 682 struct tblentry *cmdp; 683 684 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 685 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 686 if (cmdp->cmdtype == CMDNORMAL 687 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 688 cmdp->rehash = 1; 689 } 690 } 691 } 692 693 694 695 /* 696 * Fix command hash table when PATH changed. 697 * Called before PATH is changed. The argument is the new value of PATH; 698 * pathval() still returns the old value at this point. 699 * Called with interrupts off. 700 */ 701 702 void 703 changepath(const char *newval) 704 { 705 const char *old, *new; 706 int idx; 707 int firstchange; 708 int bltin; 709 710 old = pathval(); 711 new = newval; 712 firstchange = 9999; /* assume no change */ 713 idx = 0; 714 bltin = -1; 715 for (;;) { 716 if (*old != *new) { 717 firstchange = idx; 718 if ((*old == '\0' && *new == ':') 719 || (*old == ':' && *new == '\0')) 720 firstchange++; 721 old = new; /* ignore subsequent differences */ 722 } 723 if (*new == '\0') 724 break; 725 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 726 bltin = idx; 727 if (*new == ':') { 728 idx++; 729 } 730 new++, old++; 731 } 732 if (builtinloc < 0 && bltin >= 0) 733 builtinloc = bltin; /* zap builtins */ 734 if (builtinloc >= 0 && bltin < 0) 735 firstchange = 0; 736 clearcmdentry(firstchange); 737 builtinloc = bltin; 738 } 739 740 741 /* 742 * Clear out command entries. The argument specifies the first entry in 743 * PATH which has changed. 744 */ 745 746 STATIC void 747 clearcmdentry(int firstchange) 748 { 749 struct tblentry **tblp; 750 struct tblentry **pp; 751 struct tblentry *cmdp; 752 753 INTOFF; 754 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 755 pp = tblp; 756 while ((cmdp = *pp) != NULL) { 757 if ((cmdp->cmdtype == CMDNORMAL && 758 cmdp->param.index >= firstchange) 759 || (cmdp->cmdtype == CMDBUILTIN && 760 builtinloc >= firstchange)) { 761 *pp = cmdp->next; 762 ckfree(cmdp); 763 } else { 764 pp = &cmdp->next; 765 } 766 } 767 } 768 INTON; 769 } 770 771 772 /* 773 * Delete all functions. 774 */ 775 776 #ifdef mkinit 777 MKINIT void deletefuncs(void); 778 MKINIT void hash_special_builtins(void); 779 780 INIT { 781 hash_special_builtins(); 782 } 783 784 SHELLPROC { 785 deletefuncs(); 786 } 787 #endif 788 789 void 790 deletefuncs(void) 791 { 792 struct tblentry **tblp; 793 struct tblentry **pp; 794 struct tblentry *cmdp; 795 796 INTOFF; 797 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 798 pp = tblp; 799 while ((cmdp = *pp) != NULL) { 800 if (cmdp->cmdtype == CMDFUNCTION) { 801 *pp = cmdp->next; 802 freefunc(cmdp->param.func); 803 ckfree(cmdp); 804 } else { 805 pp = &cmdp->next; 806 } 807 } 808 } 809 INTON; 810 } 811 812 813 814 /* 815 * Locate a command in the command hash table. If "add" is nonzero, 816 * add the command to the table if it is not already present. The 817 * variable "lastcmdentry" is set to point to the address of the link 818 * pointing to the entry, so that delete_cmd_entry can delete the 819 * entry. 820 */ 821 822 struct tblentry **lastcmdentry; 823 824 825 STATIC struct tblentry * 826 cmdlookup(const char *name, int add) 827 { 828 int hashval; 829 const char *p; 830 struct tblentry *cmdp; 831 struct tblentry **pp; 832 833 p = name; 834 hashval = *p << 4; 835 while (*p) 836 hashval += *p++; 837 hashval &= 0x7FFF; 838 pp = &cmdtable[hashval % CMDTABLESIZE]; 839 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 840 if (equal(cmdp->cmdname, name)) 841 break; 842 pp = &cmdp->next; 843 } 844 if (add && cmdp == NULL) { 845 INTOFF; 846 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 847 + strlen(name) + 1); 848 cmdp->next = NULL; 849 cmdp->cmdtype = CMDUNKNOWN; 850 cmdp->rehash = 0; 851 strcpy(cmdp->cmdname, name); 852 INTON; 853 } 854 lastcmdentry = pp; 855 return cmdp; 856 } 857 858 /* 859 * Delete the command entry returned on the last lookup. 860 */ 861 862 STATIC void 863 delete_cmd_entry(void) 864 { 865 struct tblentry *cmdp; 866 867 INTOFF; 868 cmdp = *lastcmdentry; 869 *lastcmdentry = cmdp->next; 870 ckfree(cmdp); 871 INTON; 872 } 873 874 875 876 #ifdef notdef 877 void 878 getcmdentry(char *name, struct cmdentry *entry) 879 { 880 struct tblentry *cmdp = cmdlookup(name, 0); 881 882 if (cmdp) { 883 entry->u = cmdp->param; 884 entry->cmdtype = cmdp->cmdtype; 885 } else { 886 entry->cmdtype = CMDUNKNOWN; 887 entry->u.index = 0; 888 } 889 } 890 #endif 891 892 893 /* 894 * Add a new command entry, replacing any existing command entry for 895 * the same name - except special builtins. 896 */ 897 898 STATIC void 899 addcmdentry(char *name, struct cmdentry *entry) 900 { 901 struct tblentry *cmdp; 902 903 INTOFF; 904 cmdp = cmdlookup(name, 1); 905 if (cmdp->cmdtype != CMDSPLBLTIN) { 906 if (cmdp->cmdtype == CMDFUNCTION) { 907 freefunc(cmdp->param.func); 908 } 909 cmdp->cmdtype = entry->cmdtype; 910 cmdp->param = entry->u; 911 } 912 INTON; 913 } 914 915 916 /* 917 * Define a shell function. 918 */ 919 920 void 921 defun(char *name, union node *func) 922 { 923 struct cmdentry entry; 924 925 INTOFF; 926 entry.cmdtype = CMDFUNCTION; 927 entry.u.func = copyfunc(func); 928 addcmdentry(name, &entry); 929 INTON; 930 } 931 932 933 /* 934 * Delete a function if it exists. 935 */ 936 937 int 938 unsetfunc(char *name) 939 { 940 struct tblentry *cmdp; 941 942 if ((cmdp = cmdlookup(name, 0)) != NULL && 943 cmdp->cmdtype == CMDFUNCTION) { 944 freefunc(cmdp->param.func); 945 delete_cmd_entry(); 946 } 947 return 0; 948 } 949 950 /* 951 * Locate and print what a word is... 952 * also used for 'command -[v|V]' 953 */ 954 955 int 956 typecmd(int argc, char **argv) 957 { 958 struct cmdentry entry; 959 struct tblentry *cmdp; 960 const char * const *pp; 961 struct alias *ap; 962 int err = 0; 963 char *arg; 964 int c; 965 int V_flag = 0; 966 int v_flag = 0; 967 int p_flag = 0; 968 969 while ((c = nextopt("vVp")) != 0) { 970 switch (c) { 971 case 'v': v_flag = 1; break; 972 case 'V': V_flag = 1; break; 973 case 'p': p_flag = 1; break; 974 } 975 } 976 977 if (p_flag && (v_flag || V_flag)) 978 error("cannot specify -p with -v or -V"); 979 980 while ((arg = *argptr++)) { 981 if (!v_flag) 982 out1str(arg); 983 /* First look at the keywords */ 984 for (pp = parsekwd; *pp; pp++) 985 if (**pp == *arg && equal(*pp, arg)) 986 break; 987 988 if (*pp) { 989 if (v_flag) 990 err = 1; 991 else 992 out1str(" is a shell keyword\n"); 993 continue; 994 } 995 996 /* Then look at the aliases */ 997 if ((ap = lookupalias(arg, 1)) != NULL) { 998 if (!v_flag) 999 out1fmt(" is an alias for \n"); 1000 out1fmt("%s\n", ap->val); 1001 continue; 1002 } 1003 1004 /* Then check if it is a tracked alias */ 1005 if ((cmdp = cmdlookup(arg, 0)) != NULL) { 1006 entry.cmdtype = cmdp->cmdtype; 1007 entry.u = cmdp->param; 1008 } else { 1009 /* Finally use brute force */ 1010 find_command(arg, &entry, DO_ABS, pathval()); 1011 } 1012 1013 switch (entry.cmdtype) { 1014 case CMDNORMAL: { 1015 if (strchr(arg, '/') == NULL) { 1016 const char *path = pathval(); 1017 char *name; 1018 int j = entry.u.index; 1019 do { 1020 name = padvance(&path, arg); 1021 stunalloc(name); 1022 } while (--j >= 0); 1023 if (!v_flag) 1024 out1fmt(" is%s ", 1025 cmdp ? " a tracked alias for" : ""); 1026 out1fmt("%s\n", name); 1027 } else { 1028 if (access(arg, X_OK) == 0) { 1029 if (!v_flag) 1030 out1fmt(" is "); 1031 out1fmt("%s\n", arg); 1032 } else { 1033 if (!v_flag) 1034 out1fmt(": %s\n", 1035 strerror(errno)); 1036 else 1037 err = 126; 1038 } 1039 } 1040 break; 1041 } 1042 case CMDFUNCTION: 1043 if (!v_flag) 1044 out1str(" is a shell function\n"); 1045 else 1046 out1fmt("%s\n", arg); 1047 break; 1048 1049 case CMDBUILTIN: 1050 if (!v_flag) 1051 out1str(" is a shell builtin\n"); 1052 else 1053 out1fmt("%s\n", arg); 1054 break; 1055 1056 case CMDSPLBLTIN: 1057 if (!v_flag) 1058 out1str(" is a special shell builtin\n"); 1059 else 1060 out1fmt("%s\n", arg); 1061 break; 1062 1063 default: 1064 if (!v_flag) 1065 out1str(": not found\n"); 1066 err = 127; 1067 break; 1068 } 1069 } 1070 return err; 1071 } 1072