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