exec.c revision 266932
1184610Salfred/*- 2184610Salfred * Copyright (c) 1991, 1993 3184610Salfred * The Regents of the University of California. All rights reserved. 4184610Salfred * 5184610Salfred * This code is derived from software contributed to Berkeley by 6184610Salfred * Kenneth Almquist. 7184610Salfred * 8184610Salfred * Redistribution and use in source and binary forms, with or without 9184610Salfred * modification, are permitted provided that the following conditions 10184610Salfred * are met: 11184610Salfred * 1. Redistributions of source code must retain the above copyright 12184610Salfred * notice, this list of conditions and the following disclaimer. 13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer in the 15184610Salfred * documentation and/or other materials provided with the distribution. 16184610Salfred * 4. Neither the name of the University nor the names of its contributors 17184610Salfred * may be used to endorse or promote products derived from this software 18184610Salfred * without specific prior written permission. 19184610Salfred * 20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25190754Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27188942Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28188942Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29188942Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30184610Salfred * SUCH DAMAGE. 31194228Sthompsa */ 32184610Salfred 33188942Sthompsa#ifndef lint 34188942Sthompsa#if 0 35188942Sthompsastatic char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 36188942Sthompsa#endif 37188942Sthompsa#endif /* not lint */ 38188942Sthompsa#include <sys/cdefs.h> 39188942Sthompsa__FBSDID("$FreeBSD: head/bin/sh/exec.c 266932 2014-05-31 22:25:45Z jilles $"); 40184610Salfred 41188942Sthompsa#include <sys/types.h> 42188942Sthompsa#include <sys/stat.h> 43184610Salfred#include <unistd.h> 44194228Sthompsa#include <fcntl.h> 45184610Salfred#include <errno.h> 46184610Salfred#include <paths.h> 47184610Salfred#include <stdlib.h> 48184610Salfred 49184610Salfred/* 50184610Salfred * When commands are first encountered, they are entered in a hash table. 51184610Salfred * This ensures that a full path search will not have to be done for them 52184610Salfred * on each invocation. 53194228Sthompsa * 54184610Salfred * We should investigate converting to a linear search, even though that 55194228Sthompsa * would make the command name "hash" a misnomer. 56184610Salfred */ 57184610Salfred 58184610Salfred#include "shell.h" 59184610Salfred#include "main.h" 60184610Salfred#include "nodes.h" 61184610Salfred#include "parser.h" 62184610Salfred#include "redir.h" 63190734Sthompsa#include "eval.h" 64190734Sthompsa#include "exec.h" 65194228Sthompsa#include "builtins.h" 66192499Sthompsa#include "var.h" 67184610Salfred#include "options.h" 68184610Salfred#include "input.h" 69184610Salfred#include "output.h" 70184610Salfred#include "syntax.h" 71184610Salfred#include "memalloc.h" 72184610Salfred#include "error.h" 73184610Salfred#include "mystring.h" 74184610Salfred#include "show.h" 75192984Sthompsa#include "jobs.h" 76194228Sthompsa#include "alias.h" 77190734Sthompsa 78190734Sthompsa 79190734Sthompsa#define CMDTABLESIZE 31 /* should be prime */ 80184610Salfred 81184610Salfred 82184610Salfred 83184610Salfredstruct tblentry { 84184610Salfred struct tblentry *next; /* next entry in hash chain */ 85194228Sthompsa union param param; /* definition of builtin function */ 86194228Sthompsa int special; /* flag for special builtin commands */ 87194228Sthompsa signed char cmdtype; /* index identifying command */ 88194228Sthompsa char cmdname[]; /* name of command */ 89194228Sthompsa}; 90194228Sthompsa 91194228Sthompsa 92194228Sthompsastatic struct tblentry *cmdtable[CMDTABLESIZE]; 93194228Sthompsastatic int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */ 94194228Sthompsaint exerrno = 0; /* Last exec error */ 95194228Sthompsa 96192500Sthompsa 97184610Salfredstatic void tryexec(char *, char **, char **); 98184610Salfredstatic void printentry(struct tblentry *, int); 99194228Sthompsastatic struct tblentry *cmdlookup(const char *, int); 100190734Sthompsastatic void delete_cmd_entry(void); 101190734Sthompsastatic void addcmdentry(const char *, struct cmdentry *); 102194228Sthompsa 103190734Sthompsa 104192499Sthompsa 105194228Sthompsa/* 106190734Sthompsa * Exec a program. Never returns. If you change this routine, you may 107194228Sthompsa * have to change the find_command routine as well. 108190734Sthompsa * 109190734Sthompsa * The argv array may be changed and element argv[-1] should be writable. 110190734Sthompsa */ 111194228Sthompsa 112184610Salfredvoid 113184610Salfredshellexec(char **argv, char **envp, const char *path, int idx) 114184610Salfred{ 115184610Salfred char *cmdname; 116184610Salfred int e; 117194228Sthompsa 118184610Salfred if (strchr(argv[0], '/') != NULL) { 119184610Salfred tryexec(argv[0], argv, envp); 120184610Salfred e = errno; 121184610Salfred } else { 122184610Salfred e = ENOENT; 123184610Salfred while ((cmdname = padvance(&path, argv[0])) != NULL) { 124184610Salfred if (--idx < 0 && pathopt == NULL) { 125184610Salfred tryexec(cmdname, argv, envp); 126184610Salfred if (errno != ENOENT && errno != ENOTDIR) 127184610Salfred e = errno; 128184610Salfred if (e == ENOEXEC) 129184610Salfred break; 130184610Salfred } 131194228Sthompsa stunalloc(cmdname); 132184610Salfred } 133184610Salfred } 134184610Salfred 135184610Salfred /* Map to POSIX errors */ 136184610Salfred if (e == ENOENT || e == ENOTDIR) { 137184610Salfred exerrno = 127; 138184610Salfred exerror(EXEXEC, "%s: not found", argv[0]); 139184610Salfred } else { 140193045Sthompsa exerrno = 126; 141194228Sthompsa exerror(EXEXEC, "%s: %s", argv[0], strerror(e)); 142184610Salfred } 143184610Salfred} 144184610Salfred 145184610Salfred 146184610Salfredstatic void 147184610Salfredtryexec(char *cmd, char **argv, char **envp) 148184610Salfred{ 149184610Salfred int e, in; 150184610Salfred ssize_t n; 151184610Salfred char buf[256]; 152184610Salfred 153184610Salfred execve(cmd, argv, envp); 154184610Salfred e = errno; 155184610Salfred if (e == ENOEXEC) { 156184610Salfred INTOFF; 157184610Salfred in = open(cmd, O_RDONLY | O_NONBLOCK); 158194228Sthompsa if (in != -1) { 159184610Salfred n = pread(in, buf, sizeof buf, 0); 160184610Salfred close(in); 161184610Salfred if (n > 0 && memchr(buf, '\0', n) != NULL) { 162184610Salfred errno = ENOEXEC; 163184610Salfred return; 164184610Salfred } 165184610Salfred } 166184610Salfred *argv = cmd; 167184610Salfred *--argv = __DECONST(char *, _PATH_BSHELL); 168190180Sthompsa execve(_PATH_BSHELL, argv, envp); 169184610Salfred } 170194228Sthompsa errno = e; 171193074Sthompsa} 172193074Sthompsa 173184610Salfred/* 174192984Sthompsa * Do a path search. The variable path (passed by reference) should be 175192984Sthompsa * set to the start of the path before the first call; padvance will update 176184610Salfred * this value as it proceeds. Successive calls to padvance will return 177193074Sthompsa * the possible path expansions in sequence. If an option (indicated by 178193074Sthompsa * a percent sign) appears in the path entry then the global variable 179193074Sthompsa * pathopt will be set to point to it; otherwise pathopt will be set to 180193074Sthompsa * NULL. 181193074Sthompsa */ 182193074Sthompsa 183184610Salfredconst char *pathopt; 184184610Salfred 185184610Salfredchar * 186184610Salfredpadvance(const char **path, const char *name) 187184610Salfred{ 188184610Salfred const char *p, *start; 189184610Salfred char *q; 190184610Salfred size_t len, namelen; 191184610Salfred 192184610Salfred if (*path == NULL) 193184610Salfred return NULL; 194184610Salfred start = *path; 195184610Salfred for (p = start; *p && *p != ':' && *p != '%'; p++) 196184610Salfred ; /* nothing */ 197184610Salfred namelen = strlen(name); 198184610Salfred len = p - start + namelen + 2; /* "2" is for '/' and '\0' */ 199184610Salfred STARTSTACKSTR(q); 200184610Salfred CHECKSTRSPACE(len, q); 201184610Salfred if (p != start) { 202184610Salfred memcpy(q, start, p - start); 203184610Salfred q += p - start; 204184610Salfred *q++ = '/'; 205184610Salfred } 206184610Salfred memcpy(q, name, namelen + 1); 207184610Salfred pathopt = NULL; 208184610Salfred if (*p == '%') { 209184610Salfred pathopt = ++p; 210184610Salfred while (*p && *p != ':') p++; 211184610Salfred } 212184610Salfred if (*p == ':') 213184610Salfred *path = p + 1; 214184610Salfred else 215184610Salfred *path = NULL; 216184610Salfred return stalloc(len); 217184610Salfred} 218184610Salfred 219184610Salfred 220184610Salfred 221184610Salfred/*** Command hashing code ***/ 222184610Salfred 223184610Salfred 224187173Sthompsaint 225184610Salfredhashcmd(int argc __unused, char **argv __unused) 226184610Salfred{ 227184610Salfred struct tblentry **pp; 228184610Salfred struct tblentry *cmdp; 229187173Sthompsa int c; 230184610Salfred int verbose; 231184610Salfred struct cmdentry entry; 232184610Salfred char *name; 233184610Salfred int errors; 234184610Salfred 235184610Salfred errors = 0; 236184610Salfred verbose = 0; 237184610Salfred while ((c = nextopt("rv")) != '\0') { 238184610Salfred if (c == 'r') { 239184610Salfred clearcmdentry(); 240184610Salfred } else if (c == 'v') { 241184610Salfred verbose++; 242184610Salfred } 243184610Salfred } 244184610Salfred if (*argptr == NULL) { 245184610Salfred for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 246184610Salfred for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 247194228Sthompsa if (cmdp->cmdtype == CMDNORMAL) 248184610Salfred printentry(cmdp, verbose); 249184610Salfred } 250184610Salfred } 251184610Salfred return 0; 252184610Salfred } 253184610Salfred while ((name = *argptr) != NULL) { 254184610Salfred if ((cmdp = cmdlookup(name, 0)) != NULL 255184610Salfred && cmdp->cmdtype == CMDNORMAL) 256184610Salfred delete_cmd_entry(); 257184610Salfred find_command(name, &entry, DO_ERR, pathval()); 258184610Salfred if (entry.cmdtype == CMDUNKNOWN) 259184610Salfred errors = 1; 260194228Sthompsa else if (verbose) { 261184610Salfred cmdp = cmdlookup(name, 0); 262184610Salfred if (cmdp != NULL) 263184610Salfred printentry(cmdp, verbose); 264184610Salfred else { 265184610Salfred outfmt(out2, "%s: not found\n", name); 266184610Salfred errors = 1; 267194228Sthompsa } 268184610Salfred flushall(); 269184610Salfred } 270184610Salfred argptr++; 271184610Salfred } 272184610Salfred return errors; 273184610Salfred} 274184610Salfred 275184610Salfred 276184610Salfredstatic void 277184610Salfredprintentry(struct tblentry *cmdp, int verbose) 278184610Salfred{ 279190180Sthompsa int idx; 280184610Salfred const char *path; 281184610Salfred char *name; 282194228Sthompsa 283184610Salfred if (cmdp->cmdtype == CMDNORMAL) { 284184610Salfred idx = cmdp->param.index; 285184610Salfred path = pathval(); 286184610Salfred do { 287192984Sthompsa name = padvance(&path, cmdp->cmdname); 288184610Salfred stunalloc(name); 289184610Salfred } while (--idx >= 0); 290184610Salfred out1str(name); 291194228Sthompsa } else if (cmdp->cmdtype == CMDBUILTIN) { 292184610Salfred out1fmt("builtin %s", cmdp->cmdname); 293184610Salfred } else if (cmdp->cmdtype == CMDFUNCTION) { 294184610Salfred out1fmt("function %s", cmdp->cmdname); 295184610Salfred if (verbose) { 296184610Salfred INTOFF; 297192984Sthompsa name = commandtext(getfuncnode(cmdp->param.func)); 298192984Sthompsa out1c(' '); 299192984Sthompsa out1str(name); 300194228Sthompsa ckfree(name); 301193045Sthompsa INTON; 302193045Sthompsa } 303193045Sthompsa#ifdef DEBUG 304184610Salfred } else { 305184610Salfred error("internal error: cmdtype %d", cmdp->cmdtype); 306184610Salfred#endif 307184610Salfred } 308184610Salfred out1c('\n'); 309184610Salfred} 310184610Salfred 311184610Salfred 312184610Salfred 313184610Salfred/* 314184610Salfred * Resolve a command name. If you change this routine, you may have to 315184610Salfred * change the shellexec routine as well. 316184610Salfred */ 317193644Sthompsa 318184610Salfredvoid 319184610Salfredfind_command(const char *name, struct cmdentry *entry, int act, 320184610Salfred const char *path) 321190734Sthompsa{ 322190734Sthompsa struct tblentry *cmdp, loc_cmd; 323190734Sthompsa int idx; 324190734Sthompsa char *fullname; 325190734Sthompsa struct stat statb; 326193644Sthompsa int e; 327184610Salfred int i; 328184610Salfred int spec; 329184610Salfred int cd; 330192499Sthompsa 331184610Salfred /* If name contains a slash, don't use the hash table */ 332190734Sthompsa if (strchr(name, '/') != NULL) { 333184610Salfred entry->cmdtype = CMDNORMAL; 334184610Salfred entry->u.index = 0; 335184610Salfred return; 336184610Salfred } 337184610Salfred 338184610Salfred cd = 0; 339184610Salfred 340184610Salfred /* If name is in the table, and not invalidated by cd, we're done */ 341184610Salfred if ((cmdp = cmdlookup(name, 0)) != NULL) { 342184610Salfred if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC) 343184610Salfred cmdp = NULL; 344184610Salfred else 345184610Salfred goto success; 346184610Salfred } 347184610Salfred 348184610Salfred /* Check for builtin next */ 349184610Salfred if ((i = find_builtin(name, &spec)) >= 0) { 350184610Salfred INTOFF; 351194228Sthompsa cmdp = cmdlookup(name, 1); 352184610Salfred if (cmdp->cmdtype == CMDFUNCTION) 353184610Salfred cmdp = &loc_cmd; 354184610Salfred cmdp->cmdtype = CMDBUILTIN; 355184610Salfred cmdp->param.index = i; 356184610Salfred cmdp->special = spec; 357184610Salfred INTON; 358184610Salfred goto success; 359184610Salfred } 360184610Salfred 361184610Salfred /* We have to search path. */ 362184610Salfred 363184610Salfred e = ENOENT; 364184610Salfred idx = -1; 365184610Salfred for (;(fullname = padvance(&path, name)) != NULL; stunalloc(fullname)) { 366184610Salfred idx++; 367184610Salfred if (pathopt) { 368184610Salfred if (prefix("func", pathopt)) { 369184610Salfred /* handled below */ 370184610Salfred } else { 371184610Salfred continue; /* ignore unimplemented options */ 372184610Salfred } 373184610Salfred } 374184610Salfred if (fullname[0] != '/') 375184610Salfred cd = 1; 376184610Salfred if (stat(fullname, &statb) < 0) { 377194228Sthompsa if (errno != ENOENT && errno != ENOTDIR) 378184610Salfred e = errno; 379184610Salfred continue; 380184610Salfred } 381184610Salfred e = EACCES; /* if we fail, this will be the error */ 382184610Salfred if (!S_ISREG(statb.st_mode)) 383190181Sthompsa continue; 384184610Salfred if (pathopt) { /* this is a %func directory */ 385184610Salfred readcmdfile(fullname); 386184610Salfred if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 387184610Salfred error("%s not defined in %s", name, fullname); 388184610Salfred stunalloc(fullname); 389184610Salfred goto success; 390184610Salfred } 391184610Salfred#ifdef notdef 392184610Salfred if (statb.st_uid == geteuid()) { 393184610Salfred if ((statb.st_mode & 0100) == 0) 394184610Salfred goto loop; 395187180Sthompsa } else if (statb.st_gid == getegid()) { 396187180Sthompsa if ((statb.st_mode & 010) == 0) 397187180Sthompsa goto loop; 398187180Sthompsa } else { 399187180Sthompsa if ((statb.st_mode & 01) == 0) 400187180Sthompsa goto loop; 401184610Salfred } 402187180Sthompsa#endif 403184610Salfred TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 404184610Salfred INTOFF; 405184610Salfred stunalloc(fullname); 406184610Salfred cmdp = cmdlookup(name, 1); 407184610Salfred if (cmdp->cmdtype == CMDFUNCTION) 408184610Salfred cmdp = &loc_cmd; 409184610Salfred cmdp->cmdtype = CMDNORMAL; 410184610Salfred cmdp->param.index = idx; 411184610Salfred INTON; 412184610Salfred goto success; 413184610Salfred } 414184610Salfred 415184610Salfred if (act & DO_ERR) { 416184610Salfred if (e == ENOENT || e == ENOTDIR) 417184610Salfred outfmt(out2, "%s: not found\n", name); 418184610Salfred else 419184610Salfred outfmt(out2, "%s: %s\n", name, strerror(e)); 420184610Salfred } 421184610Salfred entry->cmdtype = CMDUNKNOWN; 422184610Salfred entry->u.index = 0; 423184610Salfred return; 424184610Salfred 425184610Salfredsuccess: 426184610Salfred if (cd) 427184610Salfred cmdtable_cd = 1; 428184610Salfred entry->cmdtype = cmdp->cmdtype; 429184610Salfred entry->u = cmdp->param; 430184610Salfred entry->special = cmdp->special; 431184610Salfred} 432187180Sthompsa 433187180Sthompsa 434187180Sthompsa 435187180Sthompsa/* 436187180Sthompsa * Search the table of builtin commands. 437187180Sthompsa */ 438187180Sthompsa 439187180Sthompsaint 440187180Sthompsafind_builtin(const char *name, int *special) 441187180Sthompsa{ 442187180Sthompsa const struct builtincmd *bp; 443187180Sthompsa 444187180Sthompsa for (bp = builtincmd ; bp->name ; bp++) { 445187180Sthompsa if (*bp->name == *name && equal(bp->name, name)) { 446187180Sthompsa *special = bp->special; 447187180Sthompsa return bp->code; 448187180Sthompsa } 449187180Sthompsa } 450184610Salfred return -1; 451184610Salfred} 452184610Salfred 453187180Sthompsa 454187180Sthompsa 455184610Salfred/* 456184610Salfred * Called when a cd is done. If any entry in cmdtable depends on the current 457184610Salfred * directory, simply clear cmdtable completely. 458184610Salfred */ 459184610Salfred 460184610Salfredvoid 461184610Salfredhashcd(void) 462184610Salfred{ 463184610Salfred if (cmdtable_cd) 464184610Salfred clearcmdentry(); 465184610Salfred} 466184610Salfred 467184610Salfred 468184610Salfred 469184610Salfred/* 470184610Salfred * Called before PATH is changed. The argument is the new value of PATH; 471184610Salfred * pathval() still returns the old value at this point. Called with 472184610Salfred * interrupts off. 473184610Salfred */ 474184610Salfred 475184610Salfredvoid 476184610Salfredchangepath(const char *newval __unused) 477184610Salfred{ 478184610Salfred clearcmdentry(); 479184610Salfred} 480184610Salfred 481194228Sthompsa 482184610Salfred/* 483184610Salfred * Clear out command entries. The argument specifies the first entry in 484184610Salfred * PATH which has changed. 485184610Salfred */ 486184610Salfred 487184610Salfredvoid 488184610Salfredclearcmdentry(void) 489184610Salfred{ 490184610Salfred struct tblentry **tblp; 491184610Salfred struct tblentry **pp; 492184610Salfred struct tblentry *cmdp; 493184610Salfred 494184610Salfred INTOFF; 495184610Salfred for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 496184610Salfred pp = tblp; 497184610Salfred while ((cmdp = *pp) != NULL) { 498184610Salfred if (cmdp->cmdtype == CMDNORMAL) { 499184610Salfred *pp = cmdp->next; 500184610Salfred ckfree(cmdp); 501184610Salfred } else { 502184610Salfred pp = &cmdp->next; 503184610Salfred } 504184610Salfred } 505184610Salfred } 506184610Salfred cmdtable_cd = 0; 507184610Salfred INTON; 508184610Salfred} 509184610Salfred 510184610Salfred 511184610Salfred/* 512184610Salfred * Locate a command in the command hash table. If "add" is nonzero, 513184610Salfred * add the command to the table if it is not already present. The 514184610Salfred * variable "lastcmdentry" is set to point to the address of the link 515184610Salfred * pointing to the entry, so that delete_cmd_entry can delete the 516184610Salfred * entry. 517184610Salfred */ 518184610Salfred 519184610Salfredstatic struct tblentry **lastcmdentry; 520184610Salfred 521184610Salfred 522184610Salfredstatic struct tblentry * 523184610Salfredcmdlookup(const char *name, int add) 524184610Salfred{ 525184610Salfred int hashval; 526184610Salfred const char *p; 527184610Salfred struct tblentry *cmdp; 528184610Salfred struct tblentry **pp; 529184610Salfred size_t len; 530184610Salfred 531184610Salfred p = name; 532184610Salfred hashval = *p << 4; 533184610Salfred while (*p) 534184610Salfred hashval += *p++; 535184610Salfred hashval &= 0x7FFF; 536184610Salfred pp = &cmdtable[hashval % CMDTABLESIZE]; 537184610Salfred for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 538184610Salfred if (equal(cmdp->cmdname, name)) 539184610Salfred break; 540184610Salfred pp = &cmdp->next; 541184610Salfred } 542184610Salfred if (add && cmdp == NULL) { 543184610Salfred INTOFF; 544184610Salfred len = strlen(name); 545184610Salfred cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1); 546184610Salfred cmdp->next = NULL; 547184610Salfred cmdp->cmdtype = CMDUNKNOWN; 548184610Salfred memcpy(cmdp->cmdname, name, len + 1); 549184610Salfred INTON; 550184610Salfred } 551184610Salfred lastcmdentry = pp; 552184610Salfred return cmdp; 553184610Salfred} 554184610Salfred 555184610Salfred/* 556184610Salfred * Delete the command entry returned on the last lookup. 557184610Salfred */ 558184610Salfred 559184610Salfredstatic void 560184610Salfreddelete_cmd_entry(void) 561184610Salfred{ 562184610Salfred struct tblentry *cmdp; 563184610Salfred 564184610Salfred INTOFF; 565184610Salfred cmdp = *lastcmdentry; 566184610Salfred *lastcmdentry = cmdp->next; 567184610Salfred ckfree(cmdp); 568184610Salfred INTON; 569184610Salfred} 570184610Salfred 571184610Salfred 572184610Salfred 573184610Salfred/* 574184610Salfred * Add a new command entry, replacing any existing command entry for 575184610Salfred * the same name. 576184610Salfred */ 577184610Salfred 578184610Salfredstatic void 579184610Salfredaddcmdentry(const char *name, struct cmdentry *entry) 580184610Salfred{ 581184610Salfred struct tblentry *cmdp; 582184610Salfred 583184610Salfred INTOFF; 584184610Salfred cmdp = cmdlookup(name, 1); 585184610Salfred if (cmdp->cmdtype == CMDFUNCTION) { 586184610Salfred unreffunc(cmdp->param.func); 587184610Salfred } 588184610Salfred cmdp->cmdtype = entry->cmdtype; 589184610Salfred cmdp->param = entry->u; 590184610Salfred INTON; 591184610Salfred} 592184610Salfred 593184610Salfred 594184610Salfred/* 595184610Salfred * Define a shell function. 596184610Salfred */ 597184610Salfred 598184610Salfredvoid 599184610Salfreddefun(const char *name, union node *func) 600184610Salfred{ 601184610Salfred struct cmdentry entry; 602184610Salfred 603184610Salfred INTOFF; 604194228Sthompsa entry.cmdtype = CMDFUNCTION; 605184610Salfred entry.u.func = copyfunc(func); 606184610Salfred addcmdentry(name, &entry); 607194228Sthompsa INTON; 608184610Salfred} 609184610Salfred 610184610Salfred 611184610Salfred/* 612184610Salfred * Delete a function if it exists. 613184610Salfred * Called with interrupts off. 614184610Salfred */ 615184610Salfred 616184610Salfredint 617184610Salfredunsetfunc(const char *name) 618184610Salfred{ 619184610Salfred struct tblentry *cmdp; 620184610Salfred 621184610Salfred if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 622190180Sthompsa unreffunc(cmdp->param.func); 623184610Salfred delete_cmd_entry(); 624184610Salfred return (0); 625184610Salfred } 626184610Salfred return (0); 627184610Salfred} 628184610Salfred 629184610Salfred 630184610Salfred/* 631192984Sthompsa * Check if a function by a certain name exists. 632184610Salfred */ 633184610Salfredint 634184610Salfredisfunc(const char *name) 635184610Salfred{ 636184610Salfred struct tblentry *cmdp; 637184610Salfred cmdp = cmdlookup(name, 0); 638184610Salfred return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION); 639184610Salfred} 640184610Salfred 641184610Salfred 642184610Salfred/* 643184610Salfred * Shared code for the following builtin commands: 644184610Salfred * type, command -v, command -V 645184610Salfred */ 646184610Salfred 647184610Salfredint 648184610Salfredtypecmd_impl(int argc, char **argv, int cmd, const char *path) 649184610Salfred{ 650190180Sthompsa struct cmdentry entry; 651184610Salfred struct tblentry *cmdp; 652184610Salfred const char *const *pp; 653184610Salfred struct alias *ap; 654184610Salfred int i; 655184610Salfred int error1 = 0; 656184610Salfred 657190181Sthompsa if (path != pathval()) 658184610Salfred clearcmdentry(); 659184610Salfred 660184610Salfred for (i = 1; i < argc; i++) { 661190181Sthompsa /* First look at the keywords */ 662184610Salfred for (pp = parsekwd; *pp; pp++) 663184610Salfred if (**pp == *argv[i] && equal(*pp, argv[i])) 664184610Salfred break; 665184610Salfred 666184610Salfred if (*pp) { 667184610Salfred if (cmd == TYPECMD_SMALLV) 668184610Salfred out1fmt("%s\n", argv[i]); 669184610Salfred else 670184610Salfred out1fmt("%s is a shell keyword\n", argv[i]); 671184610Salfred continue; 672184610Salfred } 673184610Salfred 674187173Sthompsa /* Then look at the aliases */ 675190180Sthompsa if ((ap = lookupalias(argv[i], 1)) != NULL) { 676184610Salfred if (cmd == TYPECMD_SMALLV) { 677184610Salfred out1fmt("alias %s=", argv[i]); 678184610Salfred out1qstr(ap->val); 679194228Sthompsa outcslow('\n', out1); 680184610Salfred } else 681184610Salfred out1fmt("%s is an alias for %s\n", argv[i], 682184610Salfred ap->val); 683184610Salfred continue; 684184610Salfred } 685184610Salfred 686190180Sthompsa /* Then check if it is a tracked alias */ 687184610Salfred if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { 688184610Salfred entry.cmdtype = cmdp->cmdtype; 689184610Salfred entry.u = cmdp->param; 690184610Salfred entry.special = cmdp->special; 691184610Salfred } 692184610Salfred else { 693184610Salfred /* Finally use brute force */ 694190181Sthompsa find_command(argv[i], &entry, 0, path); 695184610Salfred } 696184610Salfred 697184610Salfred switch (entry.cmdtype) { 698184610Salfred case CMDNORMAL: { 699184610Salfred if (strchr(argv[i], '/') == NULL) { 700184610Salfred const char *path2 = path; 701184610Salfred char *name; 702184610Salfred int j = entry.u.index; 703184610Salfred do { 704194228Sthompsa name = padvance(&path2, argv[i]); 705184610Salfred stunalloc(name); 706194228Sthompsa } while (--j >= 0); 707194228Sthompsa if (cmd == TYPECMD_SMALLV) 708184610Salfred out1fmt("%s\n", name); 709184610Salfred else 710184610Salfred out1fmt("%s is%s %s\n", argv[i], 711184610Salfred (cmdp && cmd == TYPECMD_TYPE) ? 712184610Salfred " a tracked alias for" : "", 713184610Salfred name); 714184610Salfred } else { 715184610Salfred if (eaccess(argv[i], X_OK) == 0) { 716193045Sthompsa if (cmd == TYPECMD_SMALLV) 717194228Sthompsa out1fmt("%s\n", argv[i]); 718192984Sthompsa else 719192984Sthompsa out1fmt("%s is %s\n", argv[i], 720187173Sthompsa argv[i]); 721184610Salfred } else { 722192984Sthompsa if (cmd != TYPECMD_SMALLV) 723192984Sthompsa outfmt(out2, "%s: %s\n", 724192984Sthompsa argv[i], strerror(errno)); 725192984Sthompsa error1 |= 127; 726193644Sthompsa } 727192984Sthompsa } 728192984Sthompsa break; 729184610Salfred } 730184610Salfred case CMDFUNCTION: 731184610Salfred if (cmd == TYPECMD_SMALLV) 732184610Salfred out1fmt("%s\n", argv[i]); 733184610Salfred else 734184610Salfred out1fmt("%s is a shell function\n", argv[i]); 735184610Salfred break; 736184610Salfred 737184610Salfred case CMDBUILTIN: 738194228Sthompsa if (cmd == TYPECMD_SMALLV) 739184610Salfred out1fmt("%s\n", argv[i]); 740184610Salfred else if (entry.special) 741184610Salfred out1fmt("%s is a special shell builtin\n", 742184610Salfred argv[i]); 743184610Salfred else 744184610Salfred out1fmt("%s is a shell builtin\n", argv[i]); 745184610Salfred break; 746184610Salfred 747184610Salfred default: 748184610Salfred if (cmd != TYPECMD_SMALLV) 749184610Salfred outfmt(out2, "%s: not found\n", argv[i]); 750187173Sthompsa error1 |= 127; 751184610Salfred break; 752187173Sthompsa } 753184610Salfred } 754184610Salfred 755184610Salfred if (path != pathval()) 756184610Salfred clearcmdentry(); 757193045Sthompsa 758184610Salfred return error1; 759184610Salfred} 760184610Salfred 761190734Sthompsa/* 762184610Salfred * Locate and print what a word is... 763184610Salfred */ 764184610Salfred 765184610Salfredint 766184610Salfredtypecmd(int argc, char **argv) 767184610Salfred{ 768184610Salfred if (argc > 2 && strcmp(argv[1], "--") == 0) 769184610Salfred argc--, argv++; 770184610Salfred return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); 771184610Salfred} 772184610Salfred