source: trunk/third/tcsh/sh.exec.c @ 12039

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