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