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