1*d73cf48cSkre /* $NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $ */
249f0ad86Scgd
361f28255Scgd /*-
437ed7877Sjtc * Copyright (c) 1991, 1993
537ed7877Sjtc * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * This code is derived from software contributed to Berkeley by
861f28255Scgd * Kenneth Almquist.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd * may be used to endorse or promote products derived from this software
2061f28255Scgd * without specific prior written permission.
2161f28255Scgd *
2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd * SUCH DAMAGE.
3361f28255Scgd */
3461f28255Scgd
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
38a45947b2Schristos static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
3949f0ad86Scgd #else
40*d73cf48cSkre __RCSID("$NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd
4407bae7edSchristos #include <sys/types.h>
4507bae7edSchristos #include <sys/stat.h>
46edcb4544Schristos #include <sys/wait.h>
4707bae7edSchristos #include <unistd.h>
4807bae7edSchristos #include <fcntl.h>
4907bae7edSchristos #include <errno.h>
50edcb4544Schristos #include <stdio.h>
5107bae7edSchristos #include <stdlib.h>
5207bae7edSchristos
5361f28255Scgd /*
5461f28255Scgd * When commands are first encountered, they are entered in a hash table.
5561f28255Scgd * This ensures that a full path search will not have to be done for them
5661f28255Scgd * on each invocation.
5761f28255Scgd *
5861f28255Scgd * We should investigate converting to a linear search, even though that
5961f28255Scgd * would make the command name "hash" a misnomer.
6061f28255Scgd */
6161f28255Scgd
6261f28255Scgd #include "shell.h"
6361f28255Scgd #include "main.h"
6461f28255Scgd #include "nodes.h"
6561f28255Scgd #include "parser.h"
6661f28255Scgd #include "redir.h"
6761f28255Scgd #include "eval.h"
6861f28255Scgd #include "exec.h"
6961f28255Scgd #include "builtins.h"
7061f28255Scgd #include "var.h"
7161f28255Scgd #include "options.h"
7261f28255Scgd #include "input.h"
7361f28255Scgd #include "output.h"
7461f28255Scgd #include "syntax.h"
7561f28255Scgd #include "memalloc.h"
7661f28255Scgd #include "error.h"
7761f28255Scgd #include "init.h"
7861f28255Scgd #include "mystring.h"
7907bae7edSchristos #include "show.h"
8037ed7877Sjtc #include "jobs.h"
81680690d3Schristos #include "alias.h"
8261f28255Scgd
8361f28255Scgd
8461f28255Scgd #define CMDTABLESIZE 31 /* should be prime */
8561f28255Scgd #define ARB 1 /* actual size determined at run time */
8661f28255Scgd
8761f28255Scgd
8861f28255Scgd
8961f28255Scgd struct tblentry {
9061f28255Scgd struct tblentry *next; /* next entry in hash chain */
9161f28255Scgd union param param; /* definition of builtin function */
9261f28255Scgd short cmdtype; /* index identifying command */
9361f28255Scgd char rehash; /* if set, cd done since entry created */
94727a69dcSkre char fn_ln1; /* for functions, LINENO from 1 */
95727a69dcSkre int lineno; /* for functions abs LINENO of definition */
9661f28255Scgd char cmdname[ARB]; /* name of command */
9761f28255Scgd };
9861f28255Scgd
9961f28255Scgd
10061f28255Scgd STATIC struct tblentry *cmdtable[CMDTABLESIZE];
10161f28255Scgd STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
1022a9c11ddSchristos int exerrno = 0; /* Last exec error */
10361f28255Scgd
10461f28255Scgd
105c02b3bbdSchristos STATIC void tryexec(char *, char **, char **, int);
106c02b3bbdSchristos STATIC void printentry(struct tblentry *, int);
107ddfe7420Schristos STATIC void addcmdentry(char *, struct cmdentry *);
108c02b3bbdSchristos STATIC void clearcmdentry(int);
109c02b3bbdSchristos STATIC struct tblentry *cmdlookup(const char *, int);
110c02b3bbdSchristos STATIC void delete_cmd_entry(void);
11161f28255Scgd
112744c8edcSdholland #ifndef BSD
113744c8edcSdholland STATIC void execinterp(char **, char **);
114744c8edcSdholland #endif
115744c8edcSdholland
11661f28255Scgd
1174498b1feSmatt extern const char *const parsekwd[];
11861f28255Scgd
11961f28255Scgd /*
12061f28255Scgd * Exec a program. Never returns. If you change this routine, you may
12161f28255Scgd * have to change the find_command routine as well.
12261f28255Scgd */
12361f28255Scgd
12461f28255Scgd void
shellexec(char ** argv,char ** envp,const char * path,int idx,int vforked)125c02b3bbdSchristos shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
12661f28255Scgd {
12761f28255Scgd char *cmdname;
12898b2eb36Skre int e, action;
12998b2eb36Skre struct stat statb;
13098b2eb36Skre
13198b2eb36Skre action = E_EXEC;
13261f28255Scgd
13361f28255Scgd if (strchr(argv[0], '/') != NULL) {
134edcb4544Schristos tryexec(argv[0], argv, envp, vforked);
13561f28255Scgd e = errno;
13698b2eb36Skre if (e == EACCES && stat(argv[0], &statb) == -1)
13798b2eb36Skre action = E_OPEN;
13861f28255Scgd } else {
13961f28255Scgd e = ENOENT;
1401676135eSkre while ((cmdname = padvance(&path, argv[0], 1)) != NULL) {
1413d424690Schristos if (--idx < 0 && pathopt == NULL) {
14298b2eb36Skre /*
14398b2eb36Skre * tryexec() does not return if it works.
14498b2eb36Skre */
145edcb4544Schristos tryexec(cmdname, argv, envp, vforked);
14698b2eb36Skre /*
14798b2eb36Skre * If do not already have a meaningful error
14898b2eb36Skre * from earlier in the PATH, examine this one
14998b2eb36Skre * if it is a simple "not found", just keep
15098b2eb36Skre * searching.
15198b2eb36Skre */
15298b2eb36Skre if (e == ENOENT &&
15398b2eb36Skre errno != ENOENT && errno != ENOTDIR) {
15498b2eb36Skre /*
15598b2eb36Skre * If the error is from permission
15698b2eb36Skre * denied on the path search (a call
15798b2eb36Skre * to stat() also fails) ignore it
15898b2eb36Skre * (just continue with the search)
15998b2eb36Skre * If it is EACCESS and the file exists
16098b2eb36Skre * (the stat succeeds) that means no
16198b2eb36Skre * 'x' perm on the file itself, which
16298b2eb36Skre * is a meaningful error, this will be
16398b2eb36Skre * the one reported if no later PATH
16498b2eb36Skre * element actually succeeds.
16598b2eb36Skre */
16698b2eb36Skre if (errno == EACCES) {
16798b2eb36Skre if (stat(cmdname, &statb) != -1)
16898b2eb36Skre e = EACCES;
16998b2eb36Skre } else {
17098b2eb36Skre /*
17198b2eb36Skre * any other error we will
17298b2eb36Skre * remember as the significant
17398b2eb36Skre * error
17498b2eb36Skre */
17561f28255Scgd e = errno;
17661f28255Scgd }
17798b2eb36Skre }
17898b2eb36Skre }
17961f28255Scgd stunalloc(cmdname);
18061f28255Scgd }
18161f28255Scgd }
1822a9c11ddSchristos
1832a9c11ddSchristos /* Map to POSIX errors */
1842a9c11ddSchristos switch (e) {
1859006b741Skre case EACCES: /* particularly this (unless no search perm) */
18698b2eb36Skre if (action == E_OPEN) {
1879006b741Skre /*
18898b2eb36Skre * this is an EACCES from namei
18998b2eb36Skre * ie: no permission to search the path given
19098b2eb36Skre * rather than an EACCESS from exec
19198b2eb36Skre * ie: no 'x' bit on the file to be executed
1929006b741Skre */
19398b2eb36Skre exerrno = 127;
19498b2eb36Skre break;
19598b2eb36Skre }
19698b2eb36Skre /* FALLTHROUGH */
1979006b741Skre case EINVAL: /* also explicitly these */
1989006b741Skre case ENOEXEC:
1999006b741Skre default: /* and anything else */
2002a9c11ddSchristos exerrno = 126;
2012a9c11ddSchristos break;
2029006b741Skre
2039006b741Skre case ENOENT: /* these are the "pathname lookup failed" errors */
2049006b741Skre case ELOOP:
2059006b741Skre case ENOTDIR:
2069006b741Skre case ENAMETOOLONG:
2072a9c11ddSchristos exerrno = 127;
2082a9c11ddSchristos break;
2092a9c11ddSchristos }
2109006b741Skre CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL,
2119006b741Skre ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
212c02b3bbdSchristos argv[0], e, vforked, suppressint));
21398b2eb36Skre exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, action));
214ee9e50eaSmycroft /* NOTREACHED */
21561f28255Scgd }
21661f28255Scgd
21761f28255Scgd
21861f28255Scgd STATIC void
tryexec(char * cmd,char ** argv,char ** envp,int vforked)219c02b3bbdSchristos tryexec(char *cmd, char **argv, char **envp, int vforked)
22061f28255Scgd {
22161f28255Scgd int e;
22207bae7edSchristos #ifndef BSD
22361f28255Scgd char *p;
22407bae7edSchristos #endif
22561f28255Scgd
22661f28255Scgd #ifdef SYSV
22761f28255Scgd do {
22861f28255Scgd execve(cmd, argv, envp);
22961f28255Scgd } while (errno == EINTR);
23061f28255Scgd #else
23161f28255Scgd execve(cmd, argv, envp);
23261f28255Scgd #endif
23361f28255Scgd e = errno;
23461f28255Scgd if (e == ENOEXEC) {
235edcb4544Schristos if (vforked) {
236edcb4544Schristos /* We are currently vfork(2)ed, so raise an
237edcb4544Schristos * exception, and evalcommand will try again
238edcb4544Schristos * with a normal fork(2).
239edcb4544Schristos */
240edcb4544Schristos exraise(EXSHELLPROC);
241edcb4544Schristos }
2422554aff2Schristos #ifdef DEBUG
2439006b741Skre VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd));
2442554aff2Schristos #endif
24561f28255Scgd initshellproc();
24661f28255Scgd setinputfile(cmd, 0);
24761f28255Scgd commandname = arg0 = savestr(argv[0]);
24861f28255Scgd #ifndef BSD
24961f28255Scgd pgetc(); pungetc(); /* fill up input buffer */
25061f28255Scgd p = parsenextc;
25161f28255Scgd if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
25261f28255Scgd argv[0] = cmd;
25361f28255Scgd execinterp(argv, envp);
25461f28255Scgd }
25561f28255Scgd #endif
25661f28255Scgd setparam(argv + 1);
25761f28255Scgd exraise(EXSHELLPROC);
25861f28255Scgd }
25961f28255Scgd errno = e;
26061f28255Scgd }
26161f28255Scgd
26261f28255Scgd
26361f28255Scgd #ifndef BSD
26461f28255Scgd /*
26561f28255Scgd * Execute an interpreter introduced by "#!", for systems where this
26661f28255Scgd * feature has not been built into the kernel. If the interpreter is
26761f28255Scgd * the shell, return (effectively ignoring the "#!"). If the execution
26861f28255Scgd * of the interpreter fails, exit.
26961f28255Scgd *
27061f28255Scgd * This code peeks inside the input buffer in order to avoid actually
27161f28255Scgd * reading any input. It would benefit from a rewrite.
27261f28255Scgd */
27361f28255Scgd
27461f28255Scgd #define NEWARGS 5
27561f28255Scgd
27661f28255Scgd STATIC void
execinterp(char ** argv,char ** envp)277c02b3bbdSchristos execinterp(char **argv, char **envp)
27861f28255Scgd {
27961f28255Scgd int n;
28061f28255Scgd char *inp;
28161f28255Scgd char *outp;
28261f28255Scgd char c;
28361f28255Scgd char *p;
28461f28255Scgd char **ap;
28561f28255Scgd char *newargs[NEWARGS];
28661f28255Scgd int i;
28761f28255Scgd char **ap2;
28861f28255Scgd char **new;
28961f28255Scgd
29061f28255Scgd n = parsenleft - 2;
29161f28255Scgd inp = parsenextc + 2;
29261f28255Scgd ap = newargs;
29361f28255Scgd for (;;) {
29461f28255Scgd while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
29561f28255Scgd inp++;
29661f28255Scgd if (n < 0)
29761f28255Scgd goto bad;
29861f28255Scgd if ((c = *inp++) == '\n')
29961f28255Scgd break;
30061f28255Scgd if (ap == &newargs[NEWARGS])
30161f28255Scgd bad: error("Bad #! line");
30261f28255Scgd STARTSTACKSTR(outp);
30361f28255Scgd do {
30461f28255Scgd STPUTC(c, outp);
30561f28255Scgd } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
30661f28255Scgd STPUTC('\0', outp);
30761f28255Scgd n++, inp--;
30861f28255Scgd *ap++ = grabstackstr(outp);
30961f28255Scgd }
31061f28255Scgd if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
31161f28255Scgd p = newargs[0];
31261f28255Scgd for (;;) {
31361f28255Scgd if (equal(p, "sh") || equal(p, "ash")) {
31461f28255Scgd return;
31561f28255Scgd }
31661f28255Scgd while (*p != '/') {
31761f28255Scgd if (*p == '\0')
31861f28255Scgd goto break2;
31961f28255Scgd p++;
32061f28255Scgd }
32161f28255Scgd p++;
32261f28255Scgd }
32361f28255Scgd break2:;
32461f28255Scgd }
32561f28255Scgd i = (char *)ap - (char *)newargs; /* size in bytes */
32661f28255Scgd if (i == 0)
32761f28255Scgd error("Bad #! line");
32861f28255Scgd for (ap2 = argv ; *ap2++ != NULL ; );
32961f28255Scgd new = ckmalloc(i + ((char *)ap2 - (char *)argv));
33061f28255Scgd ap = newargs, ap2 = new;
33161f28255Scgd while ((i -= sizeof (char **)) >= 0)
33261f28255Scgd *ap2++ = *ap++;
33361f28255Scgd ap = argv;
33461f28255Scgd while (*ap2++ = *ap++);
33561f28255Scgd shellexec(new, envp, pathval(), 0);
336ee9e50eaSmycroft /* NOTREACHED */
33761f28255Scgd }
33861f28255Scgd #endif
33961f28255Scgd
34061f28255Scgd
34161f28255Scgd
34261f28255Scgd /*
34361f28255Scgd * Do a path search. The variable path (passed by reference) should be
34461f28255Scgd * set to the start of the path before the first call; padvance will update
34561f28255Scgd * this value as it proceeds. Successive calls to padvance will return
34661f28255Scgd * the possible path expansions in sequence. If an option (indicated by
34761f28255Scgd * a percent sign) appears in the path entry then the global variable
34861f28255Scgd * pathopt will be set to point to it; otherwise pathopt will be set to
34961f28255Scgd * NULL.
35061f28255Scgd */
35161f28255Scgd
3523d424690Schristos const char *pathopt;
35361f28255Scgd
35461f28255Scgd char *
padvance(const char ** path,const char * name,int magic_percent)3551676135eSkre padvance(const char **path, const char *name, int magic_percent)
35661f28255Scgd {
3573d424690Schristos const char *p;
3583d424690Schristos char *q;
3593d424690Schristos const char *start;
36061f28255Scgd int len;
36161f28255Scgd
36261f28255Scgd if (*path == NULL)
36361f28255Scgd return NULL;
3641676135eSkre if (magic_percent)
3651676135eSkre magic_percent = '%';
3661676135eSkre
36761f28255Scgd start = *path;
3681676135eSkre for (p = start ; *p && *p != ':' && *p != magic_percent ; p++)
3691676135eSkre ;
37061f28255Scgd len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
37161f28255Scgd while (stackblocksize() < len)
37261f28255Scgd growstackblock();
37361f28255Scgd q = stackblock();
37461f28255Scgd if (p != start) {
37506f53b68Smycroft memcpy(q, start, p - start);
37661f28255Scgd q += p - start;
3771676135eSkre if (q[-1] != '/')
37861f28255Scgd *q++ = '/';
37961f28255Scgd }
38061f28255Scgd strcpy(q, name);
38161f28255Scgd pathopt = NULL;
3821676135eSkre if (*p == magic_percent) {
38361f28255Scgd pathopt = ++p;
3841676135eSkre while (*p && *p != ':')
3851676135eSkre p++;
38661f28255Scgd }
38761f28255Scgd if (*p == ':')
38861f28255Scgd *path = p + 1;
38961f28255Scgd else
39061f28255Scgd *path = NULL;
391ee3b307fSkre return grabstackstr(q + strlen(name) + 1);
39261f28255Scgd }
39361f28255Scgd
39461f28255Scgd
39561f28255Scgd /*** Command hashing code ***/
39661f28255Scgd
39761f28255Scgd
3984ce0d34aScgd int
hashcmd(int argc,char ** argv)399c02b3bbdSchristos hashcmd(int argc, char **argv)
4004ce0d34aScgd {
40161f28255Scgd struct tblentry **pp;
40261f28255Scgd struct tblentry *cmdp;
40361f28255Scgd int c;
40461f28255Scgd struct cmdentry entry;
40561f28255Scgd char *name;
40645597480Skre int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0;
4078ea2ac47Skre int errs=1, emsg=DO_ERR;
4088ea2ac47Skre int status = 0;
40961f28255Scgd
4108ea2ac47Skre while ((c = nextopt("bcefqrsuv")) != '\0')
41145597480Skre switch (c) {
41245597480Skre case 'b': bopt = 1; break;
41345597480Skre case 'c': uopt = 1; break; /* c == u */
4148ea2ac47Skre case 'e': errs = 0; break;
41545597480Skre case 'f': fopt = 1; break;
4168ea2ac47Skre case 'q': emsg = 0; break;
41745597480Skre case 'r': ropt = 1; break;
41845597480Skre case 's': sopt = 1; break;
41945597480Skre case 'u': uopt = 1; break;
42045597480Skre case 'v': verbose = 1; break;
42145597480Skre }
42245597480Skre
4238ea2ac47Skre if (!errs)
4248ea2ac47Skre emsg ^= DO_ERR;
4258ea2ac47Skre
42645597480Skre if (ropt)
42761f28255Scgd clearcmdentry(0);
42845597480Skre
42945597480Skre if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0)
43045597480Skre allopt = bopt = fopt = sopt = uopt = 1;
43145597480Skre
43237ed7877Sjtc if (*argptr == NULL) {
43337ed7877Sjtc for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
43437ed7877Sjtc for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
43545597480Skre switch (cmdp->cmdtype) {
43645597480Skre case CMDNORMAL:
43745597480Skre if (!uopt)
43845597480Skre continue;
43945597480Skre break;
44045597480Skre case CMDBUILTIN:
44145597480Skre if (!bopt)
44245597480Skre continue;
44345597480Skre break;
44445597480Skre case CMDSPLBLTIN:
44545597480Skre if (!sopt)
44645597480Skre continue;
44745597480Skre break;
44845597480Skre case CMDFUNCTION:
44945597480Skre if (!fopt)
45045597480Skre continue;
45145597480Skre break;
45245597480Skre default: /* never happens */
45345597480Skre continue;
45445597480Skre }
45545597480Skre if (!allopt || verbose ||
45645597480Skre cmdp->cmdtype == CMDNORMAL)
45737ed7877Sjtc printentry(cmdp, verbose);
45837ed7877Sjtc }
45937ed7877Sjtc }
4608ea2ac47Skre flushout(out1);
4618ea2ac47Skre if (io_err(out1)) {
4628ea2ac47Skre out2str("hash: I/O error writing to standard output\n");
4638ea2ac47Skre return 1;
4648ea2ac47Skre }
46537ed7877Sjtc return 0;
46637ed7877Sjtc }
46745597480Skre
46845597480Skre while ((name = *argptr++) != NULL) {
46945597480Skre if ((cmdp = cmdlookup(name, 0)) != NULL) {
47045597480Skre switch (cmdp->cmdtype) {
47145597480Skre case CMDNORMAL:
47245597480Skre if (!uopt)
47345597480Skre continue;
47461f28255Scgd delete_cmd_entry();
47545597480Skre break;
47645597480Skre case CMDBUILTIN:
47745597480Skre if (!bopt)
47845597480Skre continue;
47945597480Skre if (builtinloc >= 0)
48045597480Skre delete_cmd_entry();
48145597480Skre break;
48245597480Skre case CMDSPLBLTIN:
48345597480Skre if (!sopt)
48445597480Skre continue;
48545597480Skre break;
48645597480Skre case CMDFUNCTION:
48745597480Skre if (!fopt)
48845597480Skre continue;
48945597480Skre break;
49045597480Skre }
49145597480Skre }
4928ea2ac47Skre find_command(name, &entry, emsg, pathval());
4938ea2ac47Skre if (errs && entry.cmdtype == CMDUNKNOWN)
4948ea2ac47Skre status = 1;
49561f28255Scgd if (verbose) {
49661f28255Scgd if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
49761f28255Scgd cmdp = cmdlookup(name, 0);
498169a2694Schristos if (cmdp != NULL)
49937ed7877Sjtc printentry(cmdp, verbose);
50061f28255Scgd }
50161f28255Scgd flushall();
50261f28255Scgd }
50361f28255Scgd }
5048ea2ac47Skre return status;
50561f28255Scgd }
50661f28255Scgd
50761f28255Scgd STATIC void
printentry(struct tblentry * cmdp,int verbose)508c02b3bbdSchristos printentry(struct tblentry *cmdp, int verbose)
50961f28255Scgd {
5103d424690Schristos int idx;
5113d424690Schristos const char *path;
51261f28255Scgd char *name;
51361f28255Scgd
514c02b3bbdSchristos switch (cmdp->cmdtype) {
515c02b3bbdSchristos case CMDNORMAL:
5163d424690Schristos idx = cmdp->param.index;
51761f28255Scgd path = pathval();
51861f28255Scgd do {
5191676135eSkre name = padvance(&path, cmdp->cmdname, 1);
52061f28255Scgd stunalloc(name);
5213d424690Schristos } while (--idx >= 0);
52245597480Skre if (verbose)
52345597480Skre out1fmt("Command from PATH[%d]: ",
52445597480Skre cmdp->param.index);
52561f28255Scgd out1str(name);
526c02b3bbdSchristos break;
527c02b3bbdSchristos case CMDSPLBLTIN:
52845597480Skre if (verbose)
52945597480Skre out1str("special ");
53045597480Skre /* FALLTHROUGH */
531c02b3bbdSchristos case CMDBUILTIN:
53245597480Skre if (verbose)
53345597480Skre out1str("builtin ");
53445597480Skre out1fmt("%s", cmdp->cmdname);
535c02b3bbdSchristos break;
536c02b3bbdSchristos case CMDFUNCTION:
53745597480Skre if (verbose)
53845597480Skre out1str("function ");
53945597480Skre out1fmt("%s", cmdp->cmdname);
54037ed7877Sjtc if (verbose) {
541c02b3bbdSchristos struct procstat ps;
542c7c0722aSkre
54337ed7877Sjtc INTOFF;
544c7c0722aSkre commandtext(&ps, getfuncnode(cmdp->param.func));
54537ed7877Sjtc INTON;
54651d94f21Sdsl out1str("() { ");
54751d94f21Sdsl out1str(ps.cmd);
54851d94f21Sdsl out1str("; }");
54937ed7877Sjtc }
550c02b3bbdSchristos break;
551c02b3bbdSchristos default:
55245597480Skre error("internal error: %s cmdtype %d",
55345597480Skre cmdp->cmdname, cmdp->cmdtype);
55461f28255Scgd }
55561f28255Scgd if (cmdp->rehash)
55661f28255Scgd out1c('*');
55761f28255Scgd out1c('\n');
55861f28255Scgd }
55961f28255Scgd
56061f28255Scgd
56161f28255Scgd
56261f28255Scgd /*
56361f28255Scgd * Resolve a command name. If you change this routine, you may have to
56461f28255Scgd * change the shellexec routine as well.
56561f28255Scgd */
56661f28255Scgd
56761f28255Scgd void
find_command(char * name,struct cmdentry * entry,int act,const char * path)568c02b3bbdSchristos find_command(char *name, struct cmdentry *entry, int act, const char *path)
56961f28255Scgd {
570e314f958Sdsl struct tblentry *cmdp, loc_cmd;
5713d424690Schristos int idx;
57261f28255Scgd int prev;
57361f28255Scgd char *fullname;
57461f28255Scgd struct stat statb;
57561f28255Scgd int e;
576c02b3bbdSchristos int (*bltin)(int,char **);
57761f28255Scgd
578e314f958Sdsl /* If name contains a slash, don't use PATH or hash table */
57961f28255Scgd if (strchr(name, '/') != NULL) {
580c996803cSchristos if (act & DO_ABS) {
581c996803cSchristos while (stat(name, &statb) < 0) {
582c996803cSchristos #ifdef SYSV
583c996803cSchristos if (errno == EINTR)
584c996803cSchristos continue;
585c996803cSchristos #endif
586c996803cSchristos if (errno != ENOENT && errno != ENOTDIR)
587c996803cSchristos e = errno;
588c996803cSchristos entry->cmdtype = CMDUNKNOWN;
589c996803cSchristos entry->u.index = -1;
590c996803cSchristos return;
591c996803cSchristos }
592c996803cSchristos entry->cmdtype = CMDNORMAL;
593c996803cSchristos entry->u.index = -1;
594c996803cSchristos return;
595c996803cSchristos }
59661f28255Scgd entry->cmdtype = CMDNORMAL;
59761f28255Scgd entry->u.index = 0;
59861f28255Scgd return;
59961f28255Scgd }
60061f28255Scgd
601e314f958Sdsl if (path != pathval())
602e314f958Sdsl act |= DO_ALTPATH;
603e314f958Sdsl
604e314f958Sdsl if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
605e314f958Sdsl act |= DO_ALTBLTIN;
606e314f958Sdsl
607e314f958Sdsl /* If name is in the table, check answer will be ok */
608e314f958Sdsl if ((cmdp = cmdlookup(name, 0)) != NULL) {
609e314f958Sdsl switch (cmdp->cmdtype) {
610e314f958Sdsl case CMDNORMAL:
61116296115Srillig if (act & DO_ALTPATH)
612e314f958Sdsl cmdp = NULL;
613e314f958Sdsl break;
614e314f958Sdsl case CMDFUNCTION:
61516296115Srillig if (act & DO_NOFUNC)
616e314f958Sdsl cmdp = NULL;
617e314f958Sdsl break;
618e314f958Sdsl case CMDBUILTIN:
61916296115Srillig if ((act & DO_ALTBLTIN) || builtinloc >= 0)
620e314f958Sdsl cmdp = NULL;
621e314f958Sdsl break;
622e314f958Sdsl }
623e314f958Sdsl /* if not invalidated by cd, we're done */
62416296115Srillig if (cmdp != NULL && cmdp->rehash == 0)
62561f28255Scgd goto success;
626e314f958Sdsl }
62761f28255Scgd
62861f28255Scgd /* If %builtin not in path, check for builtin next */
629e314f958Sdsl if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
630e314f958Sdsl (bltin = find_builtin(name)) != 0)
631e314f958Sdsl goto builtin_success;
63261f28255Scgd
63361f28255Scgd /* We have to search path. */
63461f28255Scgd prev = -1; /* where to start */
63561f28255Scgd if (cmdp) { /* doing a rehash */
63661f28255Scgd if (cmdp->cmdtype == CMDBUILTIN)
63761f28255Scgd prev = builtinloc;
63861f28255Scgd else
63961f28255Scgd prev = cmdp->param.index;
64061f28255Scgd }
64161f28255Scgd
64261f28255Scgd e = ENOENT;
6433d424690Schristos idx = -1;
64461f28255Scgd loop:
6451676135eSkre while ((fullname = padvance(&path, name, 1)) != NULL) {
64661f28255Scgd stunalloc(fullname);
6473d424690Schristos idx++;
64861f28255Scgd if (pathopt) {
64961f28255Scgd if (prefix("builtin", pathopt)) {
650c02b3bbdSchristos if ((bltin = find_builtin(name)) == 0)
65161f28255Scgd goto loop;
652e314f958Sdsl goto builtin_success;
65361f28255Scgd } else if (prefix("func", pathopt)) {
65461f28255Scgd /* handled below */
65561f28255Scgd } else {
656e314f958Sdsl /* ignore unimplemented options */
657e314f958Sdsl goto loop;
65861f28255Scgd }
65961f28255Scgd }
66061f28255Scgd /* if rehash, don't redo absolute path names */
6613d424690Schristos if (fullname[0] == '/' && idx <= prev) {
6623d424690Schristos if (idx < prev)
66361f28255Scgd goto loop;
6649006b741Skre VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n",
6659006b741Skre name));
66661f28255Scgd goto success;
66761f28255Scgd }
66861f28255Scgd while (stat(fullname, &statb) < 0) {
66961f28255Scgd #ifdef SYSV
67061f28255Scgd if (errno == EINTR)
67161f28255Scgd continue;
67261f28255Scgd #endif
67361f28255Scgd if (errno != ENOENT && errno != ENOTDIR)
67461f28255Scgd e = errno;
67561f28255Scgd goto loop;
67661f28255Scgd }
67761f28255Scgd e = EACCES; /* if we fail, this will be the error */
678f5ad44b6Smycroft if (!S_ISREG(statb.st_mode))
67961f28255Scgd goto loop;
68061f28255Scgd if (pathopt) { /* this is a %func directory */
681ee3b307fSkre char *endname;
682ee3b307fSkre
683e314f958Sdsl if (act & DO_NOFUNC)
684e314f958Sdsl goto loop;
685ee3b307fSkre endname = fullname + strlen(fullname) + 1;
686ee3b307fSkre grabstackstr(endname);
68761f28255Scgd readcmdfile(fullname);
688e314f958Sdsl if ((cmdp = cmdlookup(name, 0)) == NULL ||
689e314f958Sdsl cmdp->cmdtype != CMDFUNCTION)
69061f28255Scgd error("%s not defined in %s", name, fullname);
691ee3b307fSkre ungrabstackstr(fullname, endname);
69261f28255Scgd goto success;
69361f28255Scgd }
69437ed7877Sjtc #ifdef notdef
695e314f958Sdsl /* XXX this code stops root executing stuff, and is buggy
696e314f958Sdsl if you need a group from the group list. */
69737ed7877Sjtc if (statb.st_uid == geteuid()) {
69861f28255Scgd if ((statb.st_mode & 0100) == 0)
69961f28255Scgd goto loop;
70061f28255Scgd } else if (statb.st_gid == getegid()) {
70161f28255Scgd if ((statb.st_mode & 010) == 0)
70261f28255Scgd goto loop;
70361f28255Scgd } else {
70437ed7877Sjtc if ((statb.st_mode & 01) == 0)
70561f28255Scgd goto loop;
70661f28255Scgd }
707a3a1e404Scgd #endif
7089006b741Skre VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name,
7099006b741Skre fullname));
71061f28255Scgd INTOFF;
711e314f958Sdsl if (act & DO_ALTPATH) {
712ee3b307fSkre /*
713ee3b307fSkre * this should be a grabstackstr() but is not needed:
714ee3b307fSkre * fullname is no longer needed for anything
715e314f958Sdsl stalloc(strlen(fullname) + 1);
716ee3b307fSkre */
717e314f958Sdsl cmdp = &loc_cmd;
718e314f958Sdsl } else
71961f28255Scgd cmdp = cmdlookup(name, 1);
720b7625640Skre
721b7625640Skre if (cmdp->cmdtype == CMDFUNCTION)
722b7625640Skre cmdp = &loc_cmd;
723b7625640Skre
72461f28255Scgd cmdp->cmdtype = CMDNORMAL;
7253d424690Schristos cmdp->param.index = idx;
72661f28255Scgd INTON;
72761f28255Scgd goto success;
72861f28255Scgd }
72961f28255Scgd
73061f28255Scgd /* We failed. If there was an entry for this command, delete it */
73161f28255Scgd if (cmdp)
73261f28255Scgd delete_cmd_entry();
733c996803cSchristos if (act & DO_ERR)
734e6132242Sabrown outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
73561f28255Scgd entry->cmdtype = CMDUNKNOWN;
736f49e0d69Skre entry->u.index = idx + 1;
73761f28255Scgd return;
73861f28255Scgd
739e314f958Sdsl builtin_success:
740e314f958Sdsl INTOFF;
741e314f958Sdsl if (act & DO_ALTPATH)
742e314f958Sdsl cmdp = &loc_cmd;
743e314f958Sdsl else
744e314f958Sdsl cmdp = cmdlookup(name, 1);
74551d94f21Sdsl if (cmdp->cmdtype == CMDFUNCTION)
74651d94f21Sdsl /* DO_NOFUNC must have been set */
74751d94f21Sdsl cmdp = &loc_cmd;
748e314f958Sdsl cmdp->cmdtype = CMDBUILTIN;
749e314f958Sdsl cmdp->param.bltin = bltin;
750e314f958Sdsl INTON;
75161f28255Scgd success:
752f6828859Schristos if (cmdp) {
75361f28255Scgd cmdp->rehash = 0;
75461f28255Scgd entry->cmdtype = cmdp->cmdtype;
755727a69dcSkre entry->lineno = cmdp->lineno;
756727a69dcSkre entry->lno_frel = cmdp->fn_ln1;
75761f28255Scgd entry->u = cmdp->param;
758f49e0d69Skre } else {
759f6828859Schristos entry->cmdtype = CMDUNKNOWN;
760f49e0d69Skre entry->u.index = -1;
761f49e0d69Skre }
76261f28255Scgd }
76361f28255Scgd
76461f28255Scgd
76561f28255Scgd
76661f28255Scgd /*
76761f28255Scgd * Search the table of builtin commands.
76861f28255Scgd */
76961f28255Scgd
77061f28255Scgd int
find_builtin(char * name)771da4f7877Smatt (*find_builtin(char *name))(int, char **)
77261f28255Scgd {
77348250187Stls const struct builtincmd *bp;
77461f28255Scgd
77561f28255Scgd for (bp = builtincmd ; bp->name ; bp++) {
776658a58d0Sdsl if (*bp->name == *name
777658a58d0Sdsl && (*name == '%' || equal(bp->name, name)))
778c02b3bbdSchristos return bp->builtin;
77961f28255Scgd }
780c02b3bbdSchristos return 0;
781c02b3bbdSchristos }
782c02b3bbdSchristos
783c02b3bbdSchristos int
find_splbltin(char * name)784da4f7877Smatt (*find_splbltin(char *name))(int, char **)
785c02b3bbdSchristos {
786c02b3bbdSchristos const struct builtincmd *bp;
787c02b3bbdSchristos
788c02b3bbdSchristos for (bp = splbltincmd ; bp->name ; bp++) {
789c02b3bbdSchristos if (*bp->name == *name && equal(bp->name, name))
790c02b3bbdSchristos return bp->builtin;
791c02b3bbdSchristos }
792c02b3bbdSchristos return 0;
793c02b3bbdSchristos }
794c02b3bbdSchristos
795c02b3bbdSchristos /*
796c02b3bbdSchristos * At shell startup put special builtins into hash table.
797c02b3bbdSchristos * ensures they are executed first (see posix).
798c02b3bbdSchristos * We stop functions being added with the same name
799c02b3bbdSchristos * (as they are impossible to call)
800c02b3bbdSchristos */
801c02b3bbdSchristos
802c02b3bbdSchristos void
hash_special_builtins(void)803c02b3bbdSchristos hash_special_builtins(void)
804c02b3bbdSchristos {
805c02b3bbdSchristos const struct builtincmd *bp;
806c02b3bbdSchristos struct tblentry *cmdp;
807c02b3bbdSchristos
808c02b3bbdSchristos for (bp = splbltincmd ; bp->name ; bp++) {
809c02b3bbdSchristos cmdp = cmdlookup(bp->name, 1);
810c02b3bbdSchristos cmdp->cmdtype = CMDSPLBLTIN;
811c02b3bbdSchristos cmdp->param.bltin = bp->builtin;
812c02b3bbdSchristos }
81361f28255Scgd }
81461f28255Scgd
81561f28255Scgd
81661f28255Scgd
81761f28255Scgd /*
81861f28255Scgd * Called when a cd is done. Marks all commands so the next time they
81961f28255Scgd * are executed they will be rehashed.
82061f28255Scgd */
82161f28255Scgd
82261f28255Scgd void
hashcd(void)823c02b3bbdSchristos hashcd(void)
824c02b3bbdSchristos {
82561f28255Scgd struct tblentry **pp;
82661f28255Scgd struct tblentry *cmdp;
82761f28255Scgd
82861f28255Scgd for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
82961f28255Scgd for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
83061f28255Scgd if (cmdp->cmdtype == CMDNORMAL
83107bae7edSchristos || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
83261f28255Scgd cmdp->rehash = 1;
83361f28255Scgd }
83461f28255Scgd }
83561f28255Scgd }
83661f28255Scgd
83761f28255Scgd
83861f28255Scgd
83961f28255Scgd /*
840e314f958Sdsl * Fix command hash table when PATH changed.
84161f28255Scgd * Called before PATH is changed. The argument is the new value of PATH;
842e314f958Sdsl * pathval() still returns the old value at this point.
843e314f958Sdsl * Called with interrupts off.
84461f28255Scgd */
84561f28255Scgd
84661f28255Scgd void
changepath(char * newval,int flags __unused)847*d73cf48cSkre changepath(char *newval, int flags __unused)
84861f28255Scgd {
8499a738ef2Schristos const char *old, *new;
8503d424690Schristos int idx;
85161f28255Scgd int firstchange;
85261f28255Scgd int bltin;
85361f28255Scgd
85461f28255Scgd old = pathval();
85561f28255Scgd new = newval;
85661f28255Scgd firstchange = 9999; /* assume no change */
8573d424690Schristos idx = 0;
85861f28255Scgd bltin = -1;
85961f28255Scgd for (;;) {
86061f28255Scgd if (*old != *new) {
8613d424690Schristos firstchange = idx;
86207bae7edSchristos if ((*old == '\0' && *new == ':')
86307bae7edSchristos || (*old == ':' && *new == '\0'))
86461f28255Scgd firstchange++;
86561f28255Scgd old = new; /* ignore subsequent differences */
86661f28255Scgd }
86761f28255Scgd if (*new == '\0')
86861f28255Scgd break;
86961f28255Scgd if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
8703d424690Schristos bltin = idx;
87161f28255Scgd if (*new == ':') {
8723d424690Schristos idx++;
87361f28255Scgd }
87461f28255Scgd new++, old++;
87561f28255Scgd }
87661f28255Scgd if (builtinloc < 0 && bltin >= 0)
87761f28255Scgd builtinloc = bltin; /* zap builtins */
87861f28255Scgd if (builtinloc >= 0 && bltin < 0)
87961f28255Scgd firstchange = 0;
88061f28255Scgd clearcmdentry(firstchange);
88161f28255Scgd builtinloc = bltin;
88261f28255Scgd }
88361f28255Scgd
88461f28255Scgd
88561f28255Scgd /*
88661f28255Scgd * Clear out command entries. The argument specifies the first entry in
88761f28255Scgd * PATH which has changed.
88861f28255Scgd */
88961f28255Scgd
89061f28255Scgd STATIC void
clearcmdentry(int firstchange)891c02b3bbdSchristos clearcmdentry(int firstchange)
8924ce0d34aScgd {
89361f28255Scgd struct tblentry **tblp;
89461f28255Scgd struct tblentry **pp;
89561f28255Scgd struct tblentry *cmdp;
89661f28255Scgd
89761f28255Scgd INTOFF;
89861f28255Scgd for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
89961f28255Scgd pp = tblp;
90061f28255Scgd while ((cmdp = *pp) != NULL) {
90107bae7edSchristos if ((cmdp->cmdtype == CMDNORMAL &&
90207bae7edSchristos cmdp->param.index >= firstchange)
90307bae7edSchristos || (cmdp->cmdtype == CMDBUILTIN &&
90407bae7edSchristos builtinloc >= firstchange)) {
90561f28255Scgd *pp = cmdp->next;
90661f28255Scgd ckfree(cmdp);
90761f28255Scgd } else {
90861f28255Scgd pp = &cmdp->next;
90961f28255Scgd }
91061f28255Scgd }
91161f28255Scgd }
91261f28255Scgd INTON;
91361f28255Scgd }
91461f28255Scgd
91561f28255Scgd
91661f28255Scgd /*
91761f28255Scgd * Delete all functions.
91861f28255Scgd */
91961f28255Scgd
92061f28255Scgd #ifdef mkinit
921c02b3bbdSchristos MKINIT void deletefuncs(void);
922c02b3bbdSchristos MKINIT void hash_special_builtins(void);
923c02b3bbdSchristos
924c02b3bbdSchristos INIT {
925c02b3bbdSchristos hash_special_builtins();
926c02b3bbdSchristos }
92761f28255Scgd
92861f28255Scgd SHELLPROC {
92961f28255Scgd deletefuncs();
93061f28255Scgd }
93161f28255Scgd #endif
93261f28255Scgd
93361f28255Scgd void
deletefuncs(void)934c02b3bbdSchristos deletefuncs(void)
935c02b3bbdSchristos {
93661f28255Scgd struct tblentry **tblp;
93761f28255Scgd struct tblentry **pp;
93861f28255Scgd struct tblentry *cmdp;
93961f28255Scgd
94061f28255Scgd INTOFF;
94161f28255Scgd for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
94261f28255Scgd pp = tblp;
94361f28255Scgd while ((cmdp = *pp) != NULL) {
94461f28255Scgd if (cmdp->cmdtype == CMDFUNCTION) {
94561f28255Scgd *pp = cmdp->next;
94661f28255Scgd freefunc(cmdp->param.func);
94761f28255Scgd ckfree(cmdp);
94861f28255Scgd } else {
94961f28255Scgd pp = &cmdp->next;
95061f28255Scgd }
95161f28255Scgd }
95261f28255Scgd }
95361f28255Scgd INTON;
95461f28255Scgd }
95561f28255Scgd
95661f28255Scgd
95761f28255Scgd
95861f28255Scgd /*
95961f28255Scgd * Locate a command in the command hash table. If "add" is nonzero,
96061f28255Scgd * add the command to the table if it is not already present. The
96161f28255Scgd * variable "lastcmdentry" is set to point to the address of the link
96261f28255Scgd * pointing to the entry, so that delete_cmd_entry can delete the
96361f28255Scgd * entry.
96461f28255Scgd */
96561f28255Scgd
96661f28255Scgd struct tblentry **lastcmdentry;
96761f28255Scgd
96861f28255Scgd
96961f28255Scgd STATIC struct tblentry *
cmdlookup(const char * name,int add)970c02b3bbdSchristos cmdlookup(const char *name, int add)
97161f28255Scgd {
97261f28255Scgd int hashval;
973c02b3bbdSchristos const char *p;
97461f28255Scgd struct tblentry *cmdp;
97561f28255Scgd struct tblentry **pp;
97661f28255Scgd
97761f28255Scgd p = name;
97861f28255Scgd hashval = *p << 4;
97961f28255Scgd while (*p)
98061f28255Scgd hashval += *p++;
98161f28255Scgd hashval &= 0x7FFF;
98261f28255Scgd pp = &cmdtable[hashval % CMDTABLESIZE];
98361f28255Scgd for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
98461f28255Scgd if (equal(cmdp->cmdname, name))
98561f28255Scgd break;
98661f28255Scgd pp = &cmdp->next;
98761f28255Scgd }
98861f28255Scgd if (add && cmdp == NULL) {
98961f28255Scgd INTOFF;
99061f28255Scgd cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
99161f28255Scgd + strlen(name) + 1);
99261f28255Scgd cmdp->next = NULL;
99361f28255Scgd cmdp->cmdtype = CMDUNKNOWN;
99461f28255Scgd cmdp->rehash = 0;
99561f28255Scgd strcpy(cmdp->cmdname, name);
99661f28255Scgd INTON;
99761f28255Scgd }
99861f28255Scgd lastcmdentry = pp;
99961f28255Scgd return cmdp;
100061f28255Scgd }
100161f28255Scgd
100261f28255Scgd /*
100361f28255Scgd * Delete the command entry returned on the last lookup.
100461f28255Scgd */
100561f28255Scgd
100661f28255Scgd STATIC void
delete_cmd_entry(void)1007c02b3bbdSchristos delete_cmd_entry(void)
1008c02b3bbdSchristos {
100961f28255Scgd struct tblentry *cmdp;
101061f28255Scgd
101161f28255Scgd INTOFF;
101261f28255Scgd cmdp = *lastcmdentry;
101361f28255Scgd *lastcmdentry = cmdp->next;
101461f28255Scgd ckfree(cmdp);
101561f28255Scgd INTON;
101661f28255Scgd }
101761f28255Scgd
101861f28255Scgd
101961f28255Scgd
102061f28255Scgd #ifdef notdef
102161f28255Scgd void
getcmdentry(char * name,struct cmdentry * entry)1022c02b3bbdSchristos getcmdentry(char *name, struct cmdentry *entry)
102361f28255Scgd {
102461f28255Scgd struct tblentry *cmdp = cmdlookup(name, 0);
102561f28255Scgd
102661f28255Scgd if (cmdp) {
102761f28255Scgd entry->u = cmdp->param;
102861f28255Scgd entry->cmdtype = cmdp->cmdtype;
102961f28255Scgd } else {
103061f28255Scgd entry->cmdtype = CMDUNKNOWN;
103161f28255Scgd entry->u.index = 0;
103261f28255Scgd }
103361f28255Scgd }
103461f28255Scgd #endif
103561f28255Scgd
103661f28255Scgd
103761f28255Scgd /*
103861f28255Scgd * Add a new command entry, replacing any existing command entry for
1039c02b3bbdSchristos * the same name - except special builtins.
104061f28255Scgd */
104161f28255Scgd
1042e314f958Sdsl STATIC void
addcmdentry(char * name,struct cmdentry * entry)1043c02b3bbdSchristos addcmdentry(char *name, struct cmdentry *entry)
104461f28255Scgd {
104561f28255Scgd struct tblentry *cmdp;
104661f28255Scgd
104761f28255Scgd INTOFF;
104861f28255Scgd cmdp = cmdlookup(name, 1);
1049c02b3bbdSchristos if (cmdp->cmdtype != CMDSPLBLTIN) {
1050c7c0722aSkre if (cmdp->cmdtype == CMDFUNCTION)
1051c7c0722aSkre unreffunc(cmdp->param.func);
105261f28255Scgd cmdp->cmdtype = entry->cmdtype;
1053727a69dcSkre cmdp->lineno = entry->lineno;
1054727a69dcSkre cmdp->fn_ln1 = entry->lno_frel;
105561f28255Scgd cmdp->param = entry->u;
1056c02b3bbdSchristos }
105761f28255Scgd INTON;
105861f28255Scgd }
105961f28255Scgd
106061f28255Scgd
106161f28255Scgd /*
106261f28255Scgd * Define a shell function.
106361f28255Scgd */
106461f28255Scgd
106561f28255Scgd void
defun(char * name,union node * func,int lineno)1066727a69dcSkre defun(char *name, union node *func, int lineno)
106761f28255Scgd {
106861f28255Scgd struct cmdentry entry;
106961f28255Scgd
107061f28255Scgd INTOFF;
107161f28255Scgd entry.cmdtype = CMDFUNCTION;
1072727a69dcSkre entry.lineno = lineno;
1073727a69dcSkre entry.lno_frel = fnline1;
107461f28255Scgd entry.u.func = copyfunc(func);
107561f28255Scgd addcmdentry(name, &entry);
107661f28255Scgd INTON;
107761f28255Scgd }
107861f28255Scgd
107961f28255Scgd
108061f28255Scgd /*
108161f28255Scgd * Delete a function if it exists.
108261f28255Scgd */
108361f28255Scgd
108437ed7877Sjtc int
unsetfunc(char * name)1085c02b3bbdSchristos unsetfunc(char *name)
108661f28255Scgd {
108761f28255Scgd struct tblentry *cmdp;
108861f28255Scgd
1089e314f958Sdsl if ((cmdp = cmdlookup(name, 0)) != NULL &&
1090e314f958Sdsl cmdp->cmdtype == CMDFUNCTION) {
1091c7c0722aSkre unreffunc(cmdp->param.func);
109261f28255Scgd delete_cmd_entry();
109361f28255Scgd }
10945c83aa64Schristos return 0;
109561f28255Scgd }
1096680690d3Schristos
1097680690d3Schristos /*
1098680690d3Schristos * Locate and print what a word is...
1099e314f958Sdsl * also used for 'command -[v|V]'
1100680690d3Schristos */
1101680690d3Schristos
1102680690d3Schristos int
typecmd(int argc,char ** argv)1103c02b3bbdSchristos typecmd(int argc, char **argv)
1104680690d3Schristos {
1105680690d3Schristos struct cmdentry entry;
1106680690d3Schristos struct tblentry *cmdp;
11074498b1feSmatt const char * const *pp;
1108680690d3Schristos struct alias *ap;
11093d424690Schristos int err = 0;
1110e314f958Sdsl char *arg;
1111e314f958Sdsl int c;
1112e314f958Sdsl int V_flag = 0;
1113e314f958Sdsl int v_flag = 0;
1114e314f958Sdsl int p_flag = 0;
1115680690d3Schristos
1116e314f958Sdsl while ((c = nextopt("vVp")) != 0) {
1117e314f958Sdsl switch (c) {
1118e314f958Sdsl case 'v': v_flag = 1; break;
1119e314f958Sdsl case 'V': V_flag = 1; break;
1120e314f958Sdsl case 'p': p_flag = 1; break;
1121e314f958Sdsl }
1122e314f958Sdsl }
1123e314f958Sdsl
1124e2f17f9aSkre if (argv[0][0] != 'c' && v_flag | V_flag | p_flag)
1125e2f17f9aSkre error("usage: %s name...", argv[0]);
1126e2f17f9aSkre
1127e2f17f9aSkre if (v_flag && V_flag)
1128e2f17f9aSkre error("-v and -V cannot both be specified");
1129e2f17f9aSkre
1130e2f17f9aSkre if (*argptr == NULL)
1131e2f17f9aSkre error("usage: %s%s name ...", argv[0],
1132e2f17f9aSkre argv[0][0] == 'c' ? " [-p] [-v|-V]" : "");
1133e314f958Sdsl
1134e314f958Sdsl while ((arg = *argptr++)) {
1135e314f958Sdsl if (!v_flag)
1136e314f958Sdsl out1str(arg);
1137680690d3Schristos /* First look at the keywords */
1138e2056eadSmatt for (pp = parsekwd; *pp; pp++)
1139e314f958Sdsl if (**pp == *arg && equal(*pp, arg))
1140680690d3Schristos break;
1141680690d3Schristos
1142680690d3Schristos if (*pp) {
1143e314f958Sdsl if (v_flag)
1144e2f17f9aSkre out1fmt("%s\n", arg);
1145e314f958Sdsl else
1146680690d3Schristos out1str(" is a shell keyword\n");
1147680690d3Schristos continue;
1148680690d3Schristos }
1149680690d3Schristos
1150680690d3Schristos /* Then look at the aliases */
1151e314f958Sdsl if ((ap = lookupalias(arg, 1)) != NULL) {
1152e2f17f9aSkre int ml = 0;
1153e2f17f9aSkre
1154e2f17f9aSkre if (!v_flag) {
1155e2f17f9aSkre out1str(" is an alias ");
1156e2f17f9aSkre if (strchr(ap->val, '\n')) {
1157e2f17f9aSkre out1str("(multiline)...\n");
1158e2f17f9aSkre ml = 1;
1159e2f17f9aSkre } else
1160e2f17f9aSkre out1str("for: ");
1161e2f17f9aSkre }
1162e314f958Sdsl out1fmt("%s\n", ap->val);
1163e2f17f9aSkre if (ml && *argptr != NULL)
1164e2f17f9aSkre out1c('\n');
1165680690d3Schristos continue;
1166680690d3Schristos }
1167680690d3Schristos
1168680690d3Schristos /* Then check if it is a tracked alias */
1169e2f17f9aSkre if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) {
1170680690d3Schristos entry.cmdtype = cmdp->cmdtype;
1171680690d3Schristos entry.u = cmdp->param;
1172e314f958Sdsl } else {
1173e2f17f9aSkre cmdp = NULL;
1174680690d3Schristos /* Finally use brute force */
1175e2f17f9aSkre find_command(arg, &entry, DO_ABS,
1176e2f17f9aSkre p_flag ? syspath() + 5 : pathval());
1177680690d3Schristos }
1178680690d3Schristos
1179680690d3Schristos switch (entry.cmdtype) {
1180680690d3Schristos case CMDNORMAL: {
1181e314f958Sdsl if (strchr(arg, '/') == NULL) {
1182e2f17f9aSkre const char *path;
11833d424690Schristos char *name;
11849cc4e15fSchristos int j = entry.u.index;
1185e2f17f9aSkre
1186e2f17f9aSkre path = p_flag ? syspath() + 5 : pathval();
1187e2f17f9aSkre
1188680690d3Schristos do {
11891676135eSkre name = padvance(&path, arg, 1);
1190680690d3Schristos stunalloc(name);
1191680690d3Schristos } while (--j >= 0);
1192e314f958Sdsl if (!v_flag)
1193e314f958Sdsl out1fmt(" is%s ",
1194e314f958Sdsl cmdp ? " a tracked alias for" : "");
1195e314f958Sdsl out1fmt("%s\n", name);
11969cc4e15fSchristos } else {
1197e314f958Sdsl if (access(arg, X_OK) == 0) {
1198e314f958Sdsl if (!v_flag)
1199e314f958Sdsl out1fmt(" is ");
1200e314f958Sdsl out1fmt("%s\n", arg);
1201e314f958Sdsl } else {
1202e314f958Sdsl if (!v_flag)
1203e314f958Sdsl out1fmt(": %s\n",
1204e314f958Sdsl strerror(errno));
12059cc4e15fSchristos else
1206e314f958Sdsl err = 126;
1207e314f958Sdsl }
12089cc4e15fSchristos }
1209680690d3Schristos break;
1210680690d3Schristos }
1211680690d3Schristos case CMDFUNCTION:
1212e314f958Sdsl if (!v_flag)
1213680690d3Schristos out1str(" is a shell function\n");
1214e314f958Sdsl else
1215e314f958Sdsl out1fmt("%s\n", arg);
1216680690d3Schristos break;
1217680690d3Schristos
1218680690d3Schristos case CMDBUILTIN:
1219e314f958Sdsl if (!v_flag)
1220680690d3Schristos out1str(" is a shell builtin\n");
1221e314f958Sdsl else
1222e314f958Sdsl out1fmt("%s\n", arg);
1223680690d3Schristos break;
1224680690d3Schristos
1225c02b3bbdSchristos case CMDSPLBLTIN:
1226e314f958Sdsl if (!v_flag)
1227c02b3bbdSchristos out1str(" is a special shell builtin\n");
1228e314f958Sdsl else
1229e314f958Sdsl out1fmt("%s\n", arg);
1230c02b3bbdSchristos break;
1231c02b3bbdSchristos
1232680690d3Schristos default:
1233e314f958Sdsl if (!v_flag)
12349cc4e15fSchristos out1str(": not found\n");
1235e314f958Sdsl err = 127;
1236680690d3Schristos break;
1237680690d3Schristos }
1238680690d3Schristos }
12393d424690Schristos return err;
1240680690d3Schristos }
1241