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