1 /* $NetBSD: exec.c,v 1.24 1997/07/20 21:27:36 christos 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.24 1997/07/20 21:27:36 christos 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, index) 121 char **argv, **envp; 122 char *path; 123 int index; 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 (--index < 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 } 157 158 159 STATIC void 160 tryexec(cmd, argv, envp) 161 char *cmd; 162 char **argv; 163 char **envp; 164 { 165 int e; 166 #ifndef BSD 167 char *p; 168 #endif 169 170 #ifdef SYSV 171 do { 172 execve(cmd, argv, envp); 173 } while (errno == EINTR); 174 #else 175 execve(cmd, argv, envp); 176 #endif 177 e = errno; 178 if (e == ENOEXEC) { 179 initshellproc(); 180 setinputfile(cmd, 0); 181 commandname = arg0 = savestr(argv[0]); 182 #ifndef BSD 183 pgetc(); pungetc(); /* fill up input buffer */ 184 p = parsenextc; 185 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 186 argv[0] = cmd; 187 execinterp(argv, envp); 188 } 189 #endif 190 setparam(argv + 1); 191 exraise(EXSHELLPROC); 192 /*NOTREACHED*/ 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 } 273 #endif 274 275 276 277 /* 278 * Do a path search. The variable path (passed by reference) should be 279 * set to the start of the path before the first call; padvance will update 280 * this value as it proceeds. Successive calls to padvance will return 281 * the possible path expansions in sequence. If an option (indicated by 282 * a percent sign) appears in the path entry then the global variable 283 * pathopt will be set to point to it; otherwise pathopt will be set to 284 * NULL. 285 */ 286 287 char *pathopt; 288 289 char * 290 padvance(path, name) 291 char **path; 292 char *name; 293 { 294 char *p, *q; 295 char *start; 296 int len; 297 298 if (*path == NULL) 299 return NULL; 300 start = *path; 301 for (p = start ; *p && *p != ':' && *p != '%' ; p++); 302 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 303 while (stackblocksize() < len) 304 growstackblock(); 305 q = stackblock(); 306 if (p != start) { 307 memcpy(q, start, p - start); 308 q += p - start; 309 *q++ = '/'; 310 } 311 strcpy(q, name); 312 pathopt = NULL; 313 if (*p == '%') { 314 pathopt = ++p; 315 while (*p && *p != ':') p++; 316 } 317 if (*p == ':') 318 *path = p + 1; 319 else 320 *path = NULL; 321 return stalloc(len); 322 } 323 324 325 326 /*** Command hashing code ***/ 327 328 329 int 330 hashcmd(argc, argv) 331 int argc; 332 char **argv; 333 { 334 struct tblentry **pp; 335 struct tblentry *cmdp; 336 int c; 337 int verbose; 338 struct cmdentry entry; 339 char *name; 340 341 verbose = 0; 342 while ((c = nextopt("rv")) != '\0') { 343 if (c == 'r') { 344 clearcmdentry(0); 345 } else if (c == 'v') { 346 verbose++; 347 } 348 } 349 if (*argptr == NULL) { 350 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 351 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 352 printentry(cmdp, verbose); 353 } 354 } 355 return 0; 356 } 357 while ((name = *argptr) != NULL) { 358 if ((cmdp = cmdlookup(name, 0)) != NULL 359 && (cmdp->cmdtype == CMDNORMAL 360 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) 361 delete_cmd_entry(); 362 find_command(name, &entry, DO_ERR, pathval()); 363 if (verbose) { 364 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 365 cmdp = cmdlookup(name, 0); 366 printentry(cmdp, verbose); 367 } 368 flushall(); 369 } 370 argptr++; 371 } 372 return 0; 373 } 374 375 376 STATIC void 377 printentry(cmdp, verbose) 378 struct tblentry *cmdp; 379 int verbose; 380 { 381 int index; 382 char *path; 383 char *name; 384 385 if (cmdp->cmdtype == CMDNORMAL) { 386 index = cmdp->param.index; 387 path = pathval(); 388 do { 389 name = padvance(&path, cmdp->cmdname); 390 stunalloc(name); 391 } while (--index >= 0); 392 out1str(name); 393 } else if (cmdp->cmdtype == CMDBUILTIN) { 394 out1fmt("builtin %s", cmdp->cmdname); 395 } else if (cmdp->cmdtype == CMDFUNCTION) { 396 out1fmt("function %s", cmdp->cmdname); 397 if (verbose) { 398 INTOFF; 399 name = commandtext(cmdp->param.func); 400 out1c(' '); 401 out1str(name); 402 ckfree(name); 403 INTON; 404 } 405 #ifdef DEBUG 406 } else { 407 error("internal error: cmdtype %d", cmdp->cmdtype); 408 #endif 409 } 410 if (cmdp->rehash) 411 out1c('*'); 412 out1c('\n'); 413 } 414 415 416 417 /* 418 * Resolve a command name. If you change this routine, you may have to 419 * change the shellexec routine as well. 420 */ 421 422 void 423 find_command(name, entry, act, path) 424 char *name; 425 struct cmdentry *entry; 426 int act; 427 char *path; 428 { 429 struct tblentry *cmdp; 430 int index; 431 int prev; 432 char *fullname; 433 struct stat statb; 434 int e; 435 int i; 436 437 /* If name contains a slash, don't use the hash table */ 438 if (strchr(name, '/') != NULL) { 439 if (act & DO_ABS) { 440 while (stat(name, &statb) < 0) { 441 #ifdef SYSV 442 if (errno == EINTR) 443 continue; 444 #endif 445 if (errno != ENOENT && errno != ENOTDIR) 446 e = errno; 447 entry->cmdtype = CMDUNKNOWN; 448 entry->u.index = -1; 449 return; 450 } 451 entry->cmdtype = CMDNORMAL; 452 entry->u.index = -1; 453 return; 454 } 455 entry->cmdtype = CMDNORMAL; 456 entry->u.index = 0; 457 return; 458 } 459 460 /* If name is in the table, and not invalidated by cd, we're done */ 461 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 462 goto success; 463 464 /* If %builtin not in path, check for builtin next */ 465 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 466 INTOFF; 467 cmdp = cmdlookup(name, 1); 468 cmdp->cmdtype = CMDBUILTIN; 469 cmdp->param.index = i; 470 INTON; 471 goto success; 472 } 473 474 /* We have to search path. */ 475 prev = -1; /* where to start */ 476 if (cmdp) { /* doing a rehash */ 477 if (cmdp->cmdtype == CMDBUILTIN) 478 prev = builtinloc; 479 else 480 prev = cmdp->param.index; 481 } 482 483 e = ENOENT; 484 index = -1; 485 loop: 486 while ((fullname = padvance(&path, name)) != NULL) { 487 stunalloc(fullname); 488 index++; 489 if (pathopt) { 490 if (prefix("builtin", pathopt)) { 491 if ((i = find_builtin(name)) < 0) 492 goto loop; 493 INTOFF; 494 cmdp = cmdlookup(name, 1); 495 cmdp->cmdtype = CMDBUILTIN; 496 cmdp->param.index = i; 497 INTON; 498 goto success; 499 } else if (prefix("func", pathopt)) { 500 /* handled below */ 501 } else { 502 goto loop; /* ignore unimplemented options */ 503 } 504 } 505 /* if rehash, don't redo absolute path names */ 506 if (fullname[0] == '/' && index <= prev) { 507 if (index < prev) 508 goto loop; 509 TRACE(("searchexec \"%s\": no change\n", name)); 510 goto success; 511 } 512 while (stat(fullname, &statb) < 0) { 513 #ifdef SYSV 514 if (errno == EINTR) 515 continue; 516 #endif 517 if (errno != ENOENT && errno != ENOTDIR) 518 e = errno; 519 goto loop; 520 } 521 e = EACCES; /* if we fail, this will be the error */ 522 if (!S_ISREG(statb.st_mode)) 523 goto loop; 524 if (pathopt) { /* this is a %func directory */ 525 stalloc(strlen(fullname) + 1); 526 readcmdfile(fullname); 527 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 528 error("%s not defined in %s", name, fullname); 529 stunalloc(fullname); 530 goto success; 531 } 532 #ifdef notdef 533 if (statb.st_uid == geteuid()) { 534 if ((statb.st_mode & 0100) == 0) 535 goto loop; 536 } else if (statb.st_gid == getegid()) { 537 if ((statb.st_mode & 010) == 0) 538 goto loop; 539 } else { 540 if ((statb.st_mode & 01) == 0) 541 goto loop; 542 } 543 #endif 544 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 545 INTOFF; 546 cmdp = cmdlookup(name, 1); 547 cmdp->cmdtype = CMDNORMAL; 548 cmdp->param.index = index; 549 INTON; 550 goto success; 551 } 552 553 /* We failed. If there was an entry for this command, delete it */ 554 if (cmdp) 555 delete_cmd_entry(); 556 if (act & DO_ERR) 557 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 558 entry->cmdtype = CMDUNKNOWN; 559 return; 560 561 success: 562 cmdp->rehash = 0; 563 entry->cmdtype = cmdp->cmdtype; 564 entry->u = cmdp->param; 565 } 566 567 568 569 /* 570 * Search the table of builtin commands. 571 */ 572 573 int 574 find_builtin(name) 575 char *name; 576 { 577 const struct builtincmd *bp; 578 579 for (bp = builtincmd ; bp->name ; bp++) { 580 if (*bp->name == *name && equal(bp->name, name)) 581 return bp->code; 582 } 583 return -1; 584 } 585 586 587 588 /* 589 * Called when a cd is done. Marks all commands so the next time they 590 * are executed they will be rehashed. 591 */ 592 593 void 594 hashcd() { 595 struct tblentry **pp; 596 struct tblentry *cmdp; 597 598 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 599 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 600 if (cmdp->cmdtype == CMDNORMAL 601 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 602 cmdp->rehash = 1; 603 } 604 } 605 } 606 607 608 609 /* 610 * Called before PATH is changed. The argument is the new value of PATH; 611 * pathval() still returns the old value at this point. Called with 612 * interrupts off. 613 */ 614 615 void 616 changepath(newval) 617 const char *newval; 618 { 619 const char *old, *new; 620 int index; 621 int firstchange; 622 int bltin; 623 624 old = pathval(); 625 new = newval; 626 firstchange = 9999; /* assume no change */ 627 index = 0; 628 bltin = -1; 629 for (;;) { 630 if (*old != *new) { 631 firstchange = index; 632 if ((*old == '\0' && *new == ':') 633 || (*old == ':' && *new == '\0')) 634 firstchange++; 635 old = new; /* ignore subsequent differences */ 636 } 637 if (*new == '\0') 638 break; 639 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 640 bltin = index; 641 if (*new == ':') { 642 index++; 643 } 644 new++, old++; 645 } 646 if (builtinloc < 0 && bltin >= 0) 647 builtinloc = bltin; /* zap builtins */ 648 if (builtinloc >= 0 && bltin < 0) 649 firstchange = 0; 650 clearcmdentry(firstchange); 651 builtinloc = bltin; 652 } 653 654 655 /* 656 * Clear out command entries. The argument specifies the first entry in 657 * PATH which has changed. 658 */ 659 660 STATIC void 661 clearcmdentry(firstchange) 662 int firstchange; 663 { 664 struct tblentry **tblp; 665 struct tblentry **pp; 666 struct tblentry *cmdp; 667 668 INTOFF; 669 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 670 pp = tblp; 671 while ((cmdp = *pp) != NULL) { 672 if ((cmdp->cmdtype == CMDNORMAL && 673 cmdp->param.index >= firstchange) 674 || (cmdp->cmdtype == CMDBUILTIN && 675 builtinloc >= firstchange)) { 676 *pp = cmdp->next; 677 ckfree(cmdp); 678 } else { 679 pp = &cmdp->next; 680 } 681 } 682 } 683 INTON; 684 } 685 686 687 /* 688 * Delete all functions. 689 */ 690 691 #ifdef mkinit 692 MKINIT void deletefuncs __P((void)); 693 694 SHELLPROC { 695 deletefuncs(); 696 } 697 #endif 698 699 void 700 deletefuncs() { 701 struct tblentry **tblp; 702 struct tblentry **pp; 703 struct tblentry *cmdp; 704 705 INTOFF; 706 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 707 pp = tblp; 708 while ((cmdp = *pp) != NULL) { 709 if (cmdp->cmdtype == CMDFUNCTION) { 710 *pp = cmdp->next; 711 freefunc(cmdp->param.func); 712 ckfree(cmdp); 713 } else { 714 pp = &cmdp->next; 715 } 716 } 717 } 718 INTON; 719 } 720 721 722 723 /* 724 * Locate a command in the command hash table. If "add" is nonzero, 725 * add the command to the table if it is not already present. The 726 * variable "lastcmdentry" is set to point to the address of the link 727 * pointing to the entry, so that delete_cmd_entry can delete the 728 * entry. 729 */ 730 731 struct tblentry **lastcmdentry; 732 733 734 STATIC struct tblentry * 735 cmdlookup(name, add) 736 char *name; 737 int add; 738 { 739 int hashval; 740 char *p; 741 struct tblentry *cmdp; 742 struct tblentry **pp; 743 744 p = name; 745 hashval = *p << 4; 746 while (*p) 747 hashval += *p++; 748 hashval &= 0x7FFF; 749 pp = &cmdtable[hashval % CMDTABLESIZE]; 750 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 751 if (equal(cmdp->cmdname, name)) 752 break; 753 pp = &cmdp->next; 754 } 755 if (add && cmdp == NULL) { 756 INTOFF; 757 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 758 + strlen(name) + 1); 759 cmdp->next = NULL; 760 cmdp->cmdtype = CMDUNKNOWN; 761 cmdp->rehash = 0; 762 strcpy(cmdp->cmdname, name); 763 INTON; 764 } 765 lastcmdentry = pp; 766 return cmdp; 767 } 768 769 /* 770 * Delete the command entry returned on the last lookup. 771 */ 772 773 STATIC void 774 delete_cmd_entry() { 775 struct tblentry *cmdp; 776 777 INTOFF; 778 cmdp = *lastcmdentry; 779 *lastcmdentry = cmdp->next; 780 ckfree(cmdp); 781 INTON; 782 } 783 784 785 786 #ifdef notdef 787 void 788 getcmdentry(name, entry) 789 char *name; 790 struct cmdentry *entry; 791 { 792 struct tblentry *cmdp = cmdlookup(name, 0); 793 794 if (cmdp) { 795 entry->u = cmdp->param; 796 entry->cmdtype = cmdp->cmdtype; 797 } else { 798 entry->cmdtype = CMDUNKNOWN; 799 entry->u.index = 0; 800 } 801 } 802 #endif 803 804 805 /* 806 * Add a new command entry, replacing any existing command entry for 807 * the same name. 808 */ 809 810 void 811 addcmdentry(name, entry) 812 char *name; 813 struct cmdentry *entry; 814 { 815 struct tblentry *cmdp; 816 817 INTOFF; 818 cmdp = cmdlookup(name, 1); 819 if (cmdp->cmdtype == CMDFUNCTION) { 820 freefunc(cmdp->param.func); 821 } 822 cmdp->cmdtype = entry->cmdtype; 823 cmdp->param = entry->u; 824 INTON; 825 } 826 827 828 /* 829 * Define a shell function. 830 */ 831 832 void 833 defun(name, func) 834 char *name; 835 union node *func; 836 { 837 struct cmdentry entry; 838 839 INTOFF; 840 entry.cmdtype = CMDFUNCTION; 841 entry.u.func = copyfunc(func); 842 addcmdentry(name, &entry); 843 INTON; 844 } 845 846 847 /* 848 * Delete a function if it exists. 849 */ 850 851 int 852 unsetfunc(name) 853 char *name; 854 { 855 struct tblentry *cmdp; 856 857 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 858 freefunc(cmdp->param.func); 859 delete_cmd_entry(); 860 return (0); 861 } 862 return (1); 863 } 864 865 /* 866 * Locate and print what a word is... 867 */ 868 869 int 870 typecmd(argc, argv) 871 int argc; 872 char **argv; 873 { 874 struct cmdentry entry; 875 struct tblentry *cmdp; 876 char **pp; 877 struct alias *ap; 878 int i; 879 int error = 0; 880 extern char *const parsekwd[]; 881 882 for (i = 1; i < argc; i++) { 883 out1str(argv[i]); 884 /* First look at the keywords */ 885 for (pp = (char **)parsekwd; *pp; pp++) 886 if (**pp == *argv[i] && equal(*pp, argv[i])) 887 break; 888 889 if (*pp) { 890 out1str(" is a shell keyword\n"); 891 continue; 892 } 893 894 /* Then look at the aliases */ 895 if ((ap = lookupalias(argv[i], 1)) != NULL) { 896 out1fmt(" is an alias for %s\n", ap->val); 897 continue; 898 } 899 900 /* Then check if it is a tracked alias */ 901 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 902 entry.cmdtype = cmdp->cmdtype; 903 entry.u = cmdp->param; 904 } 905 else { 906 /* Finally use brute force */ 907 find_command(argv[i], &entry, DO_ABS, pathval()); 908 } 909 910 switch (entry.cmdtype) { 911 case CMDNORMAL: { 912 int j = entry.u.index; 913 char *path = pathval(), *name; 914 if (j == -1) 915 name = argv[i]; 916 else { 917 do { 918 name = padvance(&path, argv[i]); 919 stunalloc(name); 920 } while (--j >= 0); 921 } 922 out1fmt(" is%s %s\n", 923 cmdp ? " a tracked alias for" : "", name); 924 break; 925 } 926 case CMDFUNCTION: 927 out1str(" is a shell function\n"); 928 break; 929 930 case CMDBUILTIN: 931 out1str(" is a shell builtin\n"); 932 break; 933 934 default: 935 out1str(" not found\n"); 936 error |= 127; 937 break; 938 } 939 } 940 return error; 941 } 942