exec.c revision 201343
192494Ssobomax/*- 292494Ssobomax * Copyright (c) 1991, 1993 392494Ssobomax * The Regents of the University of California. All rights reserved. 492494Ssobomax * 592494Ssobomax * This code is derived from software contributed to Berkeley by 692494Ssobomax * Kenneth Almquist. 792494Ssobomax * 892494Ssobomax * Redistribution and use in source and binary forms, with or without 992494Ssobomax * modification, are permitted provided that the following conditions 1092494Ssobomax * are met: 1192494Ssobomax * 1. Redistributions of source code must retain the above copyright 1292494Ssobomax * notice, this list of conditions and the following disclaimer. 1392494Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 1492494Ssobomax * notice, this list of conditions and the following disclaimer in the 1592494Ssobomax * documentation and/or other materials provided with the distribution. 1692494Ssobomax * 4. Neither the name of the University nor the names of its contributors 1792494Ssobomax * may be used to endorse or promote products derived from this software 1892494Ssobomax * without specific prior written permission. 1992494Ssobomax * 2092494Ssobomax * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2192494Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2292494Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2392494Ssobomax * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2492494Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2592494Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2692494Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2792494Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2892494Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2992494Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3092494Ssobomax * SUCH DAMAGE. 3192494Ssobomax */ 3292494Ssobomax 3392494Ssobomax#ifndef lint 3492494Ssobomax#if 0 3592494Ssobomaxstatic char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 3692494Ssobomax#endif 3792494Ssobomax#endif /* not lint */ 3892494Ssobomax#include <sys/cdefs.h> 3992494Ssobomax__FBSDID("$FreeBSD: head/bin/sh/exec.c 201343 2009-12-31 16:13:33Z jilles $"); 4092494Ssobomax 4192494Ssobomax#include <sys/types.h> 4292494Ssobomax#include <sys/stat.h> 4392494Ssobomax#include <unistd.h> 4492494Ssobomax#include <fcntl.h> 4592494Ssobomax#include <errno.h> 4692494Ssobomax#include <stdlib.h> 47124572Sjhb 4892494Ssobomax/* 4992494Ssobomax * When commands are first encountered, they are entered in a hash table. 5092494Ssobomax * This ensures that a full path search will not have to be done for them 5192494Ssobomax * on each invocation. 5292494Ssobomax * 5392494Ssobomax * We should investigate converting to a linear search, even though that 5492494Ssobomax * would make the command name "hash" a misnomer. 5592494Ssobomax */ 5692494Ssobomax 5792494Ssobomax#include "shell.h" 5892494Ssobomax#include "main.h" 5992494Ssobomax#include "nodes.h" 6092494Ssobomax#include "parser.h" 6192494Ssobomax#include "redir.h" 6292494Ssobomax#include "eval.h" 6392494Ssobomax#include "exec.h" 6492494Ssobomax#include "builtins.h" 6592494Ssobomax#include "var.h" 6692494Ssobomax#include "options.h" 6792494Ssobomax#include "input.h" 68124571Sjhb#include "output.h" 6992494Ssobomax#include "syntax.h" 70124571Sjhb#include "memalloc.h" 7192494Ssobomax#include "error.h" 7292494Ssobomax#include "init.h" 7392494Ssobomax#include "mystring.h" 7492494Ssobomax#include "show.h" 7592494Ssobomax#include "jobs.h" 7692494Ssobomax#include "alias.h" 77124571Sjhb 78124571Sjhb 7992494Ssobomax#define CMDTABLESIZE 31 /* should be prime */ 8092494Ssobomax#define ARB 1 /* actual size determined at run time */ 8192494Ssobomax 82124572Sjhb 83124572Sjhb 84124572Sjhbstruct tblentry { 85124572Sjhb struct tblentry *next; /* next entry in hash chain */ 86124572Sjhb union param param; /* definition of builtin function */ 87124572Sjhb int special; /* flag for special builtin commands */ 88124572Sjhb short cmdtype; /* index identifying command */ 89124572Sjhb char rehash; /* if set, cd done since entry created */ 90124572Sjhb char cmdname[ARB]; /* name of command */ 91124572Sjhb}; 92124572Sjhb 93124572Sjhb 94124572SjhbSTATIC struct tblentry *cmdtable[CMDTABLESIZE]; 95124572SjhbSTATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 96124572Sjhbint exerrno = 0; /* Last exec error */ 97124572Sjhb 98124572Sjhb 99124572SjhbSTATIC void tryexec(char *, char **, char **); 100124572SjhbSTATIC void printentry(struct tblentry *, int); 101124572SjhbSTATIC struct tblentry *cmdlookup(const char *, int); 102124572SjhbSTATIC void delete_cmd_entry(void); 103124572Sjhb 10492494Ssobomax 10592494Ssobomax 10692494Ssobomax/* 10792494Ssobomax * Exec a program. Never returns. If you change this routine, you may 10892494Ssobomax * have to change the find_command routine as well. 10992494Ssobomax */ 11092494Ssobomax 11192494Ssobomaxvoid 11292494Ssobomaxshellexec(char **argv, char **envp, const char *path, int idx) 11392494Ssobomax{ 11492494Ssobomax char *cmdname; 11592494Ssobomax int e; 11692494Ssobomax 11792494Ssobomax if (strchr(argv[0], '/') != NULL) { 11892494Ssobomax tryexec(argv[0], argv, envp); 11992494Ssobomax e = errno; 12092494Ssobomax } else { 12192494Ssobomax e = ENOENT; 12292494Ssobomax while ((cmdname = padvance(&path, argv[0])) != NULL) { 12392494Ssobomax if (--idx < 0 && pathopt == NULL) { 12492494Ssobomax tryexec(cmdname, argv, envp); 12592494Ssobomax if (errno != ENOENT && errno != ENOTDIR) 12692494Ssobomax e = errno; 12792494Ssobomax } 12892494Ssobomax stunalloc(cmdname); 12992494Ssobomax } 13092494Ssobomax } 13192494Ssobomax 13292494Ssobomax /* Map to POSIX errors */ 13392494Ssobomax switch (e) { 13492494Ssobomax case EACCES: 13592494Ssobomax exerrno = 126; 13692494Ssobomax break; 13792494Ssobomax case ENOENT: 13892494Ssobomax exerrno = 127; 13992494Ssobomax break; 14092494Ssobomax default: 14192494Ssobomax exerrno = 2; 14292494Ssobomax break; 14392494Ssobomax } 14492494Ssobomax if (e == ENOENT || e == ENOTDIR) 14592494Ssobomax exerror(EXEXEC, "%s: not found", argv[0]); 14692494Ssobomax exerror(EXEXEC, "%s: %s", argv[0], strerror(e)); 14792494Ssobomax} 14892494Ssobomax 14992494Ssobomax 15092494SsobomaxSTATIC void 15192494Ssobomaxtryexec(char *cmd, char **argv, char **envp) 15292494Ssobomax{ 15392494Ssobomax int e; 15492494Ssobomax 15592494Ssobomax execve(cmd, argv, envp); 15692494Ssobomax e = errno; 15792494Ssobomax if (e == ENOEXEC) { 15892494Ssobomax initshellproc(); 15992494Ssobomax setinputfile(cmd, 0); 16092494Ssobomax commandname = arg0 = savestr(argv[0]); 16192494Ssobomax setparam(argv + 1); 16292494Ssobomax exraise(EXSHELLPROC); 16392494Ssobomax /*NOTREACHED*/ 16492494Ssobomax } 165124572Sjhb errno = e; 16692494Ssobomax} 16792494Ssobomax 16892494Ssobomax/* 169124572Sjhb * Do a path search. The variable path (passed by reference) should be 170124572Sjhb * set to the start of the path before the first call; padvance will update 171124572Sjhb * this value as it proceeds. Successive calls to padvance will return 172124572Sjhb * the possible path expansions in sequence. If an option (indicated by 173124572Sjhb * a percent sign) appears in the path entry then the global variable 17492494Ssobomax * pathopt will be set to point to it; otherwise pathopt will be set to 17592494Ssobomax * NULL. 17692494Ssobomax */ 17792494Ssobomax 17892494Ssobomaxconst char *pathopt; 17992494Ssobomax 18092494Ssobomaxchar * 18192494Ssobomaxpadvance(const char **path, const char *name) 18292494Ssobomax{ 18392494Ssobomax const char *p, *start; 18492494Ssobomax char *q; 18592494Ssobomax int len; 18692494Ssobomax 18792494Ssobomax if (*path == NULL) 18892494Ssobomax return NULL; 18992494Ssobomax start = *path; 19092494Ssobomax for (p = start; *p && *p != ':' && *p != '%'; p++) 19192494Ssobomax ; /* nothing */ 19292494Ssobomax len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 19392494Ssobomax while (stackblocksize() < len) 19492494Ssobomax growstackblock(); 195146443Scharnier q = stackblock(); 196146443Scharnier if (p != start) { 19792494Ssobomax memcpy(q, start, p - start); 19892494Ssobomax q += p - start; 19992494Ssobomax *q++ = '/'; 20092494Ssobomax } 20192494Ssobomax strcpy(q, name); 20292494Ssobomax pathopt = NULL; 20392494Ssobomax if (*p == '%') { 20492494Ssobomax pathopt = ++p; 20592494Ssobomax while (*p && *p != ':') p++; 20692494Ssobomax } 20792494Ssobomax if (*p == ':') 20892494Ssobomax *path = p + 1; 20992494Ssobomax else 21092494Ssobomax *path = NULL; 211136093Sstefanf return stalloc(len); 21292494Ssobomax} 21392494Ssobomax 21492494Ssobomax 21592494Ssobomax 21692494Ssobomax/*** Command hashing code ***/ 21792494Ssobomax 21892494Ssobomax 21992494Ssobomaxint 22092494Ssobomaxhashcmd(int argc __unused, char **argv __unused) 22192494Ssobomax{ 222124572Sjhb struct tblentry **pp; 223124572Sjhb struct tblentry *cmdp; 22492494Ssobomax int c; 22592494Ssobomax int verbose; 22692494Ssobomax struct cmdentry entry; 22792494Ssobomax char *name; 22892494Ssobomax 22992494Ssobomax verbose = 0; 23092494Ssobomax while ((c = nextopt("rv")) != '\0') { 23192494Ssobomax if (c == 'r') { 23292494Ssobomax clearcmdentry(0); 23392494Ssobomax } else if (c == 'v') { 23492494Ssobomax verbose++; 23592494Ssobomax } 23692494Ssobomax } 23792494Ssobomax if (*argptr == NULL) { 23892494Ssobomax for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 23992494Ssobomax for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 24092494Ssobomax if (cmdp->cmdtype == CMDNORMAL) 24192494Ssobomax printentry(cmdp, verbose); 24292494Ssobomax } 24392494Ssobomax } 24492494Ssobomax return 0; 24592494Ssobomax } 24692494Ssobomax while ((name = *argptr) != NULL) { 24792494Ssobomax if ((cmdp = cmdlookup(name, 0)) != NULL 24892494Ssobomax && (cmdp->cmdtype == CMDNORMAL 24992494Ssobomax || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) 25092494Ssobomax delete_cmd_entry(); 25192494Ssobomax find_command(name, &entry, 1, pathval()); 25292494Ssobomax if (verbose) { 25392494Ssobomax if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 254124811Sjhb cmdp = cmdlookup(name, 0); 255124811Sjhb if (cmdp != NULL) 256124811Sjhb printentry(cmdp, verbose); 25792494Ssobomax else 25892494Ssobomax outfmt(&errout, "%s: not found\n", name); 25992494Ssobomax } 26092494Ssobomax flushall(); 26192494Ssobomax } 26292494Ssobomax argptr++; 26392494Ssobomax } 26492494Ssobomax return 0; 26592494Ssobomax} 26692494Ssobomax 26792494Ssobomax 268124811SjhbSTATIC void 269124811Sjhbprintentry(struct tblentry *cmdp, int verbose) 27092494Ssobomax{ 271124811Sjhb int idx; 27292494Ssobomax const char *path; 27392494Ssobomax char *name; 27492494Ssobomax 27592494Ssobomax if (cmdp->cmdtype == CMDNORMAL) { 27692494Ssobomax idx = cmdp->param.index; 27792494Ssobomax path = pathval(); 27892494Ssobomax do { 27992494Ssobomax name = padvance(&path, cmdp->cmdname); 28092494Ssobomax stunalloc(name); 28192494Ssobomax } while (--idx >= 0); 28292494Ssobomax out1str(name); 28392494Ssobomax } else if (cmdp->cmdtype == CMDBUILTIN) { 28492494Ssobomax out1fmt("builtin %s", cmdp->cmdname); 28592494Ssobomax } else if (cmdp->cmdtype == CMDFUNCTION) { 28692494Ssobomax out1fmt("function %s", cmdp->cmdname); 28792494Ssobomax if (verbose) { 28892494Ssobomax INTOFF; 28992494Ssobomax name = commandtext(getfuncnode(cmdp->param.func)); 29092494Ssobomax out1c(' '); 29192494Ssobomax out1str(name); 292124811Sjhb ckfree(name); 293124811Sjhb INTON; 29492494Ssobomax } 295124811Sjhb#ifdef DEBUG 29692494Ssobomax } else { 29792494Ssobomax error("internal error: cmdtype %d", cmdp->cmdtype); 29892494Ssobomax#endif 29992494Ssobomax } 30092494Ssobomax if (cmdp->rehash) 30192494Ssobomax out1c('*'); 30292494Ssobomax out1c('\n'); 30392494Ssobomax} 30492494Ssobomax 30592494Ssobomax 30692494Ssobomax 30792494Ssobomax/* 30892494Ssobomax * Resolve a command name. If you change this routine, you may have to 30992494Ssobomax * change the shellexec routine as well. 31092494Ssobomax */ 31192494Ssobomax 31292494Ssobomaxvoid 31392494Ssobomaxfind_command(const char *name, struct cmdentry *entry, int printerr, 314 const char *path) 315{ 316 struct tblentry *cmdp; 317 int idx; 318 int prev; 319 char *fullname; 320 struct stat statb; 321 int e; 322 int i; 323 int spec; 324 325 /* If name contains a slash, don't use the hash table */ 326 if (strchr(name, '/') != NULL) { 327 entry->cmdtype = CMDNORMAL; 328 entry->u.index = 0; 329 return; 330 } 331 332 /* If name is in the table, and not invalidated by cd, we're done */ 333 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 334 goto success; 335 336 /* If %builtin not in path, check for builtin next */ 337 if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) { 338 INTOFF; 339 cmdp = cmdlookup(name, 1); 340 cmdp->cmdtype = CMDBUILTIN; 341 cmdp->param.index = i; 342 cmdp->special = spec; 343 INTON; 344 goto success; 345 } 346 347 /* We have to search path. */ 348 prev = -1; /* where to start */ 349 if (cmdp) { /* doing a rehash */ 350 if (cmdp->cmdtype == CMDBUILTIN) 351 prev = builtinloc; 352 else 353 prev = cmdp->param.index; 354 } 355 356 e = ENOENT; 357 idx = -1; 358loop: 359 while ((fullname = padvance(&path, name)) != NULL) { 360 stunalloc(fullname); 361 idx++; 362 if (pathopt) { 363 if (prefix("builtin", pathopt)) { 364 if ((i = find_builtin(name, &spec)) < 0) 365 goto loop; 366 INTOFF; 367 cmdp = cmdlookup(name, 1); 368 cmdp->cmdtype = CMDBUILTIN; 369 cmdp->param.index = i; 370 cmdp->special = spec; 371 INTON; 372 goto success; 373 } else if (prefix("func", pathopt)) { 374 /* handled below */ 375 } else { 376 goto loop; /* ignore unimplemented options */ 377 } 378 } 379 /* if rehash, don't redo absolute path names */ 380 if (fullname[0] == '/' && idx <= prev) { 381 if (idx < prev) 382 goto loop; 383 TRACE(("searchexec \"%s\": no change\n", name)); 384 goto success; 385 } 386 if (stat(fullname, &statb) < 0) { 387 if (errno != ENOENT && errno != ENOTDIR) 388 e = errno; 389 goto loop; 390 } 391 e = EACCES; /* if we fail, this will be the error */ 392 if (!S_ISREG(statb.st_mode)) 393 goto loop; 394 if (pathopt) { /* this is a %func directory */ 395 stalloc(strlen(fullname) + 1); 396 readcmdfile(fullname); 397 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 398 error("%s not defined in %s", name, fullname); 399 stunalloc(fullname); 400 goto success; 401 } 402#ifdef notdef 403 if (statb.st_uid == geteuid()) { 404 if ((statb.st_mode & 0100) == 0) 405 goto loop; 406 } else if (statb.st_gid == getegid()) { 407 if ((statb.st_mode & 010) == 0) 408 goto loop; 409 } else { 410 if ((statb.st_mode & 01) == 0) 411 goto loop; 412 } 413#endif 414 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 415 INTOFF; 416 cmdp = cmdlookup(name, 1); 417 cmdp->cmdtype = CMDNORMAL; 418 cmdp->param.index = idx; 419 INTON; 420 goto success; 421 } 422 423 /* We failed. If there was an entry for this command, delete it */ 424 if (cmdp) 425 delete_cmd_entry(); 426 if (printerr) { 427 if (e == ENOENT || e == ENOTDIR) 428 outfmt(out2, "%s: not found\n", name); 429 else 430 outfmt(out2, "%s: %s\n", name, strerror(e)); 431 } 432 entry->cmdtype = CMDUNKNOWN; 433 entry->u.index = 0; 434 return; 435 436success: 437 cmdp->rehash = 0; 438 entry->cmdtype = cmdp->cmdtype; 439 entry->u = cmdp->param; 440 entry->special = cmdp->special; 441} 442 443 444 445/* 446 * Search the table of builtin commands. 447 */ 448 449int 450find_builtin(const char *name, int *special) 451{ 452 const struct builtincmd *bp; 453 454 for (bp = builtincmd ; bp->name ; bp++) { 455 if (*bp->name == *name && equal(bp->name, name)) { 456 *special = bp->special; 457 return bp->code; 458 } 459 } 460 return -1; 461} 462 463 464 465/* 466 * Called when a cd is done. Marks all commands so the next time they 467 * are executed they will be rehashed. 468 */ 469 470void 471hashcd(void) 472{ 473 struct tblentry **pp; 474 struct tblentry *cmdp; 475 476 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 477 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 478 if (cmdp->cmdtype == CMDNORMAL 479 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 480 cmdp->rehash = 1; 481 } 482 } 483} 484 485 486 487/* 488 * Called before PATH is changed. The argument is the new value of PATH; 489 * pathval() still returns the old value at this point. Called with 490 * interrupts off. 491 */ 492 493void 494changepath(const char *newval) 495{ 496 const char *old, *new; 497 int idx; 498 int firstchange; 499 int bltin; 500 501 old = pathval(); 502 new = newval; 503 firstchange = 9999; /* assume no change */ 504 idx = 0; 505 bltin = -1; 506 for (;;) { 507 if (*old != *new) { 508 firstchange = idx; 509 if ((*old == '\0' && *new == ':') 510 || (*old == ':' && *new == '\0')) 511 firstchange++; 512 old = new; /* ignore subsequent differences */ 513 } 514 if (*new == '\0') 515 break; 516 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 517 bltin = idx; 518 if (*new == ':') { 519 idx++; 520 } 521 new++, old++; 522 } 523 if (builtinloc < 0 && bltin >= 0) 524 builtinloc = bltin; /* zap builtins */ 525 if (builtinloc >= 0 && bltin < 0) 526 firstchange = 0; 527 clearcmdentry(firstchange); 528 builtinloc = bltin; 529} 530 531 532/* 533 * Clear out command entries. The argument specifies the first entry in 534 * PATH which has changed. 535 */ 536 537void 538clearcmdentry(int firstchange) 539{ 540 struct tblentry **tblp; 541 struct tblentry **pp; 542 struct tblentry *cmdp; 543 544 INTOFF; 545 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 546 pp = tblp; 547 while ((cmdp = *pp) != NULL) { 548 if ((cmdp->cmdtype == CMDNORMAL && 549 cmdp->param.index >= firstchange) 550 || (cmdp->cmdtype == CMDBUILTIN && 551 builtinloc >= firstchange)) { 552 *pp = cmdp->next; 553 ckfree(cmdp); 554 } else { 555 pp = &cmdp->next; 556 } 557 } 558 } 559 INTON; 560} 561 562 563/* 564 * Delete all functions. 565 */ 566 567#ifdef mkinit 568MKINIT void deletefuncs(void); 569 570SHELLPROC { 571 deletefuncs(); 572} 573#endif 574 575void 576deletefuncs(void) 577{ 578 struct tblentry **tblp; 579 struct tblentry **pp; 580 struct tblentry *cmdp; 581 582 INTOFF; 583 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 584 pp = tblp; 585 while ((cmdp = *pp) != NULL) { 586 if (cmdp->cmdtype == CMDFUNCTION) { 587 *pp = cmdp->next; 588 unreffunc(cmdp->param.func); 589 ckfree(cmdp); 590 } else { 591 pp = &cmdp->next; 592 } 593 } 594 } 595 INTON; 596} 597 598 599 600/* 601 * Locate a command in the command hash table. If "add" is nonzero, 602 * add the command to the table if it is not already present. The 603 * variable "lastcmdentry" is set to point to the address of the link 604 * pointing to the entry, so that delete_cmd_entry can delete the 605 * entry. 606 */ 607 608STATIC struct tblentry **lastcmdentry; 609 610 611STATIC struct tblentry * 612cmdlookup(const char *name, int add) 613{ 614 int hashval; 615 const char *p; 616 struct tblentry *cmdp; 617 struct tblentry **pp; 618 619 p = name; 620 hashval = *p << 4; 621 while (*p) 622 hashval += *p++; 623 hashval &= 0x7FFF; 624 pp = &cmdtable[hashval % CMDTABLESIZE]; 625 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 626 if (equal(cmdp->cmdname, name)) 627 break; 628 pp = &cmdp->next; 629 } 630 if (add && cmdp == NULL) { 631 INTOFF; 632 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 633 + strlen(name) + 1); 634 cmdp->next = NULL; 635 cmdp->cmdtype = CMDUNKNOWN; 636 cmdp->rehash = 0; 637 strcpy(cmdp->cmdname, name); 638 INTON; 639 } 640 lastcmdentry = pp; 641 return cmdp; 642} 643 644/* 645 * Delete the command entry returned on the last lookup. 646 */ 647 648STATIC void 649delete_cmd_entry(void) 650{ 651 struct tblentry *cmdp; 652 653 INTOFF; 654 cmdp = *lastcmdentry; 655 *lastcmdentry = cmdp->next; 656 ckfree(cmdp); 657 INTON; 658} 659 660 661 662/* 663 * Add a new command entry, replacing any existing command entry for 664 * the same name. 665 */ 666 667void 668addcmdentry(const char *name, struct cmdentry *entry) 669{ 670 struct tblentry *cmdp; 671 672 INTOFF; 673 cmdp = cmdlookup(name, 1); 674 if (cmdp->cmdtype == CMDFUNCTION) { 675 unreffunc(cmdp->param.func); 676 } 677 cmdp->cmdtype = entry->cmdtype; 678 cmdp->param = entry->u; 679 INTON; 680} 681 682 683/* 684 * Define a shell function. 685 */ 686 687void 688defun(const char *name, union node *func) 689{ 690 struct cmdentry entry; 691 692 INTOFF; 693 entry.cmdtype = CMDFUNCTION; 694 entry.u.func = copyfunc(func); 695 addcmdentry(name, &entry); 696 INTON; 697} 698 699 700/* 701 * Delete a function if it exists. 702 */ 703 704int 705unsetfunc(const char *name) 706{ 707 struct tblentry *cmdp; 708 709 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 710 unreffunc(cmdp->param.func); 711 delete_cmd_entry(); 712 return (0); 713 } 714 return (0); 715} 716 717/* 718 * Shared code for the following builtin commands: 719 * type, command -v, command -V 720 */ 721 722int 723typecmd_impl(int argc, char **argv, int cmd, const char *path) 724{ 725 struct cmdentry entry; 726 struct tblentry *cmdp; 727 const char *const *pp; 728 struct alias *ap; 729 int i; 730 int error1 = 0; 731 732 if (path != pathval()) 733 clearcmdentry(0); 734 735 for (i = 1; i < argc; i++) { 736 /* First look at the keywords */ 737 for (pp = parsekwd; *pp; pp++) 738 if (**pp == *argv[i] && equal(*pp, argv[i])) 739 break; 740 741 if (*pp) { 742 if (cmd == TYPECMD_SMALLV) 743 out1fmt("%s\n", argv[i]); 744 else 745 out1fmt("%s is a shell keyword\n", argv[i]); 746 continue; 747 } 748 749 /* Then look at the aliases */ 750 if ((ap = lookupalias(argv[i], 1)) != NULL) { 751 if (cmd == TYPECMD_SMALLV) 752 out1fmt("alias %s='%s'\n", argv[i], ap->val); 753 else 754 out1fmt("%s is an alias for %s\n", argv[i], 755 ap->val); 756 continue; 757 } 758 759 /* Then check if it is a tracked alias */ 760 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 761 entry.cmdtype = cmdp->cmdtype; 762 entry.u = cmdp->param; 763 entry.special = cmdp->special; 764 } 765 else { 766 /* Finally use brute force */ 767 find_command(argv[i], &entry, 0, path); 768 } 769 770 switch (entry.cmdtype) { 771 case CMDNORMAL: { 772 if (strchr(argv[i], '/') == NULL) { 773 const char *path2 = path; 774 char *name; 775 int j = entry.u.index; 776 do { 777 name = padvance(&path2, argv[i]); 778 stunalloc(name); 779 } while (--j >= 0); 780 if (cmd == TYPECMD_SMALLV) 781 out1fmt("%s\n", name); 782 else 783 out1fmt("%s is%s %s\n", argv[i], 784 (cmdp && cmd == TYPECMD_TYPE) ? 785 " a tracked alias for" : "", 786 name); 787 } else { 788 if (eaccess(argv[i], X_OK) == 0) { 789 if (cmd == TYPECMD_SMALLV) 790 out1fmt("%s\n", argv[i]); 791 else 792 out1fmt("%s is %s\n", argv[i], 793 argv[i]); 794 } else { 795 if (cmd != TYPECMD_SMALLV) 796 outfmt(out2, "%s: %s\n", 797 argv[i], strerror(errno)); 798 error1 |= 127; 799 } 800 } 801 break; 802 } 803 case CMDFUNCTION: 804 if (cmd == TYPECMD_SMALLV) 805 out1fmt("%s\n", argv[i]); 806 else 807 out1fmt("%s is a shell function\n", argv[i]); 808 break; 809 810 case CMDBUILTIN: 811 if (cmd == TYPECMD_SMALLV) 812 out1fmt("%s\n", argv[i]); 813 else if (entry.special) 814 out1fmt("%s is a special shell builtin\n", 815 argv[i]); 816 else 817 out1fmt("%s is a shell builtin\n", argv[i]); 818 break; 819 820 default: 821 if (cmd != TYPECMD_SMALLV) 822 outfmt(out2, "%s: not found\n", argv[i]); 823 error1 |= 127; 824 break; 825 } 826 } 827 828 if (path != pathval()) 829 clearcmdentry(0); 830 831 return error1; 832} 833 834/* 835 * Locate and print what a word is... 836 */ 837 838int 839typecmd(int argc, char **argv) 840{ 841 return typecmd_impl(argc, argv, TYPECMD_TYPE, pathval()); 842} 843