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