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