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