xref: /openbsd-src/bin/csh/exec.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: exec.c,v 1.22 2023/03/08 04:43:04 guenther Exp $	*/
26b38f156Smillert /*	$NetBSD: exec.c,v 1.9 1996/09/30 20:03:54 christos Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  * Copyright (c) 1980, 1991, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1629295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
33df930be7Sderaadt #include <sys/types.h>
34df930be7Sderaadt #include <dirent.h>
35df930be7Sderaadt #include <fcntl.h>
36df930be7Sderaadt #include <sys/stat.h>
37df930be7Sderaadt #include <errno.h>
38df930be7Sderaadt #include <stdlib.h>
39df930be7Sderaadt #include <string.h>
40df930be7Sderaadt #include <unistd.h>
41b9fc9a72Sderaadt #include <limits.h>
42df930be7Sderaadt #include <stdarg.h>
43df930be7Sderaadt 
44df930be7Sderaadt #include "csh.h"
45df930be7Sderaadt #include "extern.h"
46df930be7Sderaadt 
47df930be7Sderaadt /*
48df930be7Sderaadt  * System level search and execute of a command.  We look in each directory
49df930be7Sderaadt  * for the specified command name.  If the name contains a '/' then we
50df930be7Sderaadt  * execute only the full path name.  If there is no search path then we
51df930be7Sderaadt  * execute only full path names.
52df930be7Sderaadt  */
53df930be7Sderaadt extern char **environ;
54df930be7Sderaadt 
55df930be7Sderaadt /*
56df930be7Sderaadt  * As we search for the command we note the first non-trivial error
57df930be7Sderaadt  * message for presentation to the user.  This allows us often
58df930be7Sderaadt  * to show that a file has the wrong mode/no access when the file
59df930be7Sderaadt  * is not in the last component of the search path, so we must
60df930be7Sderaadt  * go on after first detecting the error.
61df930be7Sderaadt  */
62df930be7Sderaadt static char *exerr;		/* Execution error message */
63df930be7Sderaadt static Char *expath;		/* Path for exerr */
64df930be7Sderaadt 
65df930be7Sderaadt /*
66df930be7Sderaadt  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
67df930be7Sderaadt  * to hash execs.  If it is allocated (havhash true), then to tell
68df930be7Sderaadt  * whether ``name'' is (possibly) present in the i'th component
69df930be7Sderaadt  * of the variable path, you look at the bit in xhash indexed by
70df930be7Sderaadt  * hash(hashname("name"), i).  This is setup automatically
71df930be7Sderaadt  * after .login is executed, and recomputed whenever ``path'' is
72df930be7Sderaadt  * changed.
73df930be7Sderaadt  * The two part hash function is designed to let texec() call the
74df930be7Sderaadt  * more expensive hashname() only once and the simple hash() several
75df930be7Sderaadt  * times (once for each path component checked).
76df930be7Sderaadt  * Byte size is assumed to be 8.
77df930be7Sderaadt  */
78df930be7Sderaadt #define	HSHSIZ		8192	/* 1k bytes */
79df930be7Sderaadt #define HSHMASK		(HSHSIZ - 1)
80df930be7Sderaadt #define HSHMUL		243
81df930be7Sderaadt static char xhash[HSHSIZ / 8];
82df930be7Sderaadt 
83df930be7Sderaadt #define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
84df930be7Sderaadt #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
85df930be7Sderaadt #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
86df930be7Sderaadt static int hits, misses;
87df930be7Sderaadt 
88df930be7Sderaadt /* Dummy search path for just absolute search when no path */
89df930be7Sderaadt static Char *justabs[] = {STRNULL, 0};
90df930be7Sderaadt 
91c72b5b24Smillert static void	pexerr(void);
92c72b5b24Smillert static void	texec(Char *, Char **);
93c72b5b24Smillert static int	hashname(Char *);
946a01f4acSderaadt static int	tellmewhat(struct wordent *, Char *, int len);
95c72b5b24Smillert static int	executable(Char *, Char *, bool);
96c72b5b24Smillert static int	iscommand(Char *);
97df930be7Sderaadt 
98df930be7Sderaadt 
99df930be7Sderaadt void
doexec(Char ** v,struct command * t)100e757c91eSderaadt doexec(Char **v, struct command *t)
101df930be7Sderaadt {
102e757c91eSderaadt     Char *dp, **pv, **av, *sav;
103e757c91eSderaadt     struct varent *pathv;
104e757c91eSderaadt     bool slash;
105e757c91eSderaadt     int hashval = 0, hashval1, i;
106df930be7Sderaadt     Char   *blk[2];
107df930be7Sderaadt     sigset_t sigset;
108df930be7Sderaadt 
109df930be7Sderaadt     /*
110df930be7Sderaadt      * Glob the command name. We will search $path even if this does something,
111df930be7Sderaadt      * as in sh but not in csh.  One special case: if there is no PATH, then we
112df930be7Sderaadt      * execute only commands which start with '/'.
113df930be7Sderaadt      */
114df930be7Sderaadt     blk[0] = t->t_dcom[0];
115df930be7Sderaadt     blk[1] = 0;
116df930be7Sderaadt     gflag = 0, tglob(blk);
117df930be7Sderaadt     if (gflag) {
118df930be7Sderaadt 	pv = globall(blk);
119df930be7Sderaadt 	if (pv == 0) {
120df930be7Sderaadt 	    setname(vis_str(blk[0]));
121df930be7Sderaadt 	    stderror(ERR_NAME | ERR_NOMATCH);
122df930be7Sderaadt 	}
123df930be7Sderaadt 	gargv = 0;
124df930be7Sderaadt     }
125df930be7Sderaadt     else
126df930be7Sderaadt 	pv = saveblk(blk);
127df930be7Sderaadt 
128df930be7Sderaadt     trim(pv);
129df930be7Sderaadt 
130df930be7Sderaadt     exerr = 0;
131df930be7Sderaadt     expath = Strsave(pv[0]);
132df930be7Sderaadt     Vexpath = expath;
133df930be7Sderaadt 
134df930be7Sderaadt     pathv = adrof(STRpath);
135df930be7Sderaadt     if (pathv == 0 && expath[0] != '/') {
136df930be7Sderaadt 	blkfree(pv);
137df930be7Sderaadt 	pexerr();
138df930be7Sderaadt     }
1395f867525Sderaadt     slash = any(short2str(expath), '/');
140df930be7Sderaadt 
141df930be7Sderaadt     /*
142df930be7Sderaadt      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
143df930be7Sderaadt      */
144df930be7Sderaadt     gflag = 0;
145df930be7Sderaadt     av = &t->t_dcom[1];
146df930be7Sderaadt     tglob(av);
147df930be7Sderaadt     if (gflag) {
148df930be7Sderaadt 	av = globall(av);
149df930be7Sderaadt 	if (av == 0) {
150df930be7Sderaadt 	    blkfree(pv);
151df930be7Sderaadt 	    setname(vis_str(expath));
152df930be7Sderaadt 	    stderror(ERR_NAME | ERR_NOMATCH);
153df930be7Sderaadt 	}
154df930be7Sderaadt 	gargv = 0;
155df930be7Sderaadt     }
156df930be7Sderaadt     else
157df930be7Sderaadt 	av = saveblk(av);
158df930be7Sderaadt 
159df930be7Sderaadt     blkfree(t->t_dcom);
160df930be7Sderaadt     t->t_dcom = blkspl(pv, av);
161acdb3202Smestre     free(pv);
162acdb3202Smestre     free(av);
163df930be7Sderaadt     av = t->t_dcom;
164df930be7Sderaadt     trim(av);
165df930be7Sderaadt 
166df930be7Sderaadt     if (*av == NULL || **av == '\0')
167df930be7Sderaadt 	pexerr();
168df930be7Sderaadt 
169df930be7Sderaadt     xechoit(av);		/* Echo command if -x */
170df930be7Sderaadt     /*
171df930be7Sderaadt      * Since all internal file descriptors are set to close on exec, we don't
172df930be7Sderaadt      * need to close them explicitly here.  Just reorient ourselves for error
173df930be7Sderaadt      * messages.
174df930be7Sderaadt      */
175df930be7Sderaadt     SHIN = 0;
176df930be7Sderaadt     SHOUT = 1;
177df930be7Sderaadt     SHERR = 2;
178df930be7Sderaadt     OLDSTD = 0;
179df930be7Sderaadt     /*
180df930be7Sderaadt      * We must do this AFTER any possible forking (like `foo` in glob) so that
181df930be7Sderaadt      * this shell can still do subprocesses.
182df930be7Sderaadt      */
183df930be7Sderaadt     sigemptyset(&sigset);
184df930be7Sderaadt     sigprocmask(SIG_SETMASK, &sigset, NULL);
185df930be7Sderaadt     /*
186df930be7Sderaadt      * If no path, no words in path, or a / in the filename then restrict the
187df930be7Sderaadt      * command search.
188df930be7Sderaadt      */
189df930be7Sderaadt     if (pathv == 0 || pathv->vec[0] == 0 || slash)
190df930be7Sderaadt 	pv = justabs;
191df930be7Sderaadt     else
192df930be7Sderaadt 	pv = pathv->vec;
193df930be7Sderaadt     sav = Strspl(STRslash, *av);/* / command name for postpending */
194df930be7Sderaadt     Vsav = sav;
195df930be7Sderaadt     if (havhash)
196df930be7Sderaadt 	hashval = hashname(*av);
197df930be7Sderaadt     i = 0;
198df930be7Sderaadt     hits++;
199df930be7Sderaadt     do {
200df930be7Sderaadt 	/*
201df930be7Sderaadt 	 * Try to save time by looking at the hash table for where this command
202df930be7Sderaadt 	 * could be.  If we are doing delayed hashing, then we put the names in
203df930be7Sderaadt 	 * one at a time, as the user enters them.  This is kinda like Korn
204df930be7Sderaadt 	 * Shell's "tracked aliases".
205df930be7Sderaadt 	 */
206df930be7Sderaadt 	if (!slash && pv[0][0] == '/' && havhash) {
207df930be7Sderaadt 	    hashval1 = hash(hashval, i);
208df930be7Sderaadt 	    if (!bit(xhash, hashval1))
209df930be7Sderaadt 		goto cont;
210df930be7Sderaadt 	}
211df930be7Sderaadt 	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
212df930be7Sderaadt 	    texec(*av, av);
213df930be7Sderaadt 	else {
214df930be7Sderaadt 	    dp = Strspl(*pv, sav);
215df930be7Sderaadt 	    Vdp = dp;
216df930be7Sderaadt 	    texec(dp, av);
217df930be7Sderaadt 	    Vdp = 0;
218acdb3202Smestre 	    free(dp);
219df930be7Sderaadt 	}
220df930be7Sderaadt 	misses++;
221df930be7Sderaadt cont:
222df930be7Sderaadt 	pv++;
223df930be7Sderaadt 	i++;
224df930be7Sderaadt     } while (*pv);
225df930be7Sderaadt     hits--;
226df930be7Sderaadt     Vsav = 0;
227acdb3202Smestre     free(sav);
228df930be7Sderaadt     pexerr();
229df930be7Sderaadt }
230df930be7Sderaadt 
231df930be7Sderaadt static void
pexerr(void)232e757c91eSderaadt pexerr(void)
233df930be7Sderaadt {
234df930be7Sderaadt     /* Couldn't find the damn thing */
235df930be7Sderaadt     if (expath) {
236df930be7Sderaadt 	setname(vis_str(expath));
237df930be7Sderaadt 	Vexpath = 0;
238acdb3202Smestre 	free(expath);
239df930be7Sderaadt 	expath = 0;
240df930be7Sderaadt     }
241df930be7Sderaadt     else
242df930be7Sderaadt 	setname("");
243df930be7Sderaadt     if (exerr)
244df930be7Sderaadt 	stderror(ERR_NAME | ERR_STRING, exerr);
245df930be7Sderaadt     stderror(ERR_NAME | ERR_COMMAND);
246df930be7Sderaadt }
247df930be7Sderaadt 
248df930be7Sderaadt /*
249df930be7Sderaadt  * Execute command f, arg list t.
250df930be7Sderaadt  * Record error message if not found.
251df930be7Sderaadt  * Also do shell scripts here.
252df930be7Sderaadt  */
253df930be7Sderaadt static void
texec(Char * sf,Char ** st)254e757c91eSderaadt texec(Char *sf, Char **st)
255df930be7Sderaadt {
256e757c91eSderaadt     char **t;
257e757c91eSderaadt     char *f;
258e757c91eSderaadt     struct varent *v;
259e757c91eSderaadt     Char **vp;
260df930be7Sderaadt     Char   *lastsh[2];
261df930be7Sderaadt     int     fd;
262df930be7Sderaadt     unsigned char c;
263df930be7Sderaadt     Char   *st0, **ost;
264df930be7Sderaadt 
265df930be7Sderaadt     /* The order for the conversions is significant */
266df930be7Sderaadt     t = short2blk(st);
267df930be7Sderaadt     f = short2str(sf);
268df930be7Sderaadt     Vt = t;
269df930be7Sderaadt     errno = 0;			/* don't use a previous error */
270df930be7Sderaadt     (void) execve(f, t, environ);
271df930be7Sderaadt     Vt = 0;
272df930be7Sderaadt     blkfree((Char **) t);
273df930be7Sderaadt     switch (errno) {
274df930be7Sderaadt 
275df930be7Sderaadt     case ENOEXEC:
276df930be7Sderaadt 	/*
277df930be7Sderaadt 	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
278df930be7Sderaadt 	 * it, don't feed it to the shell if it looks like a binary!
279df930be7Sderaadt 	 */
280df930be7Sderaadt 	if ((fd = open(f, O_RDONLY)) != -1) {
281df930be7Sderaadt 	    if (read(fd, (char *) &c, 1) == 1) {
282df930be7Sderaadt 		if (!Isprint(c) && (c != '\n' && c != '\t')) {
283df930be7Sderaadt 		    (void) close(fd);
284df930be7Sderaadt 		    /*
285df930be7Sderaadt 		     * We *know* what ENOEXEC means.
286df930be7Sderaadt 		     */
287df930be7Sderaadt 		    stderror(ERR_ARCH, f, strerror(errno));
288df930be7Sderaadt 		}
289df930be7Sderaadt 	    }
290df930be7Sderaadt 	    else
291df930be7Sderaadt 		c = '#';
292df930be7Sderaadt 	    (void) close(fd);
293df930be7Sderaadt 	}
294df930be7Sderaadt 	/*
295df930be7Sderaadt 	 * If there is an alias for shell, then put the words of the alias in
296df930be7Sderaadt 	 * front of the argument list replacing the command name. Note no
297df930be7Sderaadt 	 * interpretation of the words at this point.
298df930be7Sderaadt 	 */
299df930be7Sderaadt 	v = adrof1(STRshell, &aliases);
300df930be7Sderaadt 	if (v == 0) {
301df930be7Sderaadt 	    vp = lastsh;
302df930be7Sderaadt 	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
303df930be7Sderaadt 	    vp[1] = NULL;
304df930be7Sderaadt 	    if (fd != -1 && c != '#')
305df930be7Sderaadt 		vp[0] = STR_BSHELL;
306df930be7Sderaadt 	}
307df930be7Sderaadt 	else
308df930be7Sderaadt 	    vp = v->vec;
309df930be7Sderaadt 	st0 = st[0];
310df930be7Sderaadt 	st[0] = sf;
311df930be7Sderaadt 	ost = st;
312df930be7Sderaadt 	st = blkspl(vp, st);	/* Splice up the new arglst */
313df930be7Sderaadt 	ost[0] = st0;
314df930be7Sderaadt 	sf = *st;
315df930be7Sderaadt 	/* The order for the conversions is significant */
316df930be7Sderaadt 	t = short2blk(st);
317df930be7Sderaadt 	f = short2str(sf);
318acdb3202Smestre 	free(st);
319df930be7Sderaadt 	Vt = t;
320df930be7Sderaadt 	(void) execve(f, t, environ);
321df930be7Sderaadt 	Vt = 0;
322df930be7Sderaadt 	blkfree((Char **) t);
323df930be7Sderaadt 	/* The sky is falling, the sky is falling! */
324df930be7Sderaadt 
325df930be7Sderaadt     case ENOMEM:
326df930be7Sderaadt 	stderror(ERR_SYSTEM, f, strerror(errno));
327df930be7Sderaadt 
328df930be7Sderaadt     case ENOENT:
329df930be7Sderaadt 	break;
330df930be7Sderaadt 
331df930be7Sderaadt     default:
332df930be7Sderaadt 	if (exerr == 0) {
333df930be7Sderaadt 	    exerr = strerror(errno);
334df930be7Sderaadt 	    if (expath)
335acdb3202Smestre 		free(expath);
336df930be7Sderaadt 	    expath = Strsave(sf);
337df930be7Sderaadt 	    Vexpath = expath;
338df930be7Sderaadt 	}
339df930be7Sderaadt     }
340df930be7Sderaadt }
341df930be7Sderaadt 
342df930be7Sderaadt void
execash(Char ** t,struct command * kp)343e757c91eSderaadt execash(Char **t, struct command *kp)
344df930be7Sderaadt {
345df930be7Sderaadt     int     saveIN, saveOUT, saveDIAG, saveSTD;
346df930be7Sderaadt     int     oSHIN;
347df930be7Sderaadt     int     oSHOUT;
348df930be7Sderaadt     int     oSHERR;
349df930be7Sderaadt     int     oOLDSTD;
350df930be7Sderaadt     jmp_buf osetexit;
351df930be7Sderaadt     int	    my_reenter;
352df930be7Sderaadt     int     odidfds;
353df930be7Sderaadt     sig_t   osigint, osigquit, osigterm;
354df930be7Sderaadt 
355df930be7Sderaadt     if (chkstop == 0 && setintr)
356df930be7Sderaadt 	panystop(0);
357df930be7Sderaadt     /*
358df930be7Sderaadt      * Hmm, we don't really want to do that now because we might
359df930be7Sderaadt      * fail, but what is the choice
360df930be7Sderaadt      */
361df930be7Sderaadt     rechist();
362df930be7Sderaadt 
363df930be7Sderaadt     osigint  = signal(SIGINT, parintr);
364df930be7Sderaadt     osigquit = signal(SIGQUIT, parintr);
365df930be7Sderaadt     osigterm = signal(SIGTERM, parterm);
366df930be7Sderaadt 
367df930be7Sderaadt     odidfds = didfds;
368df930be7Sderaadt     oSHIN = SHIN;
369df930be7Sderaadt     oSHOUT = SHOUT;
370df930be7Sderaadt     oSHERR = SHERR;
371df930be7Sderaadt     oOLDSTD = OLDSTD;
372df930be7Sderaadt 
373df930be7Sderaadt     saveIN = dcopy(SHIN, -1);
374df930be7Sderaadt     saveOUT = dcopy(SHOUT, -1);
375df930be7Sderaadt     saveDIAG = dcopy(SHERR, -1);
376df930be7Sderaadt     saveSTD = dcopy(OLDSTD, -1);
377df930be7Sderaadt 
378df930be7Sderaadt     lshift(kp->t_dcom, 1);
379df930be7Sderaadt 
380df930be7Sderaadt     getexit(osetexit);
381df930be7Sderaadt 
382df930be7Sderaadt     if ((my_reenter = setexit()) == 0) {
383df930be7Sderaadt 	SHIN = dcopy(0, -1);
384df930be7Sderaadt 	SHOUT = dcopy(1, -1);
385df930be7Sderaadt 	SHERR = dcopy(2, -1);
386df930be7Sderaadt 	didfds = 0;
387df930be7Sderaadt 	doexec(t, kp);
388df930be7Sderaadt     }
389df930be7Sderaadt 
390df930be7Sderaadt     (void) signal(SIGINT, osigint);
391df930be7Sderaadt     (void) signal(SIGQUIT, osigquit);
392df930be7Sderaadt     (void) signal(SIGTERM, osigterm);
393df930be7Sderaadt 
394df930be7Sderaadt     doneinp = 0;
395df930be7Sderaadt     didfds = odidfds;
396df930be7Sderaadt     (void) close(SHIN);
397df930be7Sderaadt     (void) close(SHOUT);
398df930be7Sderaadt     (void) close(SHERR);
399df930be7Sderaadt     (void) close(OLDSTD);
400df930be7Sderaadt     SHIN = dmove(saveIN, oSHIN);
401df930be7Sderaadt     SHOUT = dmove(saveOUT, oSHOUT);
402df930be7Sderaadt     SHERR = dmove(saveDIAG, oSHERR);
403df930be7Sderaadt     OLDSTD = dmove(saveSTD, oOLDSTD);
404df930be7Sderaadt 
405df930be7Sderaadt     resexit(osetexit);
406df930be7Sderaadt     if (my_reenter)
407df930be7Sderaadt 	stderror(ERR_SILENT);
408df930be7Sderaadt }
409df930be7Sderaadt 
410df930be7Sderaadt void
xechoit(Char ** t)411e757c91eSderaadt xechoit(Char **t)
412df930be7Sderaadt {
413df930be7Sderaadt     if (adrof(STRecho)) {
414df930be7Sderaadt 	(void) fflush(csherr);
415df930be7Sderaadt 	blkpr(csherr, t);
416df930be7Sderaadt 	(void) fputc('\n', csherr);
417df930be7Sderaadt     }
418df930be7Sderaadt }
419df930be7Sderaadt 
420df930be7Sderaadt void
dohash(Char ** v,struct command * t)421e757c91eSderaadt dohash(Char **v, struct command *t)
422df930be7Sderaadt {
423df930be7Sderaadt     DIR    *dirp;
424e757c91eSderaadt     struct dirent *dp;
425e757c91eSderaadt     int cnt;
426df930be7Sderaadt     int     i = 0;
427df930be7Sderaadt     struct varent *pathv = adrof(STRpath);
428df930be7Sderaadt     Char  **pv;
429df930be7Sderaadt     int     hashval;
430df930be7Sderaadt 
431df930be7Sderaadt     havhash = 1;
432df930be7Sderaadt     for (cnt = 0; cnt < sizeof xhash; cnt++)
433df930be7Sderaadt 	xhash[cnt] = 0;
434df930be7Sderaadt     if (pathv == 0)
435df930be7Sderaadt 	return;
436df930be7Sderaadt     for (pv = pathv->vec; *pv; pv++, i++) {
437df930be7Sderaadt 	if (pv[0][0] != '/')
438df930be7Sderaadt 	    continue;
439df930be7Sderaadt 	dirp = opendir(short2str(*pv));
440df930be7Sderaadt 	if (dirp == NULL)
441df930be7Sderaadt 	    continue;
442df930be7Sderaadt 	while ((dp = readdir(dirp)) != NULL) {
443df930be7Sderaadt 	    if (dp->d_ino == 0)
444df930be7Sderaadt 		continue;
445df930be7Sderaadt 	    if (dp->d_name[0] == '.' &&
446df930be7Sderaadt 		(dp->d_name[1] == '\0' ||
447df930be7Sderaadt 		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
448df930be7Sderaadt 		continue;
449df930be7Sderaadt 	    hashval = hash(hashname(str2short(dp->d_name)), i);
450df930be7Sderaadt 	    bis(xhash, hashval);
451df930be7Sderaadt 	    /* tw_add_comm_name (dp->d_name); */
452df930be7Sderaadt 	}
453df930be7Sderaadt 	(void) closedir(dirp);
454df930be7Sderaadt     }
455df930be7Sderaadt }
456df930be7Sderaadt 
457df930be7Sderaadt void
dounhash(Char ** v,struct command * t)458e757c91eSderaadt dounhash(Char **v, struct command *t)
459df930be7Sderaadt {
460df930be7Sderaadt     havhash = 0;
461df930be7Sderaadt }
462df930be7Sderaadt 
463df930be7Sderaadt void
hashstat(Char ** v,struct command * t)464e757c91eSderaadt hashstat(Char **v, struct command *t)
465df930be7Sderaadt {
466df930be7Sderaadt     if (hits + misses)
467df930be7Sderaadt 	(void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
468df930be7Sderaadt 		       hits, misses, 100 * hits / (hits + misses));
469df930be7Sderaadt }
470df930be7Sderaadt 
471df930be7Sderaadt /*
472df930be7Sderaadt  * Hash a command name.
473df930be7Sderaadt  */
474df930be7Sderaadt static int
hashname(Char * cp)475e757c91eSderaadt hashname(Char *cp)
476df930be7Sderaadt {
477e757c91eSderaadt     long h = 0;
478df930be7Sderaadt 
479df930be7Sderaadt     while (*cp)
480df930be7Sderaadt 	h = hash(h, *cp++);
481df930be7Sderaadt     return ((int) h);
482df930be7Sderaadt }
483df930be7Sderaadt 
484df930be7Sderaadt static int
iscommand(Char * name)485e757c91eSderaadt iscommand(Char *name)
486df930be7Sderaadt {
487e757c91eSderaadt     Char **pv;
488e757c91eSderaadt     Char *sav;
489e757c91eSderaadt     struct varent *v;
4905f867525Sderaadt     bool slash = any(short2str(name), '/');
491e757c91eSderaadt     int hashval = 0, hashval1, i;
492df930be7Sderaadt 
493df930be7Sderaadt     v = adrof(STRpath);
494df930be7Sderaadt     if (v == 0 || v->vec[0] == 0 || slash)
495df930be7Sderaadt 	pv = justabs;
496df930be7Sderaadt     else
497df930be7Sderaadt 	pv = v->vec;
498df930be7Sderaadt     sav = Strspl(STRslash, name);	/* / command name for postpending */
499df930be7Sderaadt     if (havhash)
500df930be7Sderaadt 	hashval = hashname(name);
501df930be7Sderaadt     i = 0;
502df930be7Sderaadt     do {
503df930be7Sderaadt 	if (!slash && pv[0][0] == '/' && havhash) {
504df930be7Sderaadt 	    hashval1 = hash(hashval, i);
505df930be7Sderaadt 	    if (!bit(xhash, hashval1))
506df930be7Sderaadt 		goto cont;
507df930be7Sderaadt 	}
508df930be7Sderaadt 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
509df930be7Sderaadt 	    if (executable(NULL, name, 0)) {
510acdb3202Smestre 		free(sav);
511df930be7Sderaadt 		return i + 1;
512df930be7Sderaadt 	    }
513df930be7Sderaadt 	}
514df930be7Sderaadt 	else {
515df930be7Sderaadt 	    if (executable(*pv, sav, 0)) {
516acdb3202Smestre 		free(sav);
517df930be7Sderaadt 		return i + 1;
518df930be7Sderaadt 	    }
519df930be7Sderaadt 	}
520df930be7Sderaadt cont:
521df930be7Sderaadt 	pv++;
522df930be7Sderaadt 	i++;
523df930be7Sderaadt     } while (*pv);
524acdb3202Smestre     free(sav);
525df930be7Sderaadt     return 0;
526df930be7Sderaadt }
527df930be7Sderaadt 
528df930be7Sderaadt /* Also by:
529df930be7Sderaadt  *  Andreas Luik <luik@isaak.isa.de>
530df930be7Sderaadt  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
531df930be7Sderaadt  *  Azenberstr. 35
532df930be7Sderaadt  *  D-7000 Stuttgart 1
533df930be7Sderaadt  *  West-Germany
534df930be7Sderaadt  * is the executable() routine below and changes to iscommand().
535df930be7Sderaadt  * Thanks again!!
536df930be7Sderaadt  */
537df930be7Sderaadt 
538df930be7Sderaadt /*
539df930be7Sderaadt  * executable() examines the pathname obtained by concatenating dir and name
540df930be7Sderaadt  * (dir may be NULL), and returns 1 either if it is executable by us, or
541df930be7Sderaadt  * if dir_ok is set and the pathname refers to a directory.
542982ba893Stodd  * This is a bit kludgy, but in the name of optimization...
543df930be7Sderaadt  */
544df930be7Sderaadt static int
executable(Char * dir,Char * name,bool dir_ok)545e757c91eSderaadt executable(Char *dir, Char *name, bool dir_ok)
546df930be7Sderaadt {
547df930be7Sderaadt     struct stat stbuf;
548b9fc9a72Sderaadt     Char    path[PATH_MAX], *dp, *sp;
549df930be7Sderaadt     char   *strname;
550df930be7Sderaadt 
551df930be7Sderaadt     if (dir && *dir) {
552df930be7Sderaadt 	for (dp = path, sp = dir; *sp; *dp++ = *sp++)
553b9fc9a72Sderaadt 	    if (dp == &path[PATH_MAX]) {
554df930be7Sderaadt 		*--dp = '\0';
555df930be7Sderaadt 		break;
556df930be7Sderaadt 	    }
557df930be7Sderaadt 	for (sp = name; *sp; *dp++ = *sp++)
558b9fc9a72Sderaadt 	    if (dp == &path[PATH_MAX]) {
559df930be7Sderaadt 		*--dp = '\0';
560df930be7Sderaadt 		break;
561df930be7Sderaadt 	    }
562df930be7Sderaadt 	*dp = '\0';
563df930be7Sderaadt 	strname = short2str(path);
564df930be7Sderaadt     }
565df930be7Sderaadt     else
566df930be7Sderaadt 	strname = short2str(name);
567df930be7Sderaadt     return (stat(strname, &stbuf) != -1 &&
568df930be7Sderaadt 	    ((S_ISREG(stbuf.st_mode) &&
569df930be7Sderaadt     /* save time by not calling access() in the hopeless case */
570df930be7Sderaadt 	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
571df930be7Sderaadt 	      access(strname, X_OK) == 0) ||
572df930be7Sderaadt 	     (dir_ok && S_ISDIR(stbuf.st_mode))));
573df930be7Sderaadt }
574df930be7Sderaadt 
575df930be7Sderaadt /* The dowhich() is by:
576df930be7Sderaadt  *  Andreas Luik <luik@isaak.isa.de>
577df930be7Sderaadt  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
578df930be7Sderaadt  *  Azenberstr. 35
579df930be7Sderaadt  *  D-7000 Stuttgart 1
580df930be7Sderaadt  *  West-Germany
581df930be7Sderaadt  * Thanks!!
582df930be7Sderaadt  */
583df930be7Sderaadt void
dowhich(Char ** v,struct command * c)584e757c91eSderaadt dowhich(Char **v, struct command *c)
585df930be7Sderaadt {
586df930be7Sderaadt     struct wordent lex[3];
587df930be7Sderaadt     struct varent *vp;
588df930be7Sderaadt 
589df930be7Sderaadt     lex[0].next = &lex[1];
590df930be7Sderaadt     lex[1].next = &lex[2];
591df930be7Sderaadt     lex[2].next = &lex[0];
592df930be7Sderaadt 
593df930be7Sderaadt     lex[0].prev = &lex[2];
594df930be7Sderaadt     lex[1].prev = &lex[0];
595df930be7Sderaadt     lex[2].prev = &lex[1];
596df930be7Sderaadt 
597df930be7Sderaadt     lex[0].word = STRNULL;
598df930be7Sderaadt     lex[2].word = STRret;
599df930be7Sderaadt 
600df930be7Sderaadt     while (*++v) {
601df930be7Sderaadt 	if ((vp = adrof1(*v, &aliases)) != NULL) {
602df930be7Sderaadt 	    (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
603df930be7Sderaadt 	    blkpr(cshout, vp->vec);
604df930be7Sderaadt 	    (void) fputc('\n', cshout);
6056b38f156Smillert 	    set(STRstatus, Strsave(STR0));
606df930be7Sderaadt 	}
607df930be7Sderaadt 	else {
608df930be7Sderaadt 	    lex[1].word = *v;
6096a01f4acSderaadt 	    set(STRstatus, Strsave(tellmewhat(lex, NULL, 0) ? STR0 : STR1));
610df930be7Sderaadt 	}
611df930be7Sderaadt     }
612df930be7Sderaadt }
613df930be7Sderaadt 
6146b38f156Smillert static int
tellmewhat(struct wordent * lexp,Char * str,int len)615e757c91eSderaadt tellmewhat(struct wordent *lexp, Char *str, int len)
616df930be7Sderaadt {
617e757c91eSderaadt     int i;
618e757c91eSderaadt     struct biltins *bptr;
619e757c91eSderaadt     struct wordent *sp = lexp->next;
6206b38f156Smillert     bool    aliased = 0, found;
621df930be7Sderaadt     Char   *s0, *s1, *s2, *cmd;
622df930be7Sderaadt     Char    qc;
623df930be7Sderaadt 
624df930be7Sderaadt     if (adrof1(sp->word, &aliases)) {
6256b38f156Smillert 	alias(lexp);
6266b38f156Smillert 	sp = lexp->next;
627df930be7Sderaadt 	aliased = 1;
628df930be7Sderaadt     }
629df930be7Sderaadt 
630df930be7Sderaadt     s0 = sp->word;		/* to get the memory freeing right... */
631df930be7Sderaadt 
632df930be7Sderaadt     /* handle quoted alias hack */
633df930be7Sderaadt     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
634df930be7Sderaadt 	(sp->word)++;
635df930be7Sderaadt 
636df930be7Sderaadt     /* do quoting, if it hasn't been done */
637df930be7Sderaadt     s1 = s2 = sp->word;
638df930be7Sderaadt     while (*s2)
639df930be7Sderaadt 	switch (*s2) {
640df930be7Sderaadt 	case '\'':
641df930be7Sderaadt 	case '"':
642df930be7Sderaadt 	    qc = *s2++;
643df930be7Sderaadt 	    while (*s2 && *s2 != qc)
644df930be7Sderaadt 		*s1++ = *s2++ | QUOTE;
645df930be7Sderaadt 	    if (*s2)
646df930be7Sderaadt 		s2++;
647df930be7Sderaadt 	    break;
648df930be7Sderaadt 	case '\\':
649df930be7Sderaadt 	    if (*++s2)
650df930be7Sderaadt 		*s1++ = *s2++ | QUOTE;
651df930be7Sderaadt 	    break;
652df930be7Sderaadt 	default:
653df930be7Sderaadt 	    *s1++ = *s2++;
654df930be7Sderaadt 	}
655df930be7Sderaadt     *s1 = '\0';
656df930be7Sderaadt 
657df930be7Sderaadt     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
658df930be7Sderaadt 	if (eq(sp->word, str2short(bptr->bname))) {
6596b38f156Smillert 	    if (str == NULL) {
660df930be7Sderaadt 	        if (aliased)
6616b38f156Smillert 		        prlex(cshout, lexp);
662df930be7Sderaadt 	        (void) fprintf(cshout, "%s: shell built-in command.\n",
663df930be7Sderaadt 			   vis_str(sp->word));
6646b38f156Smillert 	    }
6656b38f156Smillert 	    else
6666a01f4acSderaadt 		(void) Strlcpy(str, sp->word, len/sizeof(Char));
667df930be7Sderaadt 	    sp->word = s0;	/* we save and then restore this */
6686b38f156Smillert 	    return 1;
669df930be7Sderaadt 	}
670df930be7Sderaadt     }
671df930be7Sderaadt 
672df930be7Sderaadt     sp->word = cmd = globone(sp->word, G_IGNORE);
673df930be7Sderaadt 
6746b38f156Smillert     if ((i = iscommand(sp->word)) != 0) {
675e757c91eSderaadt 	Char **pv;
676e757c91eSderaadt 	struct varent *v;
6775f867525Sderaadt 	bool    slash = any(short2str(sp->word), '/');
678df930be7Sderaadt 
679df930be7Sderaadt 	v = adrof(STRpath);
680df930be7Sderaadt 	if (v == 0 || v->vec[0] == 0 || slash)
681df930be7Sderaadt 	    pv = justabs;
682df930be7Sderaadt 	else
683df930be7Sderaadt 	    pv = v->vec;
684df930be7Sderaadt 
685df930be7Sderaadt 	while (--i)
686df930be7Sderaadt 	    pv++;
687df930be7Sderaadt 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
688df930be7Sderaadt 	    if (!slash) {
689df930be7Sderaadt 		sp->word = Strspl(STRdotsl, sp->word);
6906b38f156Smillert 		prlex(cshout, lexp);
691acdb3202Smestre 		free(sp->word);
692df930be7Sderaadt 	    }
693df930be7Sderaadt 	    else
6946b38f156Smillert 		prlex(cshout, lexp);
695df930be7Sderaadt 	}
6966b38f156Smillert 	else {
697df930be7Sderaadt 	    s1 = Strspl(*pv, STRslash);
698df930be7Sderaadt 	    sp->word = Strspl(s1, sp->word);
699acdb3202Smestre 	    free(s1);
7006b38f156Smillert 	    if (str == NULL)
7016b38f156Smillert 		prlex(cshout, lexp);
7026b38f156Smillert 	    else
7036a01f4acSderaadt 		(void) Strlcpy(str, sp->word, len/sizeof(Char));
704acdb3202Smestre 	    free(sp->word);
705df930be7Sderaadt         }
7066b38f156Smillert 	found = 1;
7076b38f156Smillert     }
708df930be7Sderaadt     else {
7096b38f156Smillert 	if (str == NULL) {
710df930be7Sderaadt 	    if (aliased)
7116b38f156Smillert 		prlex(cshout, lexp);
7126b38f156Smillert 	    (void) fprintf(csherr,
7136b38f156Smillert 			   "%s: Command not found.\n", vis_str(sp->word));
7146b38f156Smillert 	}
7156b38f156Smillert 	else
7166a01f4acSderaadt 	    (void) Strlcpy(str, sp->word, len/sizeof(Char));
7176b38f156Smillert 	found = 0;
718df930be7Sderaadt     }
719df930be7Sderaadt     sp->word = s0;		/* we save and then restore this */
720acdb3202Smestre     free(cmd);
7216b38f156Smillert     return found;
722df930be7Sderaadt }
723