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 "$FreeBSD$"; 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(char *, char **, char **); 103 #ifndef BSD 104 STATIC void execinterp(char **, char **); 105 #endif 106 STATIC void printentry(struct tblentry *, int); 107 STATIC struct tblentry *cmdlookup(char *, int); 108 STATIC void delete_cmd_entry(void); 109 110 111 112 /* 113 * Exec a program. Never returns. If you change this routine, you may 114 * have to change the find_command routine as well. 115 */ 116 117 void 118 shellexec(char **argv, char **envp, char *path, int index) 119 { 120 char *cmdname; 121 int e; 122 123 if (strchr(argv[0], '/') != NULL) { 124 tryexec(argv[0], argv, envp); 125 e = errno; 126 } else { 127 e = ENOENT; 128 while ((cmdname = padvance(&path, argv[0])) != NULL) { 129 if (--index < 0 && pathopt == NULL) { 130 tryexec(cmdname, argv, envp); 131 if (errno != ENOENT && errno != ENOTDIR) 132 e = errno; 133 } 134 stunalloc(cmdname); 135 } 136 } 137 138 /* Map to POSIX errors */ 139 switch (e) { 140 case EACCES: 141 exerrno = 126; 142 break; 143 case ENOENT: 144 exerrno = 127; 145 break; 146 default: 147 exerrno = 2; 148 break; 149 } 150 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); 151 } 152 153 154 STATIC void 155 tryexec(char *cmd, char **argv, char **envp) 156 { 157 int e; 158 #ifndef BSD 159 char *p; 160 #endif 161 162 #ifdef SYSV 163 do { 164 execve(cmd, argv, envp); 165 } while (errno == EINTR); 166 #else 167 execve(cmd, argv, envp); 168 #endif 169 e = errno; 170 if (e == ENOEXEC) { 171 initshellproc(); 172 setinputfile(cmd, 0); 173 commandname = arg0 = savestr(argv[0]); 174 #ifndef BSD 175 pgetc(); pungetc(); /* fill up input buffer */ 176 p = parsenextc; 177 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 178 argv[0] = cmd; 179 execinterp(argv, envp); 180 } 181 #endif 182 setparam(argv + 1); 183 exraise(EXSHELLPROC); 184 /*NOTREACHED*/ 185 } 186 errno = e; 187 } 188 189 190 #ifndef BSD 191 /* 192 * Execute an interpreter introduced by "#!", for systems where this 193 * feature has not been built into the kernel. If the interpreter is 194 * the shell, return (effectively ignoring the "#!"). If the execution 195 * of the interpreter fails, exit. 196 * 197 * This code peeks inside the input buffer in order to avoid actually 198 * reading any input. It would benefit from a rewrite. 199 */ 200 201 #define NEWARGS 5 202 203 STATIC void 204 execinterp(char **argv, char **envp) 205 { 206 int n; 207 char *inp; 208 char *outp; 209 char c; 210 char *p; 211 char **ap; 212 char *newargs[NEWARGS]; 213 int i; 214 char **ap2; 215 char **new; 216 217 n = parsenleft - 2; 218 inp = parsenextc + 2; 219 ap = newargs; 220 for (;;) { 221 while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 222 inp++; 223 if (n < 0) 224 goto bad; 225 if ((c = *inp++) == '\n') 226 break; 227 if (ap == &newargs[NEWARGS]) 228 bad: error("Bad #! line"); 229 STARTSTACKSTR(outp); 230 do { 231 STPUTC(c, outp); 232 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 233 STPUTC('\0', outp); 234 n++, inp--; 235 *ap++ = grabstackstr(outp); 236 } 237 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 238 p = newargs[0]; 239 for (;;) { 240 if (equal(p, "sh") || equal(p, "ash")) { 241 return; 242 } 243 while (*p != '/') { 244 if (*p == '\0') 245 goto break2; 246 p++; 247 } 248 p++; 249 } 250 break2:; 251 } 252 i = (char *)ap - (char *)newargs; /* size in bytes */ 253 if (i == 0) 254 error("Bad #! line"); 255 for (ap2 = argv ; *ap2++ != NULL ; ); 256 new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 257 ap = newargs, ap2 = new; 258 while ((i -= sizeof (char **)) >= 0) 259 *ap2++ = *ap++; 260 ap = argv; 261 while (*ap2++ = *ap++); 262 shellexec(new, envp, pathval(), 0); 263 } 264 #endif 265 266 267 268 /* 269 * Do a path search. The variable path (passed by reference) should be 270 * set to the start of the path before the first call; padvance will update 271 * this value as it proceeds. Successive calls to padvance will return 272 * the possible path expansions in sequence. If an option (indicated by 273 * a percent sign) appears in the path entry then the global variable 274 * pathopt will be set to point to it; otherwise pathopt will be set to 275 * NULL. 276 */ 277 278 char *pathopt; 279 280 char * 281 padvance(char **path, char *name) 282 { 283 char *p, *q; 284 char *start; 285 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 318 int 319 hashcmd(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 printentry(cmdp, verbose); 354 } 355 flushall(); 356 } 357 argptr++; 358 } 359 return 0; 360 } 361 362 363 STATIC void 364 printentry(struct tblentry *cmdp, int verbose) 365 { 366 int index; 367 char *path; 368 char *name; 369 370 if (cmdp->cmdtype == CMDNORMAL) { 371 index = cmdp->param.index; 372 path = pathval(); 373 do { 374 name = padvance(&path, cmdp->cmdname); 375 stunalloc(name); 376 } while (--index >= 0); 377 out1str(name); 378 } else if (cmdp->cmdtype == CMDBUILTIN) { 379 out1fmt("builtin %s", cmdp->cmdname); 380 } else if (cmdp->cmdtype == CMDFUNCTION) { 381 out1fmt("function %s", cmdp->cmdname); 382 if (verbose) { 383 INTOFF; 384 name = commandtext(cmdp->param.func); 385 out1c(' '); 386 out1str(name); 387 ckfree(name); 388 INTON; 389 } 390 #ifdef DEBUG 391 } else { 392 error("internal error: cmdtype %d", cmdp->cmdtype); 393 #endif 394 } 395 if (cmdp->rehash) 396 out1c('*'); 397 out1c('\n'); 398 } 399 400 401 402 /* 403 * Resolve a command name. If you change this routine, you may have to 404 * change the shellexec routine as well. 405 */ 406 407 void 408 find_command(char *name, struct cmdentry *entry, int printerr, char *path) 409 { 410 struct tblentry *cmdp; 411 int index; 412 int prev; 413 char *fullname; 414 struct stat statb; 415 int e; 416 int i; 417 418 /* If name contains a slash, don't use the hash table */ 419 if (strchr(name, '/') != NULL) { 420 entry->cmdtype = CMDNORMAL; 421 entry->u.index = 0; 422 return; 423 } 424 425 /* If name is in the table, and not invalidated by cd, we're done */ 426 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 427 goto success; 428 429 /* If %builtin not in path, check for builtin next */ 430 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 431 INTOFF; 432 cmdp = cmdlookup(name, 1); 433 cmdp->cmdtype = CMDBUILTIN; 434 cmdp->param.index = i; 435 INTON; 436 goto success; 437 } 438 439 /* We have to search path. */ 440 prev = -1; /* where to start */ 441 if (cmdp) { /* doing a rehash */ 442 if (cmdp->cmdtype == CMDBUILTIN) 443 prev = builtinloc; 444 else 445 prev = cmdp->param.index; 446 } 447 448 e = ENOENT; 449 index = -1; 450 loop: 451 while ((fullname = padvance(&path, name)) != NULL) { 452 stunalloc(fullname); 453 index++; 454 if (pathopt) { 455 if (prefix("builtin", pathopt)) { 456 if ((i = find_builtin(name)) < 0) 457 goto loop; 458 INTOFF; 459 cmdp = cmdlookup(name, 1); 460 cmdp->cmdtype = CMDBUILTIN; 461 cmdp->param.index = i; 462 INTON; 463 goto success; 464 } else if (prefix("func", pathopt)) { 465 /* handled below */ 466 } else { 467 goto loop; /* ignore unimplemented options */ 468 } 469 } 470 /* if rehash, don't redo absolute path names */ 471 if (fullname[0] == '/' && index <= prev) { 472 if (index < prev) 473 goto loop; 474 TRACE(("searchexec \"%s\": no change\n", name)); 475 goto success; 476 } 477 while (stat(fullname, &statb) < 0) { 478 #ifdef SYSV 479 if (errno == EINTR) 480 continue; 481 #endif 482 if (errno != ENOENT && errno != ENOTDIR) 483 e = errno; 484 goto loop; 485 } 486 e = EACCES; /* if we fail, this will be the error */ 487 if (!S_ISREG(statb.st_mode)) 488 goto loop; 489 if (pathopt) { /* this is a %func directory */ 490 stalloc(strlen(fullname) + 1); 491 readcmdfile(fullname); 492 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 493 error("%s not defined in %s", name, fullname); 494 stunalloc(fullname); 495 goto success; 496 } 497 #ifdef notdef 498 if (statb.st_uid == geteuid()) { 499 if ((statb.st_mode & 0100) == 0) 500 goto loop; 501 } else if (statb.st_gid == getegid()) { 502 if ((statb.st_mode & 010) == 0) 503 goto loop; 504 } else { 505 if ((statb.st_mode & 01) == 0) 506 goto loop; 507 } 508 #endif 509 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 510 INTOFF; 511 cmdp = cmdlookup(name, 1); 512 cmdp->cmdtype = CMDNORMAL; 513 cmdp->param.index = index; 514 INTON; 515 goto success; 516 } 517 518 /* We failed. If there was an entry for this command, delete it */ 519 if (cmdp) 520 delete_cmd_entry(); 521 if (printerr) 522 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 523 entry->cmdtype = CMDUNKNOWN; 524 return; 525 526 success: 527 cmdp->rehash = 0; 528 entry->cmdtype = cmdp->cmdtype; 529 entry->u = cmdp->param; 530 } 531 532 533 534 /* 535 * Search the table of builtin commands. 536 */ 537 538 int 539 find_builtin(char *name) 540 { 541 const struct builtincmd *bp; 542 543 for (bp = builtincmd ; bp->name ; bp++) { 544 if (*bp->name == *name && equal(bp->name, name)) 545 return bp->code; 546 } 547 return -1; 548 } 549 550 551 552 /* 553 * Called when a cd is done. Marks all commands so the next time they 554 * are executed they will be rehashed. 555 */ 556 557 void 558 hashcd(void) 559 { 560 struct tblentry **pp; 561 struct tblentry *cmdp; 562 563 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 564 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 565 if (cmdp->cmdtype == CMDNORMAL 566 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 567 cmdp->rehash = 1; 568 } 569 } 570 } 571 572 573 574 /* 575 * Called before PATH is changed. The argument is the new value of PATH; 576 * pathval() still returns the old value at this point. Called with 577 * interrupts off. 578 */ 579 580 void 581 changepath(const char *newval) 582 { 583 const char *old, *new; 584 int index; 585 int firstchange; 586 int bltin; 587 588 old = pathval(); 589 new = newval; 590 firstchange = 9999; /* assume no change */ 591 index = 0; 592 bltin = -1; 593 for (;;) { 594 if (*old != *new) { 595 firstchange = index; 596 if ((*old == '\0' && *new == ':') 597 || (*old == ':' && *new == '\0')) 598 firstchange++; 599 old = new; /* ignore subsequent differences */ 600 } 601 if (*new == '\0') 602 break; 603 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 604 bltin = index; 605 if (*new == ':') { 606 index++; 607 } 608 new++, old++; 609 } 610 if (builtinloc < 0 && bltin >= 0) 611 builtinloc = bltin; /* zap builtins */ 612 if (builtinloc >= 0 && bltin < 0) 613 firstchange = 0; 614 clearcmdentry(firstchange); 615 builtinloc = bltin; 616 } 617 618 619 /* 620 * Clear out command entries. The argument specifies the first entry in 621 * PATH which has changed. 622 */ 623 624 void 625 clearcmdentry(int firstchange) 626 { 627 struct tblentry **tblp; 628 struct tblentry **pp; 629 struct tblentry *cmdp; 630 631 INTOFF; 632 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 633 pp = tblp; 634 while ((cmdp = *pp) != NULL) { 635 if ((cmdp->cmdtype == CMDNORMAL && 636 cmdp->param.index >= firstchange) 637 || (cmdp->cmdtype == CMDBUILTIN && 638 builtinloc >= firstchange)) { 639 *pp = cmdp->next; 640 ckfree(cmdp); 641 } else { 642 pp = &cmdp->next; 643 } 644 } 645 } 646 INTON; 647 } 648 649 650 /* 651 * Delete all functions. 652 */ 653 654 #ifdef mkinit 655 MKINIT void deletefuncs(); 656 657 SHELLPROC { 658 deletefuncs(); 659 } 660 #endif 661 662 void 663 deletefuncs(void) 664 { 665 struct tblentry **tblp; 666 struct tblentry **pp; 667 struct tblentry *cmdp; 668 669 INTOFF; 670 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 671 pp = tblp; 672 while ((cmdp = *pp) != NULL) { 673 if (cmdp->cmdtype == CMDFUNCTION) { 674 *pp = cmdp->next; 675 freefunc(cmdp->param.func); 676 ckfree(cmdp); 677 } else { 678 pp = &cmdp->next; 679 } 680 } 681 } 682 INTON; 683 } 684 685 686 687 /* 688 * Locate a command in the command hash table. If "add" is nonzero, 689 * add the command to the table if it is not already present. The 690 * variable "lastcmdentry" is set to point to the address of the link 691 * pointing to the entry, so that delete_cmd_entry can delete the 692 * entry. 693 */ 694 695 struct tblentry **lastcmdentry; 696 697 698 STATIC struct tblentry * 699 cmdlookup(char *name, int add) 700 { 701 int hashval; 702 char *p; 703 struct tblentry *cmdp; 704 struct tblentry **pp; 705 706 p = name; 707 hashval = *p << 4; 708 while (*p) 709 hashval += *p++; 710 hashval &= 0x7FFF; 711 pp = &cmdtable[hashval % CMDTABLESIZE]; 712 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 713 if (equal(cmdp->cmdname, name)) 714 break; 715 pp = &cmdp->next; 716 } 717 if (add && cmdp == NULL) { 718 INTOFF; 719 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 720 + strlen(name) + 1); 721 cmdp->next = NULL; 722 cmdp->cmdtype = CMDUNKNOWN; 723 cmdp->rehash = 0; 724 strcpy(cmdp->cmdname, name); 725 INTON; 726 } 727 lastcmdentry = pp; 728 return cmdp; 729 } 730 731 /* 732 * Delete the command entry returned on the last lookup. 733 */ 734 735 STATIC void 736 delete_cmd_entry(void) 737 { 738 struct tblentry *cmdp; 739 740 INTOFF; 741 cmdp = *lastcmdentry; 742 *lastcmdentry = cmdp->next; 743 ckfree(cmdp); 744 INTON; 745 } 746 747 748 749 /* 750 * Add a new command entry, replacing any existing command entry for 751 * the same name. 752 */ 753 754 void 755 addcmdentry(char *name, struct cmdentry *entry) 756 { 757 struct tblentry *cmdp; 758 759 INTOFF; 760 cmdp = cmdlookup(name, 1); 761 if (cmdp->cmdtype == CMDFUNCTION) { 762 freefunc(cmdp->param.func); 763 } 764 cmdp->cmdtype = entry->cmdtype; 765 cmdp->param = entry->u; 766 INTON; 767 } 768 769 770 /* 771 * Define a shell function. 772 */ 773 774 void 775 defun(char *name, union node *func) 776 { 777 struct cmdentry entry; 778 779 INTOFF; 780 entry.cmdtype = CMDFUNCTION; 781 entry.u.func = copyfunc(func); 782 addcmdentry(name, &entry); 783 INTON; 784 } 785 786 787 /* 788 * Delete a function if it exists. 789 */ 790 791 int 792 unsetfunc(char *name) 793 { 794 struct tblentry *cmdp; 795 796 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 797 freefunc(cmdp->param.func); 798 delete_cmd_entry(); 799 return (0); 800 } 801 return (1); 802 } 803 804 /* 805 * Locate and print what a word is... 806 */ 807 808 int 809 typecmd(int argc, char **argv) 810 { 811 struct cmdentry entry; 812 struct tblentry *cmdp; 813 char **pp; 814 struct alias *ap; 815 int i; 816 int error = 0; 817 extern char *const parsekwd[]; 818 819 for (i = 1; i < argc; i++) { 820 out1str(argv[i]); 821 /* First look at the keywords */ 822 for (pp = (char **)parsekwd; *pp; pp++) 823 if (**pp == *argv[i] && equal(*pp, argv[i])) 824 break; 825 826 if (*pp) { 827 out1str(" is a shell keyword\n"); 828 continue; 829 } 830 831 /* Then look at the aliases */ 832 if ((ap = lookupalias(argv[i], 1)) != NULL) { 833 out1fmt(" is an alias for %s\n", ap->val); 834 continue; 835 } 836 837 /* Then check if it is a tracked alias */ 838 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 839 entry.cmdtype = cmdp->cmdtype; 840 entry.u = cmdp->param; 841 } 842 else { 843 /* Finally use brute force */ 844 find_command(argv[i], &entry, 0, pathval()); 845 } 846 847 switch (entry.cmdtype) { 848 case CMDNORMAL: { 849 if (strchr(argv[i], '/') == NULL) { 850 char *path = pathval(), *name; 851 int j = entry.u.index; 852 do { 853 name = padvance(&path, argv[i]); 854 stunalloc(name); 855 } while (--j >= 0); 856 out1fmt(" is%s %s\n", 857 cmdp ? " a tracked alias for" : "", name); 858 } else { 859 if (access(argv[i], X_OK) == 0) 860 out1fmt(" is %s\n", argv[i]); 861 else 862 out1fmt(": %s\n", strerror(errno)); 863 } 864 break; 865 } 866 case CMDFUNCTION: 867 out1str(" is a shell function\n"); 868 break; 869 870 case CMDBUILTIN: 871 out1str(" is a shell builtin\n"); 872 break; 873 874 default: 875 out1str(": not found\n"); 876 error |= 127; 877 break; 878 } 879 } 880 return error; 881 } 882