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

Defined functions

doexec defined in line 100; used 3 times
dounhash defined in line 490; used 1 times
execash defined in line 404; used 1 times
executable defined in line 582; used 9 times
hashname defined in line 510; used 3 times
hashstat defined in line 497; used 1 times
iscommand defined in line 521; used 2 times
pexerr defined in line 259; used 3 times
rcsid defined in line 39; never used
tellmewhat defined in line 606; used 1 times
texec defined in line 283; used 2 times
xechoit defined in line 429; used 3 times

Defined variables

exerr defined in line 64; used 5 times
expath defined in line 65; used 13 times
hits defined in line 89; used 6 times
justabs defined in line 94; used 3 times
misses defined in line 89; used 4 times
xhash defined in line 83; used 5 times

Defined macros

HSHMASK defined in line 81; used 1 times
  • in line 85
HSHMUL defined in line 82; used 1 times
  • in line 85
HSHSIZ defined in line 80; used 2 times
bis defined in line 87; used 1 times
bit defined in line 86; used 2 times
hash defined in line 85; used 4 times
Last modified: 1991-08-21
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4498
Valid CSS Valid XHTML 1.0 Strict