1: static  char    *sccsid[] = "@(#)sh.c	2.1"; /*	SCCS id keyword	*/
   2: /* Copyright (c) 1980 Regents of the University of California */
   3: #include "sh.h"
   4: 
   5: /*
   6:  * C Shell
   7:  *
   8:  * Bill Joy, UC Berkeley
   9:  * October, 1978
  10:  */
  11: 
  12: char    *pathlist[] =   { SRCHPATH, 0 };
  13: 
  14: main(c, av)
  15:     int c;
  16:     char **av;
  17: {
  18:     register char **v, *cp;
  19:     int nofile = 0;
  20:     int reenter = 0;
  21:     bool nverbose = 0, nexececho = 0, quitit = 0, fast = 0, prompt = 1;
  22:     char *hp;
  23: 
  24:     settimes();         /* Immed. estab. timing base */
  25:     hp = getenv("HOME");
  26:     v = av;
  27:     if (eq(v[0], "a.out"))      /* A.out's are quittable */
  28:         quitit = 1;
  29:     uid = getuid();
  30: #ifdef V6
  31:     loginsh = eq(*v, "-");      /* To do .login/.logout */
  32: #else
  33:     loginsh = **v == '-';
  34: #endif
  35:     if (loginsh)
  36:         time(&chktim);
  37: 
  38:     /*
  39: 	 * Move the descriptors to safe places.
  40: 	 * The variable didfds is 0 while we have only FSH* to work with.
  41: 	 * When didfds is true, we have 0,1,2 and prefer to use these.
  42: 	 */
  43:     initdesc();
  44: 
  45:     /*
  46: 	 * Initialize the shell variables.
  47: 	 * ARGV and PROMPT are initialized later.
  48: 	 * STATUS is also munged in several places.
  49: 	 * CHILD is munged when forking/waiting
  50: 	 */
  51: 
  52:     set("status", "0");
  53: 
  54:     /* Default history characters */
  55:     HIST = '!'; HISTSUB = '^';
  56: 
  57:     if (hp == 0)
  58:         fast++;         /* No home -> can't read scripts */
  59:     else
  60:         set("home", hp);
  61:     if (uid == 0)
  62:         pathlist[0] = "/etc";
  63:     set1("path", saveblk(pathlist), &shvhed);
  64:     /*
  65: 	 * Re-initialize path if set in environment
  66: 	 */
  67:     cp = getenv("PATH");
  68:     if (cp != 0) {
  69:         importpath(cp);
  70:     }
  71: 
  72:     set("shell", SHELLPATH);
  73: 
  74:     doldol = putn(getpid());        /* For $$ */
  75:     shtemp = strspl("/tmp/sh", doldol); /* For << */
  76: 
  77:     /*
  78: 	 * Record the interrupt states from the parent process.
  79: 	 * If the parent is non-interruptible our hand must be forced
  80: 	 * or we (and our children) won't be either.
  81: 	 * Our children inherit termination from our parent.
  82: 	 * We catch it only if we are the login shell.
  83: 	 */
  84:     parintr = signal(SIGINT, SIG_IGN);  /* parents interruptibility */
  85:     signal(SIGINT, parintr);            /* ... restore */
  86:     parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */
  87:     signal(SIGTERM, parterm);           /* ... restore */
  88: 
  89:     /*
  90: 	 * Process the arguments.
  91: 	 *
  92: 	 * Note that processing of -v/-x is actually delayed till after
  93: 	 * script processing.
  94: 	 *
  95: 	 * We set the first character of our name to be '-' if we are
  96: 	 * a shell running interruptible commands.  Many programs which
  97: 	 * examine ps'es use this to filter such shells out.
  98: 	 */
  99:     c--, v++;
 100:     while (c > 0 && (cp = v[0])[0] == '-') {
 101:         do switch (*cp++) {
 102: 
 103:         case 0:         /* -	Interruptible, no prompt */
 104:             prompt = 0;
 105:             **av = '-';
 106:             nofile++;
 107:             break;
 108: 
 109:         case 'c':       /* -c	Command input from arg */
 110:             if (c == 1)
 111:                 exit(0);
 112:             c--, v++;
 113:             arginp = v[0];
 114:             prompt = 0;
 115:             nofile++;
 116:             break;
 117: 
 118:         case 'e':       /* -e	Exit on any error */
 119:             exiterr++;
 120:             break;
 121: 
 122:         case 'f':       /* -f	Fast start */
 123:             fast++;
 124:             break;
 125: 
 126:         case 'i':       /* -i	Interactive, even if !intty */
 127:             intact++;
 128:             **av = '-';
 129:             nofile++;
 130:             break;
 131: 
 132:         case 'n':       /* -n	Don't execute */
 133:             noexec++;
 134:             break;
 135: 
 136:         case 'q':       /* -q	(Undoc'd) ... die on quit */
 137:             quitit = 1;
 138:             break;
 139: 
 140:         case 's':       /* -s	Read from std input */
 141:             nofile++;
 142:             if (isatty(SHIN))
 143:                 **v = '-';
 144:             break;
 145: 
 146:         case 't':       /* -t	Read one line from input */
 147:             onelflg = 2;
 148:             if (isatty(SHIN))
 149:                 **v = '-';
 150:             prompt = 0;
 151:             nofile++;
 152:             break;
 153: 
 154:         case 'v':       /* -v	Echo hist expanded input */
 155:             nverbose = 1;           /* ... later */
 156:             break;
 157: 
 158:         case 'x':       /* -x	Echo just before execution */
 159:             nexececho = 1;          /* ... later */
 160:             break;
 161: 
 162:         case 'V':       /* -V	Echo hist expanded input */
 163:             setNS("verbose");       /* NOW! */
 164:             break;
 165: 
 166:         case 'X':       /* -X	Echo just before execution */
 167:             setNS("echo");          /* NOW! */
 168:             break;
 169: 
 170:         } while (*cp);
 171:         v++, c--;
 172:     }
 173: 
 174:     if (quitit)         /* With all due haste, for debugging */
 175:         signal(SIGQUIT, SIG_DFL);
 176: 
 177:     /*
 178: 	 * Unless prevented by -, -c, -i, -s, or -t, if there
 179: 	 * are remaining arguments the first of them is the name
 180: 	 * of a shell file from which to read commands.
 181: 	 */
 182:     if (nofile == 0 && c > 0) {
 183:         nofile = open(v[0], 0);
 184:         if (nofile < 0) {
 185:             child++;        /* So this ... */
 186:             Perror(v[0]);       /* ... doesn't return */
 187:         }
 188:         file = v[0];
 189:         SHIN = dmove(nofile, FSHIN);    /* Replace FSHIN */
 190:         prompt = 0;
 191:         c--, v++;
 192:     }
 193: 
 194:     /*
 195: 	 * Consider input a tty if it really is or we are interactive.
 196: 	 */
 197:     intty = intact || isatty(SHIN);
 198: #ifdef TELL
 199:     settell();
 200: #endif
 201:     /*
 202: 	 * Commands are interruptible if we are interactive
 203: 	 * or the process which created us was.
 204: 	 */
 205:     if (intact || parintr == SIG_DFL)
 206:         **av = '-';
 207: 
 208:     /*
 209: 	 * Save the remaining arguments in ARGV.
 210: 	 * Normally the system-supplied argument list is ok as
 211: 	 * a zero terminated value block.
 212: 	 * On some version 6 systems, it is -1 terminated and setting it
 213: 	 * to zero messes up "ps" so we change it to zero, copy
 214: 	 * the block of pointers, and put it back the way it was.
 215: 	 */
 216: /*
 217: 	if (c == 0)
 218: 		set("argv", 0);
 219: 	else
 220:  */
 221:     if ((int) v[c] == -1) {
 222:         /* ick */
 223:         v[c] = 0, setq("argv", copyblk(v), &shvhed), v[c] = (char *) -1;
 224:     } else
 225:         setq("argv", v, &shvhed);
 226: 
 227:     /*
 228: 	 * Set up the prompt.
 229: 	 */
 230:     if (prompt)
 231:         set("prompt", uid == 0 ? "# " : "% ");
 232: 
 233:     /*
 234: 	 * If we are an interactive shell, then start fiddling
 235: 	 * with the signals; this is a tricky game.
 236: 	 */
 237:     if (**av == '-') {
 238:         setintr++;
 239:         if (!quitit)        /* Wary! */
 240:             signal(SIGQUIT, SIG_IGN);
 241:         signal(SIGINT, SIG_IGN);
 242:         signal(SIGTERM, SIG_IGN);
 243:     }
 244: 
 245:     /*
 246: 	 * Set an exit here in case of an interrupt or error reading
 247: 	 * the shell start-up scripts.
 248: 	 */
 249:     setexit();
 250:     haderr = 0;     /* In case second time through */
 251:     if (!fast && reenter == 0) {
 252:         reenter++;
 253:         /* Will have value("home") here because set fast if don't */
 254:         srccat(value("home"), "/.cshrc");
 255:         if (!fast && !arginp && !onelflg)
 256:             dohash();
 257:         if (loginsh)
 258: #ifdef NOHELP
 259:             srccat("", ".login");
 260: #else
 261:             srccat(value("home"), "/.login");
 262: #endif
 263:     }
 264: 
 265:     /*
 266: 	 * Now are ready for the -v and -x flags
 267: 	 */
 268:     if (nverbose)
 269:         setNS("verbose");
 270:     if (nexececho)
 271:         setNS("echo");
 272: 
 273:     /*
 274: 	 * All the rest of the world is inside this call.
 275: 	 * The argument to process indicates whether it should
 276: 	 * catch "error unwinds".  Thus if we are a interactive shell
 277: 	 * our call here will never return by being blown past on an error.
 278: 	 */
 279:     process(setintr);
 280: 
 281:     /*
 282: 	 * Mop-up.
 283: 	 */
 284:     if (loginsh) {
 285:         printf("logout\n");
 286:         close(SHIN);
 287:         child++;
 288:         goodbye();
 289:     }
 290:     exitstat();
 291: }
 292: 
 293: importpath(cp)
 294: char *cp;
 295: {
 296:     register int i = 0;
 297:     register char *dp;
 298:     register char **pv;
 299:     int c;
 300:     static char dot[2] = {'.', 0};
 301: 
 302:     for (dp = cp; *dp; dp++)
 303:         if (*dp == ':')
 304:             i++;
 305:     /*
 306: 	 * i+2 where i is the number of colons in the path.
 307: 	 * There are i+1 directories in the path plus we need
 308: 	 * room for a zero terminator.
 309: 	 */
 310:     pv = (char **) calloc(i+2, sizeof (char **));
 311:     dp = cp;
 312:     i = 0;
 313:     for (;;) {
 314:         if ((c = *dp) == ':' || c == 0) {
 315:             *dp = 0;
 316:             pv[i++] = savestr(*cp ? cp : dot);
 317:             if (c) {
 318:                 cp = dp + 1;
 319:                 *dp = ':';
 320:             } else
 321:                 break;
 322:         }
 323:         dp++;
 324:     }
 325:     pv[i] = 0;
 326:     set1("path", pv, &shvhed);
 327:     dohash();
 328: }
 329: 
 330: /*
 331:  * Source to the file which is the catenation of the argument names.
 332:  */
 333: srccat(cp, dp)
 334:     char *cp, *dp;
 335: {
 336:     register char *ep = strspl(cp, dp);
 337:     register int unit = dmove(open(ep, 0), -1);
 338: 
 339:     /* ioctl(unit, FIOCLEX, NULL); */
 340:     xfree(ep);
 341:     srcunit(unit, 0);
 342: }
 343: 
 344: /*
 345:  * Source to a unit.  If onlyown it must be our file or
 346:  * we don't chance it.	This occurs on ".cshrc"s and the like.
 347:  */
 348: srcunit(unit, onlyown)
 349:     register int unit;
 350:     bool onlyown;
 351: {
 352:     /* We have to push down a lot of state here */
 353:     /* All this could go into a structure */
 354:     int oSHIN = -1, oldintty = intty;
 355:     struct whyle *oldwhyl = whyles;
 356:     char *ogointr = gointr, *oarginp = arginp;
 357:     int oonelflg = onelflg;
 358: #ifdef TELL
 359:     bool otell = cantell;
 360: #endif
 361:     struct Bin saveB;
 362: 
 363:     /* The (few) real local variables */
 364:     jmp_buf oldexit;
 365:     int reenter;
 366:     register int (*oldint)();
 367: 
 368:     if (unit < 0)
 369:         return;
 370:     if (onlyown) {
 371:         struct stat stb;
 372: 
 373: #ifdef V69
 374:         if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) {
 375: #else
 376:         if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_gid != getgid())) {
 377: #endif
 378:             close(unit);
 379:             return;
 380:         }
 381:     }
 382: 
 383:     /*
 384: 	 * There is a critical section here while we are pushing down the
 385: 	 * input stream since we have stuff in different structures.
 386: 	 * If we weren't careful an interrupt could corrupt SHIN's Bin
 387: 	 * structure and kill the shell.
 388: 	 *
 389: 	 * We could avoid the critical region by grouping all the stuff
 390: 	 * in a single structure and pointing at it to move it all at
 391: 	 * once.  This is less efficient globally on many variable references
 392: 	 * however.
 393: 	 */
 394:     getexit(oldexit);
 395:     reenter = 0;
 396:     oldint = signal(SIGINT, SIG_IGN);
 397:     setexit();
 398:     reenter++;
 399:     if (reenter == 1) {
 400:         /* Setup the new values of the state stuff saved above */
 401:         copy(&saveB, &B, sizeof saveB);
 402:         fbuf = (char **) 0;
 403:         fseekp = feobp = fblocks = 0;
 404:         oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
 405:         intty = isatty(SHIN), whyles = 0, gointr = 0;
 406:         /*
 407: 		 * Now if we are allowing commands to be interrupted,
 408: 		 * we let ourselves be interrupted.
 409: 		 */
 410:         signal(SIGINT, setintr ? pintr : oldint);
 411: #ifdef TELL
 412:         settell();
 413: #endif
 414:         process(0);     /* 0 -> blow away on errors */
 415:     }
 416:     signal(SIGINT, oldint);
 417:     if (oSHIN >= 0) {
 418:         register int i;
 419: 
 420:         /* We made it to the new state... free up its storage */
 421:         /* This code could get run twice but xfree doesn't care */
 422:         for (i = 0; i < fblocks; i++)
 423:             xfree(fbuf[i]);
 424:         xfree(fbuf);
 425: 
 426:         /* Reset input arena */
 427:         copy(&B, &saveB, sizeof B);
 428: 
 429:         close(SHIN), SHIN = oSHIN;
 430:         arginp = oarginp, onelflg = oonelflg;
 431:         intty = oldintty, whyles = oldwhyl, gointr = ogointr;
 432: #ifdef TELL
 433:         cantell = otell;
 434: #endif
 435:     }
 436: 
 437:     resexit(oldexit);
 438:     /*
 439: 	 * If process reset() (effectively an unwind) then
 440: 	 * we must also unwind.
 441: 	 */
 442:     if (reenter >= 2)
 443:         error(0);
 444: }
 445: 
 446: goodbye()
 447: {
 448: 
 449:     if (loginsh) {
 450:         signal(SIGQUIT, SIG_IGN);
 451:         signal(SIGINT, SIG_IGN);
 452:         signal(SIGTERM, SIG_IGN);
 453:         setintr = 0;        /* No interrupts after "logout" */
 454:         if (adrof("home"))
 455:             srccat(value("home"), "/.logout");
 456:     }
 457:     exitstat();
 458: }
 459: 
 460: exitstat()
 461: {
 462: 
 463:     /*
 464: 	 * Note that if STATUS is corrupted (i.e. getn bombs)
 465: 	 * then error will exit directly because we poke child here.
 466: 	 * Otherwise we might continue unwarrantedly (sic).
 467: 	 */
 468:     child++;
 469:     exit(getn(value("status")));
 470: }
 471: 
 472: /*
 473:  * Catch an interrupt, e.g. during lexical input.
 474:  * If we are an interactive shell, we reset the interrupt catch
 475:  * immediately.  In any case we drain the shell output,
 476:  * and finally go through the normal error mechanism, which
 477:  * gets a chance to make the shell go away.
 478:  */
 479: pintr()
 480: {
 481:     register char **v;
 482: 
 483:     if (setintr)
 484:         signal(SIGINT, SIG_IGN);
 485:     draino();
 486: 
 487:     /*
 488: 	 * If we have an active "onintr" then we search for the label.
 489: 	 * Note that if one does "onintr -" then we shan't be interruptible
 490: 	 * so we needn't worry about that here.
 491: 	 */
 492:     if (gointr) {
 493:         search(ZGOTO, 0, gointr);
 494:         timflg = 0;
 495:         if (v = pargv)
 496:             pargv = 0, blkfree(v);
 497:         if (v = gargv)
 498:             gargv = 0, blkfree(v);
 499:         reset();
 500:     } else if (intty)
 501:         printf("\n");       /* Some like this, others don't */
 502:     error(0);
 503: }
 504: 
 505: /*
 506:  * Process is the main driving routine for the shell.
 507:  * It runs all command processing, except for those within { ... }
 508:  * in expressions (which is run by a routine evalav in sh.exp.c which
 509:  * is a stripped down process), and `...` evaluation which is run
 510:  * also by a subset of this code in sh.glob.c in the routine backeval.
 511:  *
 512:  * The code here is a little strange because part of it is interruptible
 513:  * and hence freeing of structures appears to occur when none is necessary
 514:  * if this is ignored.
 515:  *
 516:  * Note that if catch is not set then we will unwind on any error.
 517:  * In an end-of-file occurs, we return.
 518:  */
 519: process(catch)
 520:     bool catch;
 521: {
 522:     register char *cp;
 523:     jmp_buf osetexit;
 524:     struct command *t;
 525: 
 526:     getexit(osetexit);
 527:     for (;;) {
 528:         paraml.next = paraml.prev = &paraml;
 529:         paraml.word = "";
 530:         t = 0;
 531:         setexit();
 532:         justpr = 0;         /* A chance to execute */
 533: 
 534:         /*
 535: 		 * Interruptible during interactive reads
 536: 		 */
 537:         if (setintr)
 538:             signal(SIGINT, pintr);
 539: 
 540:         /*
 541: 		 * For the sake of reset()
 542: 		 */
 543:         freelex(&paraml), freesyn(t), t = 0;
 544: 
 545:         if (haderr) {
 546:             if (!catch) {
 547:                 /* unwind */
 548:                 doneinp = 0;
 549:                 resexit(osetexit);
 550:                 reset();
 551:             }
 552:             haderr = 0;
 553:             /*
 554: 			 * Every error is eventually caught here or
 555: 			 * the shell dies.  It is at this
 556: 			 * point that we clean up any left-over open
 557: 			 * files, by closing all but a fixed number
 558: 			 * of pre-defined files.  Thus routines don't
 559: 			 * have to worry about leaving files open due
 560: 			 * to deeper errors... they will get closed here.
 561: 			 */
 562:             closem();
 563:             continue;
 564:         }
 565:         if (doneinp) {
 566:             doneinp = 0;
 567:             break;
 568:         }
 569:         if (intty) {
 570:             mailchk();
 571:             /*
 572: 			 * If we are at the end of the input buffer
 573: 			 * then we are going to read fresh stuff.
 574: 			 * Otherwise, we are rereading input and don't
 575: 			 * need or want to prompt.
 576: 			 */
 577:             if (fseekp == feobp)
 578:                 if (!whyles)
 579:                     for (cp = value("prompt"); *cp; cp++)
 580:                         if (*cp == HIST)
 581:                             printf("%d", eventno + 1);
 582:                         else {
 583:                             if (*cp == '\\' && cp[1] == HIST)
 584:                                 cp++;
 585:                             putchar(*cp | QUOTE);
 586:                         }
 587:                 else
 588:                     /*
 589: 					 * Prompt for forward reading loop
 590: 					 * body content.
 591: 					 */
 592:                     printf("? ");
 593:             flush();
 594:         }
 595:         err = 0;
 596: 
 597:         /*
 598: 		 * Echo not only on VERBOSE, but also with history expansion.
 599: 		 * If there is a lexical error then we forego history echo.
 600: 		 */
 601:         if (lex(&paraml) && !err && intty || adrof("verbose")) {
 602:             haderr = 1;
 603:             prlex(&paraml);
 604:             haderr = 0;
 605:         }
 606: 
 607:         /*
 608: 		 * The parser may lose space if interrupted.
 609: 		 */
 610:         if (setintr)
 611:             signal(SIGINT, SIG_IGN);
 612: 
 613:         /*
 614: 		 * Save input text on the history list if it
 615: 		 * is from the terminal at the top level and not
 616: 		 * in a loop.
 617: 		 */
 618:         if (catch && intty && !whyles)
 619:             savehist(&paraml);
 620: 
 621:         /*
 622: 		 * Print lexical error messages.
 623: 		 */
 624:         if (err)
 625:             error(err);
 626: 
 627:         /*
 628: 		 * If had a history command :p modifier then
 629: 		 * this is as far as we should go
 630: 		 */
 631:         if (justpr)
 632:             reset();
 633: 
 634:         alias(&paraml);
 635: 
 636:         /*
 637: 		 * Parse the words of the input into a parse tree.
 638: 		 */
 639:         t = syntax(paraml.next, &paraml, 0);
 640:         if (err)
 641:             error(err);
 642: 
 643:         /*
 644: 		 * Execute the parse tree
 645: 		 */
 646:         execute(t);
 647: 
 648:         /*
 649: 		 * Made it!
 650: 		 */
 651:         freelex(&paraml), freesyn(t);
 652:     }
 653:     resexit(osetexit);
 654: }
 655: 
 656: dosource(t)
 657:     register char **t;
 658: {
 659:     register char *f;
 660:     register int u;
 661: 
 662:     t++;
 663:     f = globone(*t);
 664:     u = dmove(open(f, 0), -1);
 665:     xfree(f);
 666:     if (u < 0)
 667:         Perror(f);
 668:     didfds = 0;
 669:     srcunit(u, 0);
 670: }
 671: 
 672: /*
 673:  * Check for mail.
 674:  * If we are a login shell, then we don't want to tell
 675:  * about any mail file unless its been modified
 676:  * after the time we started.
 677:  * This prevents us from telling the user things he already
 678:  * knows, since the login program insists on saying
 679:  * "You have mail."
 680:  */
 681: mailchk()
 682: {
 683:     register struct varent *v;
 684:     register char **vp;
 685:     time_t t;
 686:     int intvl, cnt;
 687: 
 688:     v = adrof("mail");
 689:     if (v == 0)
 690:         return;
 691:     time(&t);
 692:     vp = v->vec;
 693:     cnt = blklen(vp);
 694:     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
 695:     if (intvl < 1)
 696:         intvl = 1;
 697:     if (chktim + intvl > t)
 698:         return;
 699:     for (; *vp; vp++) {
 700:         bool new;
 701:         struct stat stb;
 702: 
 703:         if (stat(*vp, &stb) < 0)
 704:             continue;
 705:         /*
 706: 		 * We assume that a file has been read if the access time is
 707: 		 * greater than the mod time.
 708: 		 */
 709:         if (stb.st_size == 0)
 710:             continue;
 711:         if (stb.st_atime > stb.st_mtime || (stb.st_atime < chktim && stb.st_mtime < chktim))
 712:             continue;
 713:         new = stb.st_mtime > time0;
 714:         if (loginsh && !new)
 715:             continue;
 716:         if (cnt == 1)
 717:             printf("You have %smail.\n", new ? "new " : "");
 718:         else
 719:             printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
 720:     }
 721:     chktim = t;
 722: }
 723: 
 724: #include <pwd.h>
 725: /*
 726:  * Extract a home directory from the password file
 727:  * The argument points to a buffer where the name of the
 728:  * user whose home directory is sought is currently.
 729:  * We write the home directory of the user back there.
 730:  */
 731: gethdir(home)
 732:     char *home;
 733: {
 734:     register struct passwd *pp;
 735: 
 736:     pp = getpwnam(home);
 737:     if (pp == 0)
 738:         return (1);
 739:     strcpy(home, pp->pw_dir);
 740:     return (0);
 741: }
 742: 
 743: /*
 744:  * Move the initial descriptors to their eventual
 745:  * resting places, closin all other units.
 746:  */
 747: initdesc()
 748: {
 749: 
 750:     didcch = 0;         /* Havent closed for child */
 751:     didfds = 0;         /* 0, 1, 2 aren't set up */
 752:     SHIN = dcopy(0, FSHIN);
 753:     SHOUT = dcopy(1, FSHOUT);
 754:     SHDIAG = dcopy(2, FSHDIAG);
 755:     OLDSTD = dcopy(SHIN, FOLDSTD);
 756:     closem();
 757: }
 758: 
 759: #ifndef V6
 760: exit(i)
 761:     int i;
 762: {
 763: 
 764:     _exit(i);
 765: }
 766: #endif

Defined functions

dosource defined in line 656; used 4 times
exit defined in line 760; used 4 times
gethdir defined in line 731; used 1 times
goodbye defined in line 446; used 4 times
importpath defined in line 293; used 2 times
initdesc defined in line 747; used 2 times
mailchk defined in line 681; used 1 times
main defined in line 14; never used
pintr defined in line 479; used 5 times
process defined in line 519; used 2 times
srccat defined in line 333; used 4 times
srcunit defined in line 348; used 2 times

Defined variables

pathlist defined in line 12; used 2 times
sccsid defined in line 1; never used
Last modified: 1981-12-05
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1616
Valid CSS Valid XHTML 1.0 Strict