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 if (cmdp->cmdtype == CMDNORMAL) 340 printentry(cmdp, verbose); 341 } 342 } 343 return 0; 344 } 345 while ((name = *argptr) != NULL) { 346 if ((cmdp = cmdlookup(name, 0)) != NULL 347 && (cmdp->cmdtype == CMDNORMAL 348 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) 349 delete_cmd_entry(); 350 find_command(name, &entry, 1, pathval()); 351 if (verbose) { 352 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 353 cmdp = cmdlookup(name, 0); 354 if (cmdp != NULL) 355 printentry(cmdp, verbose); 356 else 357 outfmt(&errout, "%s: not found\n", name); 358 } 359 flushall(); 360 } 361 argptr++; 362 } 363 return 0; 364 } 365 366 367 STATIC void 368 printentry(struct tblentry *cmdp, int verbose) 369 { 370 int index; 371 char *path; 372 char *name; 373 374 if (cmdp->cmdtype == CMDNORMAL) { 375 index = cmdp->param.index; 376 path = pathval(); 377 do { 378 name = padvance(&path, cmdp->cmdname); 379 stunalloc(name); 380 } while (--index >= 0); 381 out1str(name); 382 } else if (cmdp->cmdtype == CMDBUILTIN) { 383 out1fmt("builtin %s", cmdp->cmdname); 384 } else if (cmdp->cmdtype == CMDFUNCTION) { 385 out1fmt("function %s", cmdp->cmdname); 386 if (verbose) { 387 INTOFF; 388 name = commandtext(cmdp->param.func); 389 out1c(' '); 390 out1str(name); 391 ckfree(name); 392 INTON; 393 } 394 #ifdef DEBUG 395 } else { 396 error("internal error: cmdtype %d", cmdp->cmdtype); 397 #endif 398 } 399 if (cmdp->rehash) 400 out1c('*'); 401 out1c('\n'); 402 } 403 404 405 406 /* 407 * Resolve a command name. If you change this routine, you may have to 408 * change the shellexec routine as well. 409 */ 410 411 void 412 find_command(char *name, struct cmdentry *entry, int printerr, char *path) 413 { 414 struct tblentry *cmdp; 415 int index; 416 int prev; 417 char *fullname; 418 struct stat statb; 419 int e; 420 int i; 421 422 /* If name contains a slash, don't use the hash table */ 423 if (strchr(name, '/') != NULL) { 424 entry->cmdtype = CMDNORMAL; 425 entry->u.index = 0; 426 return; 427 } 428 429 /* If name is in the table, and not invalidated by cd, we're done */ 430 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 431 goto success; 432 433 /* If %builtin not in path, check for builtin next */ 434 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 435 INTOFF; 436 cmdp = cmdlookup(name, 1); 437 cmdp->cmdtype = CMDBUILTIN; 438 cmdp->param.index = i; 439 INTON; 440 goto success; 441 } 442 443 /* We have to search path. */ 444 prev = -1; /* where to start */ 445 if (cmdp) { /* doing a rehash */ 446 if (cmdp->cmdtype == CMDBUILTIN) 447 prev = builtinloc; 448 else 449 prev = cmdp->param.index; 450 } 451 452 e = ENOENT; 453 index = -1; 454 loop: 455 while ((fullname = padvance(&path, name)) != NULL) { 456 stunalloc(fullname); 457 index++; 458 if (pathopt) { 459 if (prefix("builtin", pathopt)) { 460 if ((i = find_builtin(name)) < 0) 461 goto loop; 462 INTOFF; 463 cmdp = cmdlookup(name, 1); 464 cmdp->cmdtype = CMDBUILTIN; 465 cmdp->param.index = i; 466 INTON; 467 goto success; 468 } else if (prefix("func", pathopt)) { 469 /* handled below */ 470 } else { 471 goto loop; /* ignore unimplemented options */ 472 } 473 } 474 /* if rehash, don't redo absolute path names */ 475 if (fullname[0] == '/' && index <= prev) { 476 if (index < prev) 477 goto loop; 478 TRACE(("searchexec \"%s\": no change\n", name)); 479 goto success; 480 } 481 while (stat(fullname, &statb) < 0) { 482 #ifdef SYSV 483 if (errno == EINTR) 484 continue; 485 #endif 486 if (errno != ENOENT && errno != ENOTDIR) 487 e = errno; 488 goto loop; 489 } 490 e = EACCES; /* if we fail, this will be the error */ 491 if (!S_ISREG(statb.st_mode)) 492 goto loop; 493 if (pathopt) { /* this is a %func directory */ 494 stalloc(strlen(fullname) + 1); 495 readcmdfile(fullname); 496 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 497 error("%s not defined in %s", name, fullname); 498 stunalloc(fullname); 499 goto success; 500 } 501 #ifdef notdef 502 if (statb.st_uid == geteuid()) { 503 if ((statb.st_mode & 0100) == 0) 504 goto loop; 505 } else if (statb.st_gid == getegid()) { 506 if ((statb.st_mode & 010) == 0) 507 goto loop; 508 } else { 509 if ((statb.st_mode & 01) == 0) 510 goto loop; 511 } 512 #endif 513 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 514 INTOFF; 515 cmdp = cmdlookup(name, 1); 516 cmdp->cmdtype = CMDNORMAL; 517 cmdp->param.index = index; 518 INTON; 519 goto success; 520 } 521 522 /* We failed. If there was an entry for this command, delete it */ 523 if (cmdp) 524 delete_cmd_entry(); 525 if (printerr) 526 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 527 entry->cmdtype = CMDUNKNOWN; 528 return; 529 530 success: 531 cmdp->rehash = 0; 532 entry->cmdtype = cmdp->cmdtype; 533 entry->u = cmdp->param; 534 } 535 536 537 538 /* 539 * Search the table of builtin commands. 540 */ 541 542 int 543 find_builtin(char *name) 544 { 545 const struct builtincmd *bp; 546 547 for (bp = builtincmd ; bp->name ; bp++) { 548 if (*bp->name == *name && equal(bp->name, name)) 549 return bp->code; 550 } 551 return -1; 552 } 553 554 555 556 /* 557 * Called when a cd is done. Marks all commands so the next time they 558 * are executed they will be rehashed. 559 */ 560 561 void 562 hashcd(void) 563 { 564 struct tblentry **pp; 565 struct tblentry *cmdp; 566 567 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 568 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 569 if (cmdp->cmdtype == CMDNORMAL 570 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 571 cmdp->rehash = 1; 572 } 573 } 574 } 575 576 577 578 /* 579 * Called before PATH is changed. The argument is the new value of PATH; 580 * pathval() still returns the old value at this point. Called with 581 * interrupts off. 582 */ 583 584 void 585 changepath(const char *newval) 586 { 587 const char *old, *new; 588 int index; 589 int firstchange; 590 int bltin; 591 592 old = pathval(); 593 new = newval; 594 firstchange = 9999; /* assume no change */ 595 index = 0; 596 bltin = -1; 597 for (;;) { 598 if (*old != *new) { 599 firstchange = index; 600 if ((*old == '\0' && *new == ':') 601 || (*old == ':' && *new == '\0')) 602 firstchange++; 603 old = new; /* ignore subsequent differences */ 604 } 605 if (*new == '\0') 606 break; 607 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 608 bltin = index; 609 if (*new == ':') { 610 index++; 611 } 612 new++, old++; 613 } 614 if (builtinloc < 0 && bltin >= 0) 615 builtinloc = bltin; /* zap builtins */ 616 if (builtinloc >= 0 && bltin < 0) 617 firstchange = 0; 618 clearcmdentry(firstchange); 619 builtinloc = bltin; 620 } 621 622 623 /* 624 * Clear out command entries. The argument specifies the first entry in 625 * PATH which has changed. 626 */ 627 628 void 629 clearcmdentry(int firstchange) 630 { 631 struct tblentry **tblp; 632 struct tblentry **pp; 633 struct tblentry *cmdp; 634 635 INTOFF; 636 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 637 pp = tblp; 638 while ((cmdp = *pp) != NULL) { 639 if ((cmdp->cmdtype == CMDNORMAL && 640 cmdp->param.index >= firstchange) 641 || (cmdp->cmdtype == CMDBUILTIN && 642 builtinloc >= firstchange)) { 643 *pp = cmdp->next; 644 ckfree(cmdp); 645 } else { 646 pp = &cmdp->next; 647 } 648 } 649 } 650 INTON; 651 } 652 653 654 /* 655 * Delete all functions. 656 */ 657 658 #ifdef mkinit 659 MKINIT void deletefuncs(); 660 661 SHELLPROC { 662 deletefuncs(); 663 } 664 #endif 665 666 void 667 deletefuncs(void) 668 { 669 struct tblentry **tblp; 670 struct tblentry **pp; 671 struct tblentry *cmdp; 672 673 INTOFF; 674 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 675 pp = tblp; 676 while ((cmdp = *pp) != NULL) { 677 if (cmdp->cmdtype == CMDFUNCTION) { 678 *pp = cmdp->next; 679 freefunc(cmdp->param.func); 680 ckfree(cmdp); 681 } else { 682 pp = &cmdp->next; 683 } 684 } 685 } 686 INTON; 687 } 688 689 690 691 /* 692 * Locate a command in the command hash table. If "add" is nonzero, 693 * add the command to the table if it is not already present. The 694 * variable "lastcmdentry" is set to point to the address of the link 695 * pointing to the entry, so that delete_cmd_entry can delete the 696 * entry. 697 */ 698 699 struct tblentry **lastcmdentry; 700 701 702 STATIC struct tblentry * 703 cmdlookup(char *name, int add) 704 { 705 int hashval; 706 char *p; 707 struct tblentry *cmdp; 708 struct tblentry **pp; 709 710 p = name; 711 hashval = *p << 4; 712 while (*p) 713 hashval += *p++; 714 hashval &= 0x7FFF; 715 pp = &cmdtable[hashval % CMDTABLESIZE]; 716 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 717 if (equal(cmdp->cmdname, name)) 718 break; 719 pp = &cmdp->next; 720 } 721 if (add && cmdp == NULL) { 722 INTOFF; 723 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 724 + strlen(name) + 1); 725 cmdp->next = NULL; 726 cmdp->cmdtype = CMDUNKNOWN; 727 cmdp->rehash = 0; 728 strcpy(cmdp->cmdname, name); 729 INTON; 730 } 731 lastcmdentry = pp; 732 return cmdp; 733 } 734 735 /* 736 * Delete the command entry returned on the last lookup. 737 */ 738 739 STATIC void 740 delete_cmd_entry(void) 741 { 742 struct tblentry *cmdp; 743 744 INTOFF; 745 cmdp = *lastcmdentry; 746 *lastcmdentry = cmdp->next; 747 ckfree(cmdp); 748 INTON; 749 } 750 751 752 753 /* 754 * Add a new command entry, replacing any existing command entry for 755 * the same name. 756 */ 757 758 void 759 addcmdentry(char *name, struct cmdentry *entry) 760 { 761 struct tblentry *cmdp; 762 763 INTOFF; 764 cmdp = cmdlookup(name, 1); 765 if (cmdp->cmdtype == CMDFUNCTION) { 766 freefunc(cmdp->param.func); 767 } 768 cmdp->cmdtype = entry->cmdtype; 769 cmdp->param = entry->u; 770 INTON; 771 } 772 773 774 /* 775 * Define a shell function. 776 */ 777 778 void 779 defun(char *name, union node *func) 780 { 781 struct cmdentry entry; 782 783 INTOFF; 784 entry.cmdtype = CMDFUNCTION; 785 entry.u.func = copyfunc(func); 786 addcmdentry(name, &entry); 787 INTON; 788 } 789 790 791 /* 792 * Delete a function if it exists. 793 */ 794 795 int 796 unsetfunc(char *name) 797 { 798 struct tblentry *cmdp; 799 800 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 801 freefunc(cmdp->param.func); 802 delete_cmd_entry(); 803 return (0); 804 } 805 return (1); 806 } 807 808 /* 809 * Locate and print what a word is... 810 */ 811 812 int 813 typecmd(int argc, char **argv) 814 { 815 struct cmdentry entry; 816 struct tblentry *cmdp; 817 char **pp; 818 struct alias *ap; 819 int i; 820 int error = 0; 821 extern char *const parsekwd[]; 822 823 for (i = 1; i < argc; i++) { 824 out1str(argv[i]); 825 /* First look at the keywords */ 826 for (pp = (char **)parsekwd; *pp; pp++) 827 if (**pp == *argv[i] && equal(*pp, argv[i])) 828 break; 829 830 if (*pp) { 831 out1str(" is a shell keyword\n"); 832 continue; 833 } 834 835 /* Then look at the aliases */ 836 if ((ap = lookupalias(argv[i], 1)) != NULL) { 837 out1fmt(" is an alias for %s\n", ap->val); 838 continue; 839 } 840 841 /* Then check if it is a tracked alias */ 842 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 843 entry.cmdtype = cmdp->cmdtype; 844 entry.u = cmdp->param; 845 } 846 else { 847 /* Finally use brute force */ 848 find_command(argv[i], &entry, 0, pathval()); 849 } 850 851 switch (entry.cmdtype) { 852 case CMDNORMAL: { 853 if (strchr(argv[i], '/') == NULL) { 854 char *path = pathval(), *name; 855 int j = entry.u.index; 856 do { 857 name = padvance(&path, argv[i]); 858 stunalloc(name); 859 } while (--j >= 0); 860 out1fmt(" is%s %s\n", 861 cmdp ? " a tracked alias for" : "", name); 862 } else { 863 if (access(argv[i], X_OK) == 0) 864 out1fmt(" is %s\n", argv[i]); 865 else 866 out1fmt(": %s\n", strerror(errno)); 867 } 868 break; 869 } 870 case CMDFUNCTION: 871 out1str(" is a shell function\n"); 872 break; 873 874 case CMDBUILTIN: 875 out1str(" is a shell builtin\n"); 876 break; 877 878 default: 879 out1str(": not found\n"); 880 error |= 127; 881 break; 882 } 883 } 884 return error; 885 } 886