nrelease - fix/improve livecd
[dragonfly.git] / contrib / tcsh-6 / sh.exec.c
CommitLineData
7d8fb588
MS
1/*
2 * sh.exec.c: Search, find, and execute a command!
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include "sh.h"
7d8fb588
MS
33#include "tc.h"
34#include "tw.h"
35#ifdef WINNT_NATIVE
36#include <nt.const.h>
37#endif /*WINNT_NATIVE*/
38
39/*
40 * C shell
41 */
42
43#ifndef OLDHASH
44# define FASTHASH /* Fast hashing is the default */
45#endif /* OLDHASH */
46
47/*
48 * System level search and execute of a command.
49 * We look in each directory for the specified command name.
50 * If the name contains a '/' then we execute only the full path name.
51 * If there is no search path then we execute only full path names.
52 */
53
54/*
55 * As we search for the command we note the first non-trivial error
56 * message for presentation to the user. This allows us often
57 * to show that a file has the wrong mode/no access when the file
58 * is not in the last component of the search path, so we must
59 * go on after first detecting the error.
60 */
61static char *exerr; /* Execution error message */
62static Char *expath; /* Path for exerr */
63
64/*
65 * The two part hash function is designed to let texec() call the
66 * more expensive hashname() only once and the simple hash() several
67 * times (once for each path component checked).
68 * Byte size is assumed to be 8.
69 */
70#define BITS_PER_BYTE 8
71
72#ifdef FASTHASH
73/*
74 * xhash is an array of hash buckets which are used to hash execs. If
75 * it is allocated (havhash true), then to tell if ``name'' is
94afa86d 76 * (possibly) present in the i'th component of the variable path, look
7d8fb588
MS
77 * at the [hashname(name)] bucket of size [hashwidth] bytes, in the [i
78 * mod size*8]'th bit. The cache size is defaults to a length of 1024
79 * buckets, each 1 byte wide. This implementation guarantees that
80 * objects n bytes wide will be aligned on n byte boundaries.
81 */
82# define HSHMUL 241
83
84static unsigned long *xhash = NULL;
85static unsigned int hashlength = 0, uhashlength = 0;
86static unsigned int hashwidth = 0, uhashwidth = 0;
87static int hashdebug = 0;
88
89# define hash(a, b) (((a) * HSHMUL + (b)) % (hashlength))
90# define widthof(t) (sizeof(t) * BITS_PER_BYTE)
91# define tbit(f, i, t) (((t *) xhash)[(f)] & \
92 (1UL << (i & (widthof(t) - 1))))
93# define tbis(f, i, t) (((t *) xhash)[(f)] |= \
94 (1UL << (i & (widthof(t) - 1))))
95# define cbit(f, i) tbit(f, i, unsigned char)
96# define cbis(f, i) tbis(f, i, unsigned char)
97# define sbit(f, i) tbit(f, i, unsigned short)
98# define sbis(f, i) tbis(f, i, unsigned short)
99# define ibit(f, i) tbit(f, i, unsigned int)
100# define ibis(f, i) tbis(f, i, unsigned int)
101# define lbit(f, i) tbit(f, i, unsigned long)
102# define lbis(f, i) tbis(f, i, unsigned long)
103
104# define bit(f, i) (hashwidth==sizeof(unsigned char) ? cbit(f,i) : \
105 ((hashwidth==sizeof(unsigned short) ? sbit(f,i) : \
106 ((hashwidth==sizeof(unsigned int) ? ibit(f,i) : \
107 lbit(f,i))))))
108# define bis(f, i) (hashwidth==sizeof(unsigned char) ? cbis(f,i) : \
109 ((hashwidth==sizeof(unsigned short) ? sbis(f,i) : \
110 ((hashwidth==sizeof(unsigned int) ? ibis(f,i) : \
111 lbis(f,i))))))
112#else /* OLDHASH */
113/*
114 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
115 * to hash execs. If it is allocated (havhash true), then to tell
116 * whether ``name'' is (possibly) present in the i'th component
117 * of the variable path, you look at the bit in xhash indexed by
118 * hash(hashname("name"), i). This is setup automatically
119 * after .login is executed, and recomputed whenever ``path'' is
120 * changed.
121 */
122# define HSHSIZ 8192 /* 1k bytes */
123# define HSHMASK (HSHSIZ - 1)
124# define HSHMUL 243
125static char xhash[HSHSIZ / BITS_PER_BYTE];
126
127# define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK)
128# define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */
129# define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */
130
131#endif /* FASTHASH */
132
133#ifdef VFORK
134static int hits, misses;
135#endif /* VFORK */
136
137/* Dummy search path for just absolute search when no path */
138static Char *justabs[] = {STRNULL, 0};
139
94afa86d 140static void pexerr (void) __attribute__((__noreturn__));
7d8fb588
MS
141static void texec (Char *, Char **);
142int hashname (Char *);
143static int iscommand (Char *);
144
145void
146doexec(struct command *t, int do_glob)
147{
94afa86d 148 Char *dp, **pv, **opv, **av, *sav;
7d8fb588 149 struct varent *v;
94afa86d 150 int slash, gflag, rehashed;
7d8fb588
MS
151 int hashval, i;
152 Char *blk[2];
153
154 /*
155 * Glob the command name. We will search $path even if this does something,
156 * as in sh but not in csh. One special case: if there is no PATH, then we
157 * execute only commands which start with '/'.
158 */
159 blk[0] = t->t_dcom[0];
160 blk[1] = 0;
161 gflag = 0;
162 if (do_glob)
163 gflag = tglob(blk);
164 if (gflag) {
165 pv = globall(blk, gflag);
166 if (pv == 0) {
167 setname(short2str(blk[0]));
168 stderror(ERR_NAME | ERR_NOMATCH);
169 }
170 }
171 else
172 pv = saveblk(blk);
173 cleanup_push(pv, blk_cleanup);
174
175 trim(pv);
176
177 exerr = 0;
178 expath = Strsave(pv[0]);
179#ifdef VFORK
180 Vexpath = expath;
181#endif /* VFORK */
182
183 v = adrof(STRpath);
184 if (v == 0 && expath[0] != '/' && expath[0] != '.')
185 pexerr();
186 slash = any(short2str(expath), '/');
187
188 /*
189 * Glob the argument list, if necessary. Otherwise trim off the quote bits.
190 */
191 gflag = 0;
192 av = &t->t_dcom[1];
193 if (do_glob)
194 gflag = tglob(av);
195 if (gflag) {
196 av = globall(av, gflag);
197 if (av == 0) {
198 setname(short2str(expath));
199 stderror(ERR_NAME | ERR_NOMATCH);
200 }
201 }
202 else
203 av = saveblk(av);
204
205 blkfree(t->t_dcom);
206 cleanup_ignore(pv);
207 cleanup_until(pv);
208 t->t_dcom = blkspl(pv, av);
209 xfree(pv);
210 xfree(av);
211 av = t->t_dcom;
212 trim(av);
213
214 if (*av == NULL || **av == '\0')
215 pexerr();
216
217 xechoit(av); /* Echo command if -x */
218#ifdef CLOSE_ON_EXEC
219 /*
220 * Since all internal file descriptors are set to close on exec, we don't
221 * need to close them explicitly here. Just reorient ourselves for error
222 * messages.
223 */
224 SHIN = 0;
225 SHOUT = 1;
226 SHDIAG = 2;
227 OLDSTD = 0;
228 isoutatty = isatty(SHOUT);
229 isdiagatty = isatty(SHDIAG);
230#else
231 closech(); /* Close random fd's */
232#endif
233 /*
234 * We must do this AFTER any possible forking (like `foo` in glob) so that
235 * this shell can still do subprocesses.
236 */
237 {
238 sigset_t set;
239 sigemptyset(&set);
240 sigaddset(&set, SIGINT);
241 sigaddset(&set, SIGCHLD);
242 sigprocmask(SIG_UNBLOCK, &set, NULL);
243 }
244 pintr_disabled = 0;
245 pchild_disabled = 0;
246
247 /*
248 * If no path, no words in path, or a / in the filename then restrict the
249 * command search.
250 */
251 if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
94afa86d 252 opv = justabs;
7d8fb588 253 else
94afa86d 254 opv = v->vec;
7d8fb588
MS
255 sav = Strspl(STRslash, *av);/* / command name for postpending */
256#ifndef VFORK
257 cleanup_push(sav, xfree);
258#else /* VFORK */
259 Vsav = sav;
260#endif /* VFORK */
261 hashval = havhash ? hashname(*av) : 0;
262
94afa86d
JM
263 rehashed = 0;
264retry:
265 pv = opv;
7d8fb588
MS
266 i = 0;
267#ifdef VFORK
268 hits++;
269#endif /* VFORK */
270 do {
271 /*
272 * Try to save time by looking at the hash table for where this command
273 * could be. If we are doing delayed hashing, then we put the names in
274 * one at a time, as the user enters them. This is kinda like Korn
275 * Shell's "tracked aliases".
276 */
277 if (!slash && ABSOLUTEP(pv[0]) && havhash) {
278#ifdef FASTHASH
279 if (!bit(hashval, i))
280 goto cont;
281#else /* OLDHASH */
282 int hashval1 = hash(hashval, i);
283 if (!bit(xhash, hashval1))
284 goto cont;
285#endif /* FASTHASH */
286 }
287 if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */
288 texec(*av, av);
289 else {
290 dp = Strspl(*pv, sav);
291#ifndef VFORK
292 cleanup_push(dp, xfree);
293#else /* VFORK */
294 Vdp = dp;
295#endif /* VFORK */
296
297 texec(dp, av);
298#ifndef VFORK
299 cleanup_until(dp);
300#else /* VFORK */
301 Vdp = 0;
302 xfree(dp);
303#endif /* VFORK */
304 }
305#ifdef VFORK
306 misses++;
307#endif /* VFORK */
308cont:
309 pv++;
310 i++;
311 } while (*pv);
312#ifdef VFORK
313 hits--;
314#endif /* VFORK */
94afa86d
JM
315 if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
316 dohash(NULL, NULL);
317 rehashed = 1;
318 goto retry;
319 }
7d8fb588
MS
320#ifndef VFORK
321 cleanup_until(sav);
322#else /* VFORK */
323 Vsav = 0;
324 xfree(sav);
325#endif /* VFORK */
326 pexerr();
327}
328
329static void
330pexerr(void)
331{
332 /* Couldn't find the damn thing */
333 if (expath) {
334 setname(short2str(expath));
335#ifdef VFORK
336 Vexpath = 0;
337#endif /* VFORK */
338 xfree(expath);
339 expath = 0;
340 }
341 else
342 setname("");
343 if (exerr)
344 stderror(ERR_NAME | ERR_STRING, exerr);
345 stderror(ERR_NAME | ERR_COMMAND);
346}
347
348/*
349 * Execute command f, arg list t.
350 * Record error message if not found.
351 * Also do shell scripts here.
352 */
353static void
354texec(Char *sf, Char **st)
355{
356 char **t;
357 char *f;
358 struct varent *v;
359 Char **vp;
360 Char *lastsh[2];
361 char pref[2];
362 int fd;
363 Char *st0, **ost;
364
365 /* The order for the conversions is significant */
366 t = short2blk(st);
367 f = short2str(sf);
368#ifdef VFORK
369 Vt = t;
370#endif /* VFORK */
371 errno = 0; /* don't use a previous error */
372#ifdef apollo
373 /*
374 * If we try to execute an nfs mounted directory on the apollo, we
375 * hang forever. So until apollo fixes that..
376 */
377 {
378 struct stat stb;
379 if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
380 errno = EISDIR;
381 }
382 if (errno == 0)
383#endif /* apollo */
384 {
385#ifdef ISC_POSIX_EXEC_BUG
386 __setostype(0); /* "0" is "__OS_SYSV" in <sys/user.h> */
387#endif /* ISC_POSIX_EXEC_BUG */
388 (void) execv(f, t);
389#ifdef ISC_POSIX_EXEC_BUG
390 __setostype(1); /* "1" is "__OS_POSIX" in <sys/user.h> */
391#endif /* ISC_POSIX_EXEC_BUG */
392 }
393#ifdef VFORK
394 Vt = 0;
395#endif /* VFORK */
396 blkfree((Char **) t);
397 switch (errno) {
398
399 case ENOEXEC:
400#ifdef WINNT_NATIVE
401 nt_feed_to_cmd(f,t);
402#endif /* WINNT_NATIVE */
403 /*
404 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
405 * it, don't feed it to the shell if it looks like a binary!
406 */
407 if ((fd = xopen(f, O_RDONLY|O_LARGEFILE)) != -1) {
408 int nread;
7d8fb588
MS
409 if ((nread = xread(fd, pref, 2)) == 2) {
410 if (!isprint((unsigned char)pref[0]) &&
411 (pref[0] != '\n' && pref[0] != '\t')) {
412 int err;
413
414 err = errno;
415 xclose(fd);
416 /*
417 * We *know* what ENOEXEC means.
418 */
419 stderror(ERR_ARCH, f, strerror(err));
420 }
421 }
422 else if (nread < 0) {
423#ifdef convex
424 int err;
425
426 err = errno;
427 xclose(fd);
428 /* need to print error incase the file is migrated */
429 stderror(ERR_SYSTEM, f, strerror(err));
430#endif
431 }
432#ifdef _PATH_BSHELL
433 else {
434 pref[0] = '#';
435 pref[1] = '\0';
436 }
437#endif
438 }
439#ifdef HASHBANG
440 if (fd == -1 ||
441 pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
442#endif /* HASHBANG */
443 /*
444 * If there is an alias for shell, then put the words of the alias in
445 * front of the argument list replacing the command name. Note no
446 * interpretation of the words at this point.
447 */
448 v = adrof1(STRshell, &aliases);
449 if (v == NULL || v->vec == NULL) {
450 vp = lastsh;
451 vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
452 vp[1] = NULL;
453#ifdef _PATH_BSHELL
d6ab524c 454 if (fd != -1
7d8fb588
MS
455# ifndef ISC /* Compatible with ISC's /bin/csh */
456 && pref[0] != '#'
457# endif /* ISC */
458 )
459 vp[0] = STR_BSHELL;
460#endif
461 vp = saveblk(vp);
462 }
463 else
464 vp = saveblk(v->vec);
465#ifdef HASHBANG
466 }
467#endif /* HASHBANG */
468 if (fd != -1)
469 xclose(fd);
470
471 st0 = st[0];
472 st[0] = sf;
473 ost = st;
474 st = blkspl(vp, st); /* Splice up the new arglst */
475 ost[0] = st0;
476 sf = *st;
477 /* The order for the conversions is significant */
478 t = short2blk(st);
479 f = short2str(sf);
480 xfree(st);
481 blkfree((Char **) vp);
482#ifdef VFORK
483 Vt = t;
484#endif /* VFORK */
485#ifdef ISC_POSIX_EXEC_BUG
486 __setostype(0); /* "0" is "__OS_SYSV" in <sys/user.h> */
487#endif /* ISC_POSIX_EXEC_BUG */
488 (void) execv(f, t);
489#ifdef ISC_POSIX_EXEC_BUG
490 __setostype(1); /* "1" is "__OS_POSIX" in <sys/user.h> */
491#endif /* ISC_POSIX_EXEC_BUG */
492#ifdef VFORK
493 Vt = 0;
494#endif /* VFORK */
495 blkfree((Char **) t);
496 /* The sky is falling, the sky is falling! */
497 stderror(ERR_SYSTEM, f, strerror(errno));
498 break;
499
500 case ENOMEM:
501 stderror(ERR_SYSTEM, f, strerror(errno));
502 break;
503
504#ifdef _IBMR2
505 case 0: /* execv fails and returns 0! */
506#endif /* _IBMR2 */
507 case ENOENT:
508 break;
509
510 default:
511 if (exerr == 0) {
512 exerr = strerror(errno);
513 xfree(expath);
514 expath = Strsave(sf);
515#ifdef VFORK
516 Vexpath = expath;
517#endif /* VFORK */
518 }
519 break;
520 }
521}
522
523struct execash_state
524{
525 int saveIN, saveOUT, saveDIAG, saveSTD;
526 int SHIN, SHOUT, SHDIAG, OLDSTD;
527 int didfds;
528#ifndef CLOSE_ON_EXEC
529 int didcch;
530#endif
531 struct sigaction sigint, sigquit, sigterm;
532};
533
534static void
535execash_cleanup(void *xstate)
536{
537 struct execash_state *state;
538
539 state = xstate;
540 sigaction(SIGINT, &state->sigint, NULL);
541 sigaction(SIGQUIT, &state->sigquit, NULL);
542 sigaction(SIGTERM, &state->sigterm, NULL);
543
544 doneinp = 0;
545#ifndef CLOSE_ON_EXEC
546 didcch = state->didcch;
547#endif /* CLOSE_ON_EXEC */
548 didfds = state->didfds;
549 xclose(SHIN);
550 xclose(SHOUT);
551 xclose(SHDIAG);
552 xclose(OLDSTD);
553 close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
554 close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
555 close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
556 close_on_exec(OLDSTD = dmove(state->saveSTD, state->OLDSTD), 1);
557}
558
559/*ARGSUSED*/
560void
561execash(Char **t, struct command *kp)
562{
563 struct execash_state state;
564
565 USE(t);
566 if (chkstop == 0 && setintr)
567 panystop(0);
568 /*
569 * Hmm, we don't really want to do that now because we might
570 * fail, but what is the choice
571 */
572 rechist(NULL, adrof(STRsavehist) != NULL);
573
574
575 sigaction(SIGINT, &parintr, &state.sigint);
576 sigaction(SIGQUIT, &parintr, &state.sigquit);
577 sigaction(SIGTERM, &parterm, &state.sigterm);
578
579 state.didfds = didfds;
580#ifndef CLOSE_ON_EXEC
581 state.didcch = didcch;
582#endif /* CLOSE_ON_EXEC */
583 state.SHIN = SHIN;
584 state.SHOUT = SHOUT;
585 state.SHDIAG = SHDIAG;
586 state.OLDSTD = OLDSTD;
587
588 (void)close_on_exec (state.saveIN = dcopy(SHIN, -1), 1);
589 (void)close_on_exec (state.saveOUT = dcopy(SHOUT, -1), 1);
590 (void)close_on_exec (state.saveDIAG = dcopy(SHDIAG, -1), 1);
591 (void)close_on_exec (state.saveSTD = dcopy(OLDSTD, -1), 1);
592
593 lshift(kp->t_dcom, 1);
594
595 (void)close_on_exec (SHIN = dcopy(0, -1), 1);
596 (void)close_on_exec (SHOUT = dcopy(1, -1), 1);
597 (void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
598#ifndef CLOSE_ON_EXEC
599 didcch = 0;
600#endif /* CLOSE_ON_EXEC */
601 didfds = 0;
602 cleanup_push(&state, execash_cleanup);
603
604 /*
653fab9e 605 * Decrement the shell level, if not in a subshell
7d8fb588 606 */
653fab9e
SW
607 if (mainpid == getpid())
608 shlvl(-1);
7d8fb588
MS
609#ifdef WINNT_NATIVE
610 __nt_really_exec=1;
611#endif /* WINNT_NATIVE */
612 doexec(kp, 1);
613
614 cleanup_until(&state);
615}
616
617void
618xechoit(Char **t)
619{
620 if (adrof(STRecho)) {
621 int odidfds = didfds;
622 flush();
623 haderr = 1;
624 didfds = 0;
625 blkpr(t), xputchar('\n');
626 flush();
627 didfds = odidfds;
628 haderr = 0;
629 }
630}
631
632/*ARGSUSED*/
633void
634dohash(Char **vv, struct command *c)
635{
636#ifdef COMMENT
637 struct stat stb;
638#endif
639 DIR *dirp;
640 struct dirent *dp;
641 int i = 0;
642 struct varent *v = adrof(STRpath);
643 Char **pv;
644 int hashval;
645#ifdef WINNT_NATIVE
646 int is_windir; /* check if it is the windows directory */
647 USE(hashval);
648#endif /* WINNT_NATIVE */
649
650 USE(c);
651#ifdef FASTHASH
652 if (vv && vv[1]) {
653 uhashlength = atoi(short2str(vv[1]));
654 if (vv[2]) {
655 uhashwidth = atoi(short2str(vv[2]));
d6ab524c
AHJ
656 if ((uhashwidth != sizeof(unsigned char)) &&
657 (uhashwidth != sizeof(unsigned short)) &&
7d8fb588
MS
658 (uhashwidth != sizeof(unsigned long)))
659 uhashwidth = 0;
660 if (vv[3])
661 hashdebug = atoi(short2str(vv[3]));
662 }
663 }
664
665 if (uhashwidth)
666 hashwidth = uhashwidth;
667 else {
668 hashwidth = 0;
669 if (v == NULL)
670 return;
671 for (pv = v->vec; pv && *pv; pv++, hashwidth++)
672 continue;
673 if (hashwidth <= widthof(unsigned char))
674 hashwidth = sizeof(unsigned char);
675 else if (hashwidth <= widthof(unsigned short))
676 hashwidth = sizeof(unsigned short);
677 else if (hashwidth <= widthof(unsigned int))
678 hashwidth = sizeof(unsigned int);
679 else
680 hashwidth = sizeof(unsigned long);
681 }
682
683 if (uhashlength)
684 hashlength = uhashlength;
685 else
686 hashlength = hashwidth * (8*64);/* "average" files per dir in path */
687
688 xfree(xhash);
689 xhash = xcalloc(hashlength * hashwidth, 1);
690#endif /* FASTHASH */
691
692 (void) getusername(NULL); /* flush the tilde cashe */
693 tw_cmd_free();
694 havhash = 1;
695 if (v == NULL)
696 return;
697 for (pv = v->vec; pv && *pv; pv++, i++) {
698 if (!ABSOLUTEP(pv[0]))
699 continue;
700 dirp = opendir(short2str(*pv));
701 if (dirp == NULL)
702 continue;
703 cleanup_push(dirp, opendir_cleanup);
704#ifdef COMMENT /* this isn't needed. opendir won't open
705 * non-dirs */
706 if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
707 cleanup_until(dirp);
708 continue;
709 }
710#endif
711#ifdef WINNT_NATIVE
712 is_windir = nt_check_if_windir(short2str(*pv));
713#endif /* WINNT_NATIVE */
714 while ((dp = readdir(dirp)) != NULL) {
715 if (dp->d_ino == 0)
716 continue;
717 if (dp->d_name[0] == '.' &&
718 (dp->d_name[1] == '\0' ||
719 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
720 continue;
721#ifdef WINNT_NATIVE
722 nt_check_name_and_hash(is_windir, dp->d_name, i);
723#else /* !WINNT_NATIVE*/
724#if defined(_UWIN) || defined(__CYGWIN__)
725 /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
726 * the file with the .exe, .com, .bat extension
94afa86d
JM
727 *
728 * Same for Cygwin, but only for .exe and .com extension.
7d8fb588
MS
729 */
730 {
731 ssize_t ext = strlen(dp->d_name) - 4;
732 if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
94afa86d 733#ifndef __CYGWIN__
7d8fb588 734 strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
94afa86d 735#endif
7d8fb588
MS
736 strcasecmp(&dp->d_name[ext], ".com") == 0)) {
737#ifdef __CYGWIN__
738 /* Also store the variation with extension. */
739 hashval = hashname(str2short(dp->d_name));
740 bis(hashval, i);
741#endif /* __CYGWIN__ */
742 dp->d_name[ext] = '\0';
743 }
744 }
745#endif /* _UWIN || __CYGWIN__ */
746# ifdef FASTHASH
747 hashval = hashname(str2short(dp->d_name));
748 bis(hashval, i);
749 if (hashdebug & 1)
750 xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
751 hashname(str2short(dp->d_name)), i, dp->d_name);
752# else /* OLD HASH */
753 hashval = hash(hashname(str2short(dp->d_name)), i);
754 bis(xhash, hashval);
755# endif /* FASTHASH */
756 /* tw_add_comm_name (dp->d_name); */
757#endif /* WINNT_NATIVE */
758 }
759 cleanup_until(dirp);
760 }
761}
762
763/*ARGSUSED*/
764void
765dounhash(Char **v, struct command *c)
766{
767 USE(c);
768 USE(v);
769 havhash = 0;
770#ifdef FASTHASH
771 xfree(xhash);
772 xhash = NULL;
773#endif /* FASTHASH */
774}
775
776/*ARGSUSED*/
777void
778hashstat(Char **v, struct command *c)
779{
780 USE(c);
781 USE(v);
d6ab524c 782#ifdef FASTHASH
7d8fb588
MS
783 if (havhash && hashlength && hashwidth)
784 xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
785 hashlength, hashwidth*8);
786 if (hashdebug)
787 xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
788#endif /* FASTHASH */
789#ifdef VFORK
790 if (hits + misses)
791 xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
792 hits, misses, 100 * hits / (hits + misses));
793#endif
794}
795
796
797/*
798 * Hash a command name.
799 */
800int
801hashname(Char *cp)
802{
803 unsigned long h;
804
805 for (h = 0; *cp; cp++)
806 h = hash(h, *cp);
807 return ((int) h);
808}
809
810static int
811iscommand(Char *name)
812{
94afa86d 813 Char **opv, **pv;
7d8fb588
MS
814 Char *sav;
815 struct varent *v;
816 int slash = any(short2str(name), '/');
94afa86d 817 int hashval, rehashed, i;
7d8fb588
MS
818
819 v = adrof(STRpath);
820 if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
94afa86d 821 opv = justabs;
7d8fb588 822 else
94afa86d 823 opv = v->vec;
7d8fb588
MS
824 sav = Strspl(STRslash, name); /* / command name for postpending */
825 hashval = havhash ? hashname(name) : 0;
94afa86d
JM
826
827 rehashed = 0;
828retry:
829 pv = opv;
7d8fb588
MS
830 i = 0;
831 do {
832 if (!slash && ABSOLUTEP(pv[0]) && havhash) {
833#ifdef FASTHASH
834 if (!bit(hashval, i))
835 goto cont;
836#else /* OLDHASH */
837 int hashval1 = hash(hashval, i);
838 if (!bit(xhash, hashval1))
839 goto cont;
840#endif /* FASTHASH */
841 }
842 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */
843 if (executable(NULL, name, 0)) {
844 xfree(sav);
845 return i + 1;
846 }
847 }
848 else {
849 if (executable(*pv, sav, 0)) {
850 xfree(sav);
851 return i + 1;
852 }
853 }
854cont:
855 pv++;
856 i++;
857 } while (*pv);
94afa86d
JM
858 if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
859 dohash(NULL, NULL);
860 rehashed = 1;
861 goto retry;
862 }
7d8fb588
MS
863 xfree(sav);
864 return 0;
865}
866
867/* Also by:
868 * Andreas Luik <luik@isaak.isa.de>
869 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
870 * Azenberstr. 35
871 * D-7000 Stuttgart 1
872 * West-Germany
873 * is the executable() routine below and changes to iscommand().
874 * Thanks again!!
875 */
876
877#ifndef WINNT_NATIVE
878/*
879 * executable() examines the pathname obtained by concatenating dir and name
880 * (dir may be NULL), and returns 1 either if it is executable by us, or
881 * if dir_ok is set and the pathname refers to a directory.
882 * This is a bit kludgy, but in the name of optimization...
883 */
884int
885executable(const Char *dir, const Char *name, int dir_ok)
886{
887 struct stat stbuf;
888 char *strname;
889
890 if (dir && *dir) {
891 Char *path;
892
893 path = Strspl(dir, name);
894 strname = short2str(path);
895 xfree(path);
896 }
897 else
898 strname = short2str(name);
899
900 return (stat(strname, &stbuf) != -1 &&
901 ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
902 (S_ISREG(stbuf.st_mode) &&
903 /* save time by not calling access() in the hopeless case */
904 (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
905 access(strname, X_OK) == 0
906 )));
907}
908#endif /*!WINNT_NATIVE*/
909
910struct tellmewhat_s0_cleanup
911{
912 Char **dest, *val;
913};
914
915static void
916tellmewhat_s0_cleanup(void *xstate)
917{
918 struct tellmewhat_s0_cleanup *state;
919
920 state = xstate;
921 *state->dest = state->val;
922}
923
924int
925tellmewhat(struct wordent *lexp, Char **str)
926{
927 struct tellmewhat_s0_cleanup s0;
928 int i;
929 const struct biltins *bptr;
930 struct wordent *sp = lexp->next;
931 int aliased = 0, found;
932 Char *s1, *s2, *cmd;
933 Char qc;
934
935 if (adrof1(sp->word, &aliases)) {
936 alias(lexp);
937 sp = lexp->next;
938 aliased = 1;
939 }
940
941 s0.dest = &sp->word; /* to get the memory freeing right... */
942 s0.val = sp->word;
943 cleanup_push(&s0, tellmewhat_s0_cleanup);
944
945 /* handle quoted alias hack */
946 if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
947 (sp->word)++;
948
949 /* do quoting, if it hasn't been done */
950 s1 = s2 = sp->word;
951 while (*s2)
952 switch (*s2) {
953 case '\'':
954 case '"':
955 qc = *s2++;
956 while (*s2 && *s2 != qc)
957 *s1++ = *s2++ | QUOTE;
958 if (*s2)
959 s2++;
960 break;
961 case '\\':
962 if (*++s2)
963 *s1++ = *s2++ | QUOTE;
964 break;
965 default:
966 *s1++ = *s2++;
967 }
968 *s1 = '\0';
969
970 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
971 if (eq(sp->word, str2short(bptr->bname))) {
972 if (str == NULL) {
973 if (aliased)
974 prlex(lexp);
d6ab524c 975 xprintf(CGETS(13, 5, "%" TCSH_S ": shell built-in command.\n"),
7d8fb588
MS
976 sp->word);
977 flush();
978 }
979 else
980 *str = Strsave(sp->word);
981 cleanup_until(&s0);
982 return TRUE;
983 }
984 }
985#ifdef WINNT_NATIVE
986 for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
987 if (eq(sp->word, str2short(bptr->bname))) {
988 if (str == NULL) {
989 if (aliased)
990 prlex(lexp);
d6ab524c 991 xprintf(CGETS(13, 5, "%" TCSH_S ": shell built-in command.\n"),
7d8fb588
MS
992 sp->word);
993 flush();
994 }
995 else
996 *str = Strsave(sp->word);
997 cleanup_until(&s0);
998 return TRUE;
999 }
1000 }
1001#endif /* WINNT_NATIVE*/
1002
1003 sp->word = cmd = globone(sp->word, G_IGNORE);
1004 cleanup_push(cmd, xfree);
1005
1006 if ((i = iscommand(sp->word)) != 0) {
1007 Char **pv;
1008 struct varent *v;
1009 int slash = any(short2str(sp->word), '/');
1010
1011 v = adrof(STRpath);
1012 if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
1013 pv = justabs;
1014 else
1015 pv = v->vec;
1016
1017 pv += i - 1;
1018 if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
1019 if (!slash) {
1020 sp->word = Strspl(STRdotsl, sp->word);
1021 cleanup_push(sp->word, xfree);
1022 prlex(lexp);
1023 cleanup_until(sp->word);
1024 }
1025 else
1026 prlex(lexp);
1027 }
1028 else {
1029 s1 = Strspl(*pv, STRslash);
1030 sp->word = Strspl(s1, sp->word);
1031 xfree(s1);
1032 cleanup_push(sp->word, xfree);
1033 if (str == NULL)
1034 prlex(lexp);
1035 else
1036 *str = Strsave(sp->word);
1037 cleanup_until(sp->word);
1038 }
1039 found = 1;
1040 }
1041 else {
1042 if (str == NULL) {
1043 if (aliased)
1044 prlex(lexp);
d6ab524c
AHJ
1045 xprintf(CGETS(13, 6, "%" TCSH_S ": Command not found.\n"),
1046 sp->word);
7d8fb588
MS
1047 flush();
1048 }
1049 else
1050 *str = Strsave(sp->word);
1051 found = 0;
1052 }
1053 cleanup_until(&s0);
1054 return found;
1055}
1056
1057/*
1058 * Builtin to look at and list all places a command may be defined:
1059 * aliases, shell builtins, and the path.
1060 *
1061 * Marc Horowitz <marc@mit.edu>
1062 * MIT Student Information Processing Board
1063 */
1064
1065/*ARGSUSED*/
1066void
1067dowhere(Char **v, struct command *c)
1068{
1069 int found = 1;
1070 USE(c);
60962bbc
JM
1071
1072 if (adrof(STRautorehash))
1073 dohash(NULL, NULL);
7d8fb588
MS
1074 for (v++; *v; v++)
1075 found &= find_cmd(*v, 1);
1076 /* Make status nonzero if any command is not found. */
1077 if (!found)
d6ab524c 1078 setstatus(1);
7d8fb588
MS
1079}
1080
1081int
1082find_cmd(Char *cmd, int prt)
1083{
1084 struct varent *var;
1085 const struct biltins *bptr;
1086 Char **pv;
1087 Char *sv;
94afa86d 1088 int hashval, rehashed, i, ex, rval = 0;
7d8fb588
MS
1089
1090 if (prt && any(short2str(cmd), '/')) {
57e3f2b5 1091 xprintf("%s", CGETS(13, 7, "where: / in command makes no sense\n"));
7d8fb588
MS
1092 return rval;
1093 }
1094
1095 /* first, look for an alias */
1096
1097 if (prt && adrof1(cmd, &aliases)) {
1098 if ((var = adrof1(cmd, &aliases)) != NULL) {
d6ab524c 1099 xprintf(CGETS(13, 8, "%" TCSH_S " is aliased to "), cmd);
7d8fb588
MS
1100 if (var->vec != NULL)
1101 blkpr(var->vec);
1102 xputchar('\n');
1103 rval = 1;
1104 }
1105 }
1106
1107 /* next, look for a shell builtin */
1108
1109 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
1110 if (eq(cmd, str2short(bptr->bname))) {
1111 rval = 1;
1112 if (prt)
d6ab524c 1113 xprintf(CGETS(13, 9, "%" TCSH_S " is a shell built-in\n"), cmd);
7d8fb588
MS
1114 else
1115 return rval;
1116 }
1117 }
1118#ifdef WINNT_NATIVE
1119 for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1120 if (eq(cmd, str2short(bptr->bname))) {
1121 rval = 1;
1122 if (prt)
d6ab524c 1123 xprintf(CGETS(13, 9, "%" TCSH_S " is a shell built-in\n"), cmd);
7d8fb588
MS
1124 else
1125 return rval;
1126 }
1127 }
1128#endif /* WINNT_NATIVE*/
1129
1130 /* last, look through the path for the command */
1131
1132 if ((var = adrof(STRpath)) == NULL)
1133 return rval;
1134
1135 hashval = havhash ? hashname(cmd) : 0;
1136
1137 sv = Strspl(STRslash, cmd);
1138 cleanup_push(sv, xfree);
1139
94afa86d
JM
1140 rehashed = 0;
1141retry:
7d8fb588
MS
1142 for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
1143 if (havhash && !eq(*pv, STRdot)) {
1144#ifdef FASTHASH
1145 if (!bit(hashval, i))
1146 continue;
1147#else /* OLDHASH */
1148 int hashval1 = hash(hashval, i);
1149 if (!bit(xhash, hashval1))
1150 continue;
1151#endif /* FASTHASH */
1152 }
1153 ex = executable(*pv, sv, 0);
1154#ifdef FASTHASH
1155 if (!ex && (hashdebug & 2)) {
57e3f2b5 1156 xprintf("%s", CGETS(13, 10, "hash miss: "));
7d8fb588
MS
1157 ex = 1; /* Force printing */
1158 }
1159#endif /* FASTHASH */
1160 if (ex) {
1161 rval = 1;
1162 if (prt) {
d6ab524c
AHJ
1163 xprintf("%" TCSH_S "/", *pv);
1164 xprintf("%" TCSH_S "\n", cmd);
7d8fb588
MS
1165 }
1166 else
1167 return rval;
1168 }
1169 }
60962bbc 1170 /*
d6ab524c 1171 * If we are printing, we are being called from dowhere() which it
60962bbc
JM
1172 * has rehashed already
1173 */
1174 if (!prt && adrof(STRautorehash) && !rehashed && havhash) {
94afa86d
JM
1175 dohash(NULL, NULL);
1176 rehashed = 1;
1177 goto retry;
1178 }
7d8fb588
MS
1179 cleanup_until(sv);
1180 return rval;
1181}
1182#ifdef WINNT_NATIVE
1183int hashval_extern(cp)
1184 Char *cp;
1185{
1186 return havhash?hashname(cp):0;
1187}
1188int bit_extern(val,i)
1189 int val;
1190 int i;
1191{
1192 return bit(val,i);
1193}
1194void bis_extern(val,i)
1195 int val;
1196 int i;
1197{
1198 bis(val,i);
1199}
1200#endif /* WINNT_NATIVE */
1201