1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley Software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if !defined(lint) && defined(DOSCCS) 8: static char *sccsid = "@(#)sh.c 5.3 (Berkeley) 3/29/86"; 9: #endif 10: 11: #include "sh.h" 12: #include <sys/ioctl.h> 13: /* 14: * C Shell 15: * 16: * Bill Joy, UC Berkeley, California, USA 17: * October 1978, May 1980 18: * 19: * Jim Kulp, IIASA, Laxenburg, Austria 20: * April 1980 21: */ 22: 23: char *pathlist[] = { ".", "/usr/ucb", "/bin", "/usr/bin", 0 }; 24: char *dumphist[] = { "history", "-h", 0, 0 }; 25: char *loadhist[] = { "source", "-h", "~/.history", 0 }; 26: char HIST = '!'; 27: char HISTSUB = '^'; 28: bool nofile; 29: bool reenter; 30: bool nverbose; 31: bool nexececho; 32: bool quitit; 33: bool fast; 34: bool batch; 35: bool prompt = 1; 36: bool enterhist = 0; 37: 38: extern gid_t getegid(), getgid(); 39: extern uid_t geteuid(), getuid(); 40: 41: main(c, av) 42: int c; 43: char **av; 44: { 45: register char **v, *cp; 46: register int f; 47: struct sigvec osv; 48: 49: settimes(); /* Immed. estab. timing base */ 50: v = av; 51: if (eq(v[0], "a.out")) /* A.out's are quittable */ 52: quitit = 1; 53: uid = getuid(); 54: loginsh = **v == '-' && c == 1; 55: if (loginsh) 56: (void) time(&chktim); 57: 58: /* 59: * Move the descriptors to safe places. 60: * The variable didfds is 0 while we have only FSH* to work with. 61: * When didfds is true, we have 0,1,2 and prefer to use these. 62: */ 63: initdesc(); 64: 65: /* 66: * Initialize the shell variables. 67: * ARGV and PROMPT are initialized later. 68: * STATUS is also munged in several places. 69: * CHILD is munged when forking/waiting 70: */ 71: 72: set("status", "0"); 73: dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a 74: * login shell */ 75: if (cp == NOSTR) 76: fast++; /* No home -> can't read scripts */ 77: else 78: set("home", savestr(cp)); 79: /* 80: * Grab other useful things from the environment. 81: * Should we grab everything?? 82: */ 83: if ((cp = getenv("USER")) != NOSTR) 84: set("user", savestr(cp)); 85: if ((cp = getenv("TERM")) != NOSTR) 86: set("term", savestr(cp)); 87: /* 88: * Re-initialize path if set in environment 89: */ 90: if ((cp = getenv("PATH")) == NOSTR) 91: set1("path", saveblk(pathlist), &shvhed); 92: else 93: importpath(cp); 94: set("shell", SHELLPATH); 95: 96: doldol = putn(getpid()); /* For $$ */ 97: shtemp = strspl("/tmp/sh", doldol); /* For << */ 98: 99: /* 100: * Record the interrupt states from the parent process. 101: * If the parent is non-interruptible our hand must be forced 102: * or we (and our children) won't be either. 103: * Our children inherit termination from our parent. 104: * We catch it only if we are the login shell. 105: */ 106: /* parents interruptibility */ 107: (void) sigvec(SIGINT, (struct sigvec *)0, &osv); 108: parintr = osv.sv_handler; 109: /* parents terminability */ 110: (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); 111: parterm = osv.sv_handler; 112: if (loginsh) { 113: (void) signal(SIGHUP, phup); /* exit processing on HUP */ 114: (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 115: (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 116: } 117: 118: /* 119: * Process the arguments. 120: * 121: * Note that processing of -v/-x is actually delayed till after 122: * script processing. 123: */ 124: c--, v++; 125: while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { 126: do switch (*cp++) { 127: 128: case 'b': /* -b Next arg is input file */ 129: batch++; 130: break; 131: 132: case 'c': /* -c Command input from arg */ 133: if (c == 1) 134: exit(0); 135: c--, v++; 136: arginp = v[0]; 137: prompt = 0; 138: nofile++; 139: break; 140: 141: case 'e': /* -e Exit on any error */ 142: exiterr++; 143: break; 144: 145: case 'f': /* -f Fast start */ 146: fast++; 147: break; 148: 149: case 'i': /* -i Interactive, even if !intty */ 150: intact++; 151: nofile++; 152: break; 153: 154: case 'n': /* -n Don't execute */ 155: noexec++; 156: break; 157: 158: case 'q': /* -q (Undoc'd) ... die on quit */ 159: quitit = 1; 160: break; 161: 162: case 's': /* -s Read from std input */ 163: nofile++; 164: break; 165: 166: case 't': /* -t Read one line from input */ 167: onelflg = 2; 168: prompt = 0; 169: nofile++; 170: break; 171: 172: case 'v': /* -v Echo hist expanded input */ 173: nverbose = 1; /* ... later */ 174: break; 175: 176: case 'x': /* -x Echo just before execution */ 177: nexececho = 1; /* ... later */ 178: break; 179: 180: case 'V': /* -V Echo hist expanded input */ 181: setNS("verbose"); /* NOW! */ 182: break; 183: 184: case 'X': /* -X Echo just before execution */ 185: setNS("echo"); /* NOW! */ 186: break; 187: 188: } while (*cp); 189: v++, c--; 190: } 191: 192: if (quitit) /* With all due haste, for debugging */ 193: (void) signal(SIGQUIT, SIG_DFL); 194: 195: /* 196: * Unless prevented by -c, -i, -s, or -t, if there 197: * are remaining arguments the first of them is the name 198: * of a shell file from which to read commands. 199: */ 200: if (nofile == 0 && c > 0) { 201: nofile = open(v[0], 0); 202: if (nofile < 0) { 203: child++; /* So this ... */ 204: Perror(v[0]); /* ... doesn't return */ 205: } 206: file = v[0]; 207: SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 208: (void) ioctl(SHIN, FIOCLEX, (char *)0); 209: prompt = 0; 210: c--, v++; 211: } 212: if (!batch && (uid != geteuid() || getgid() != getegid())) { 213: errno = EACCES; 214: child++; /* So this ... */ 215: Perror("csh"); /* ... doesn't return */ 216: } 217: /* 218: * Consider input a tty if it really is or we are interactive. 219: */ 220: intty = intact || isatty(SHIN); 221: /* 222: * Decide whether we should play with signals or not. 223: * If we are explicitly told (via -i, or -) or we are a login 224: * shell (arg0 starts with -) or the input and output are both 225: * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 226: * Note that in only the login shell is it likely that parent 227: * may have set signals to be ignored 228: */ 229: if (loginsh || intact || intty && isatty(SHOUT)) 230: setintr = 1; 231: #ifdef TELL 232: settell(); 233: #endif 234: /* 235: * Save the remaining arguments in argv. 236: */ 237: setq("argv", v, &shvhed); 238: 239: /* 240: * Set up the prompt. 241: */ 242: if (prompt) 243: set("prompt", uid == 0 ? "# " : "% "); 244: 245: /* 246: * If we are an interactive shell, then start fiddling 247: * with the signals; this is a tricky game. 248: */ 249: shpgrp = getpgrp(0); 250: opgrp = tpgrp = -1; 251: oldisc = -1; 252: if (setintr) { 253: **av = '-'; 254: if (!quitit) /* Wary! */ 255: (void) signal(SIGQUIT, SIG_IGN); 256: (void) signal(SIGINT, pintr); 257: (void) sigblock(sigmask(SIGINT)); 258: (void) signal(SIGTERM, SIG_IGN); 259: if (quitit == 0 && arginp == 0) { 260: (void) signal(SIGTSTP, SIG_IGN); 261: (void) signal(SIGTTIN, SIG_IGN); 262: (void) signal(SIGTTOU, SIG_IGN); 263: /* 264: * Wait till in foreground, in case someone 265: * stupidly runs 266: * csh & 267: * dont want to try to grab away the tty. 268: */ 269: if (isatty(FSHDIAG)) 270: f = FSHDIAG; 271: else if (isatty(FSHOUT)) 272: f = FSHOUT; 273: else if (isatty(OLDSTD)) 274: f = OLDSTD; 275: else 276: f = -1; 277: retry: 278: if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && 279: tpgrp != -1) { 280: int ldisc; 281: if (tpgrp != shpgrp) { 282: int (*old)() = signal(SIGTTIN, SIG_DFL); 283: (void) kill(0, SIGTTIN); 284: (void) signal(SIGTTIN, old); 285: goto retry; 286: } 287: if (ioctl(f, TIOCGETD, (char *)&oldisc) != 0) 288: goto notty; 289: if (oldisc != NTTYDISC) { 290: #ifdef DEBUG 291: printf("Switching to new tty driver...\n"); 292: #endif DEBUG 293: ldisc = NTTYDISC; 294: (void) ioctl(f, TIOCSETD, 295: (char *)&ldisc); 296: } else 297: oldisc = -1; 298: opgrp = shpgrp; 299: shpgrp = getpid(); 300: tpgrp = shpgrp; 301: (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); 302: (void) setpgrp(0, shpgrp); 303: (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, 304: (char *)0); 305: } else { 306: notty: 307: printf("Warning: no access to tty; thus no job control in this shell...\n"); 308: tpgrp = -1; 309: } 310: } 311: } 312: if (setintr == 0 && parintr == SIG_DFL) 313: setintr++; 314: (void) signal(SIGCHLD, pchild); /* while signals not ready */ 315: 316: /* 317: * Set an exit here in case of an interrupt or error reading 318: * the shell start-up scripts. 319: */ 320: setexit(); 321: haderr = 0; /* In case second time through */ 322: if (!fast && reenter == 0) { 323: reenter++; 324: /* Will have value("home") here because set fast if don't */ 325: srccat(value("home"), "/.cshrc"); 326: if (!fast && !arginp && !onelflg && !havhash) 327: dohash(); 328: if (loginsh) { 329: srccat(value("home"), "/.login"); 330: } 331: dosource(loadhist); 332: } 333: 334: /* 335: * Now are ready for the -v and -x flags 336: */ 337: if (nverbose) 338: setNS("verbose"); 339: if (nexececho) 340: setNS("echo"); 341: 342: /* 343: * All the rest of the world is inside this call. 344: * The argument to process indicates whether it should 345: * catch "error unwinds". Thus if we are a interactive shell 346: * our call here will never return by being blown past on an error. 347: */ 348: process(setintr); 349: 350: /* 351: * Mop-up. 352: */ 353: if (loginsh) { 354: printf("logout\n"); 355: (void) close(SHIN); 356: child++; 357: goodbye(); 358: } 359: rechist(); 360: exitstat(); 361: } 362: 363: untty() 364: { 365: 366: if (tpgrp > 0) { 367: (void) setpgrp(0, opgrp); 368: (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 369: if (oldisc != -1 && oldisc != NTTYDISC) { 370: #ifdef DEBUG 371: printf("\nReverting to old tty driver...\n"); 372: #endif DEBUG 373: (void) ioctl(FSHTTY, TIOCSETD, (char *)&oldisc); 374: } 375: } 376: } 377: 378: importpath(cp) 379: char *cp; 380: { 381: register int i = 0; 382: register char *dp; 383: register char **pv; 384: int c; 385: static char dot[2] = {'.', 0}; 386: 387: for (dp = cp; *dp; dp++) 388: if (*dp == ':') 389: i++; 390: /* 391: * i+2 where i is the number of colons in the path. 392: * There are i+1 directories in the path plus we need 393: * room for a zero terminator. 394: */ 395: pv = (char **) calloc((unsigned) (i + 2), sizeof (char **)); 396: dp = cp; 397: i = 0; 398: if (*dp) 399: for (;;) { 400: if ((c = *dp) == ':' || c == 0) { 401: *dp = 0; 402: pv[i++] = savestr(*cp ? cp : dot); 403: if (c) { 404: cp = dp + 1; 405: *dp = ':'; 406: } else 407: break; 408: } 409: dp++; 410: } 411: pv[i] = 0; 412: set1("path", pv, &shvhed); 413: } 414: 415: /* 416: * Source to the file which is the catenation of the argument names. 417: */ 418: srccat(cp, dp) 419: char *cp, *dp; 420: { 421: register char *ep = strspl(cp, dp); 422: register int unit = dmove(open(ep, 0), -1); 423: 424: (void) ioctl(unit, FIOCLEX, (char *)0); 425: xfree(ep); 426: #ifdef INGRES 427: srcunit(unit, 0, 0); 428: #else 429: srcunit(unit, 1, 0); 430: #endif 431: } 432: 433: /* 434: * Source to a unit. If onlyown it must be our file or our group or 435: * we don't chance it. This occurs on ".cshrc"s and the like. 436: */ 437: srcunit(unit, onlyown, hflg) 438: register int unit; 439: bool onlyown; 440: bool hflg; 441: { 442: /* We have to push down a lot of state here */ 443: /* All this could go into a structure */ 444: int oSHIN = -1, oldintty = intty; 445: struct whyle *oldwhyl = whyles; 446: char *ogointr = gointr, *oarginp = arginp; 447: char *oevalp = evalp, **oevalvec = evalvec; 448: int oonelflg = onelflg; 449: bool oenterhist = enterhist; 450: char OHIST = HIST; 451: #ifdef TELL 452: bool otell = cantell; 453: #endif 454: struct Bin saveB; 455: 456: /* The (few) real local variables */ 457: jmp_buf oldexit; 458: int reenter; 459: long omask; 460: 461: if (unit < 0) 462: return; 463: if (didfds) 464: donefds(); 465: if (onlyown) { 466: struct stat stb; 467: 468: if (fstat(unit, &stb) < 0 || 469: (stb.st_uid != uid && stb.st_gid != getgid())) { 470: (void) close(unit); 471: return; 472: } 473: } 474: 475: /* 476: * There is a critical section here while we are pushing down the 477: * input stream since we have stuff in different structures. 478: * If we weren't careful an interrupt could corrupt SHIN's Bin 479: * structure and kill the shell. 480: * 481: * We could avoid the critical region by grouping all the stuff 482: * in a single structure and pointing at it to move it all at 483: * once. This is less efficient globally on many variable references 484: * however. 485: */ 486: getexit(oldexit); 487: reenter = 0; 488: if (setintr) 489: omask = sigblock(sigmask(SIGINT)); 490: setexit(); 491: reenter++; 492: if (reenter == 1) { 493: /* Setup the new values of the state stuff saved above */ 494: copy((char *)&saveB, (char *)&B, sizeof saveB); 495: fbuf = (char **) 0; 496: fseekp = feobp = fblocks = 0; 497: oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 498: intty = isatty(SHIN), whyles = 0, gointr = 0; 499: evalvec = 0; evalp = 0; 500: enterhist = hflg; 501: if (enterhist) 502: HIST = '\0'; 503: /* 504: * Now if we are allowing commands to be interrupted, 505: * we let ourselves be interrupted. 506: */ 507: if (setintr) 508: (void) sigsetmask(omask); 509: #ifdef TELL 510: settell(); 511: #endif 512: process(0); /* 0 -> blow away on errors */ 513: } 514: if (setintr) 515: (void) sigsetmask(omask); 516: if (oSHIN >= 0) { 517: register int i; 518: 519: /* We made it to the new state... free up its storage */ 520: /* This code could get run twice but xfree doesn't care */ 521: for (i = 0; i < fblocks; i++) 522: xfree(fbuf[i]); 523: xfree((char *)fbuf); 524: 525: /* Reset input arena */ 526: copy((char *)&B, (char *)&saveB, sizeof B); 527: 528: (void) close(SHIN), SHIN = oSHIN; 529: arginp = oarginp, onelflg = oonelflg; 530: evalp = oevalp, evalvec = oevalvec; 531: intty = oldintty, whyles = oldwhyl, gointr = ogointr; 532: if (enterhist) 533: HIST = OHIST; 534: enterhist = oenterhist; 535: #ifdef TELL 536: cantell = otell; 537: #endif 538: } 539: 540: resexit(oldexit); 541: /* 542: * If process reset() (effectively an unwind) then 543: * we must also unwind. 544: */ 545: if (reenter >= 2) 546: error(NOSTR); 547: } 548: 549: rechist() 550: { 551: char buf[BUFSIZ]; 552: int fp, ftmp, oldidfds; 553: 554: if (!fast) { 555: if (value("savehist")[0] == '\0') 556: return; 557: (void) strcpy(buf, value("home")); 558: (void) strcat(buf, "/.history"); 559: fp = creat(buf, 0666); 560: if (fp == -1) 561: return; 562: oldidfds = didfds; 563: didfds = 0; 564: ftmp = SHOUT; 565: SHOUT = fp; 566: (void) strcpy(buf, value("savehist")); 567: dumphist[2] = buf; 568: dohist(dumphist); 569: (void) close(fp); 570: SHOUT = ftmp; 571: didfds = oldidfds; 572: } 573: } 574: 575: goodbye() 576: { 577: if (loginsh) { 578: (void) signal(SIGQUIT, SIG_IGN); 579: (void) signal(SIGINT, SIG_IGN); 580: (void) signal(SIGTERM, SIG_IGN); 581: setintr = 0; /* No interrupts after "logout" */ 582: if (adrof("home")) 583: srccat(value("home"), "/.logout"); 584: } 585: rechist(); 586: exitstat(); 587: } 588: 589: exitstat() 590: { 591: 592: #ifdef PROF 593: monitor(0); 594: #endif 595: /* 596: * Note that if STATUS is corrupted (i.e. getn bombs) 597: * then error will exit directly because we poke child here. 598: * Otherwise we might continue unwarrantedly (sic). 599: */ 600: child++; 601: exit(getn(value("status"))); 602: } 603: 604: /* 605: * in the event of a HUP we want to save the history 606: */ 607: phup() 608: { 609: rechist(); 610: exit(1); 611: } 612: 613: char *jobargv[2] = { "jobs", 0 }; 614: /* 615: * Catch an interrupt, e.g. during lexical input. 616: * If we are an interactive shell, we reset the interrupt catch 617: * immediately. In any case we drain the shell output, 618: * and finally go through the normal error mechanism, which 619: * gets a chance to make the shell go away. 620: */ 621: pintr() 622: { 623: pintr1(1); 624: } 625: 626: pintr1(wantnl) 627: bool wantnl; 628: { 629: register char **v; 630: long omask; 631: 632: omask = sigblock(0L); 633: if (setintr) { 634: (void) sigsetmask(omask & ~sigmask(SIGINT)); 635: if (pjobs) { 636: pjobs = 0; 637: printf("\n"); 638: dojobs(jobargv); 639: bferr("Interrupted"); 640: } 641: } 642: (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 643: draino(); 644: 645: /* 646: * If we have an active "onintr" then we search for the label. 647: * Note that if one does "onintr -" then we shan't be interruptible 648: * so we needn't worry about that here. 649: */ 650: if (gointr) { 651: search(ZGOTO, 0, gointr); 652: timflg = 0; 653: if (v = pargv) 654: pargv = 0, blkfree(v); 655: if (v = gargv) 656: gargv = 0, blkfree(v); 657: reset(); 658: } else if (intty && wantnl) 659: printf("\n"); /* Some like this, others don't */ 660: error(NOSTR); 661: } 662: 663: /* 664: * Process is the main driving routine for the shell. 665: * It runs all command processing, except for those within { ... } 666: * in expressions (which is run by a routine evalav in sh.exp.c which 667: * is a stripped down process), and `...` evaluation which is run 668: * also by a subset of this code in sh.glob.c in the routine backeval. 669: * 670: * The code here is a little strange because part of it is interruptible 671: * and hence freeing of structures appears to occur when none is necessary 672: * if this is ignored. 673: * 674: * Note that if catch is not set then we will unwind on any error. 675: * If an end-of-file occurs, we return. 676: */ 677: process(catch) 678: bool catch; 679: { 680: jmp_buf osetexit; 681: register struct command *t; 682: 683: getexit(osetexit); 684: for (;;) { 685: pendjob(); 686: paraml.next = paraml.prev = ¶ml; 687: paraml.word = ""; 688: t = 0; 689: setexit(); 690: justpr = enterhist; /* execute if not entering history */ 691: 692: /* 693: * Interruptible during interactive reads 694: */ 695: if (setintr) 696: (void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT)); 697: 698: /* 699: * For the sake of reset() 700: */ 701: freelex(¶ml), freesyn(t), t = 0; 702: 703: if (haderr) { 704: if (!catch) { 705: /* unwind */ 706: doneinp = 0; 707: resexit(osetexit); 708: reset(); 709: } 710: haderr = 0; 711: /* 712: * Every error is eventually caught here or 713: * the shell dies. It is at this 714: * point that we clean up any left-over open 715: * files, by closing all but a fixed number 716: * of pre-defined files. Thus routines don't 717: * have to worry about leaving files open due 718: * to deeper errors... they will get closed here. 719: */ 720: closem(); 721: continue; 722: } 723: if (doneinp) { 724: doneinp = 0; 725: break; 726: } 727: if (chkstop) 728: chkstop--; 729: if (neednote) 730: pnote(); 731: if (intty && prompt && evalvec == 0) { 732: mailchk(); 733: /* 734: * If we are at the end of the input buffer 735: * then we are going to read fresh stuff. 736: * Otherwise, we are rereading input and don't 737: * need or want to prompt. 738: */ 739: if (fseekp == feobp) 740: printprompt(); 741: } 742: err = 0; 743: 744: /* 745: * Echo not only on VERBOSE, but also with history expansion. 746: * If there is a lexical error then we forego history echo. 747: */ 748: if (lex(¶ml) && !err && intty || 749: adrof("verbose")) { 750: haderr = 1; 751: prlex(¶ml); 752: haderr = 0; 753: } 754: 755: /* 756: * The parser may lose space if interrupted. 757: */ 758: if (setintr) 759: (void) sigblock(sigmask(SIGINT)); 760: 761: /* 762: * Save input text on the history list if 763: * reading in old history, or it 764: * is from the terminal at the top level and not 765: * in a loop. 766: */ 767: if (enterhist || catch && intty && !whyles) 768: savehist(¶ml); 769: 770: /* 771: * Print lexical error messages, except when sourcing 772: * history lists. 773: */ 774: if (!enterhist && err) 775: error(err); 776: 777: /* 778: * If had a history command :p modifier then 779: * this is as far as we should go 780: */ 781: if (justpr) 782: reset(); 783: 784: alias(¶ml); 785: 786: /* 787: * Parse the words of the input into a parse tree. 788: */ 789: t = syntax(paraml.next, ¶ml, 0); 790: if (err) 791: error(err); 792: 793: /* 794: * Execute the parse tree 795: */ 796: execute(t, tpgrp); 797: 798: /* 799: * Made it! 800: */ 801: freelex(¶ml), freesyn(t); 802: } 803: resexit(osetexit); 804: } 805: 806: dosource(t) 807: register char **t; 808: { 809: register char *f; 810: register int u; 811: bool hflg = 0; 812: char buf[BUFSIZ]; 813: 814: t++; 815: if (*t && eq(*t, "-h")) { 816: t++; 817: hflg++; 818: } 819: (void) strcpy(buf, *t); 820: f = globone(buf); 821: u = dmove(open(f, 0), -1); 822: xfree(f); 823: if (u < 0 && !hflg) 824: Perror(f); 825: (void) ioctl(u, FIOCLEX, (char *)0); 826: srcunit(u, 0, hflg); 827: } 828: 829: /* 830: * Check for mail. 831: * If we are a login shell, then we don't want to tell 832: * about any mail file unless its been modified 833: * after the time we started. 834: * This prevents us from telling the user things he already 835: * knows, since the login program insists on saying 836: * "You have mail." 837: */ 838: mailchk() 839: { 840: register struct varent *v; 841: register char **vp; 842: time_t t; 843: int intvl, cnt; 844: struct stat stb; 845: bool new; 846: 847: v = adrof("mail"); 848: if (v == 0) 849: return; 850: (void) time(&t); 851: vp = v->vec; 852: cnt = blklen(vp); 853: intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 854: if (intvl < 1) 855: intvl = 1; 856: if (chktim + intvl > t) 857: return; 858: for (; *vp; vp++) { 859: if (stat(*vp, &stb) < 0) 860: continue; 861: new = stb.st_mtime > time0.tv_sec; 862: if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 863: (stb.st_atime < chktim && stb.st_mtime < chktim) || 864: loginsh && !new) 865: continue; 866: if (cnt == 1) 867: printf("You have %smail.\n", new ? "new " : ""); 868: else 869: printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 870: } 871: chktim = t; 872: } 873: 874: #include <pwd.h> 875: /* 876: * Extract a home directory from the password file 877: * The argument points to a buffer where the name of the 878: * user whose home directory is sought is currently. 879: * We write the home directory of the user back there. 880: */ 881: gethdir(home) 882: char *home; 883: { 884: register struct passwd *pp = getpwnam(home); 885: 886: if (pp == 0) 887: return (1); 888: (void) strcpy(home, pp->pw_dir); 889: return (0); 890: } 891: 892: /* 893: * Move the initial descriptors to their eventual 894: * resting places, closin all other units. 895: */ 896: initdesc() 897: { 898: 899: didfds = 0; /* 0, 1, 2 aren't set up */ 900: (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0); 901: (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0); 902: (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0); 903: (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0); 904: closem(); 905: } 906: 907: #ifdef PROF 908: done(i) 909: #else 910: exit(i) 911: #endif 912: int i; 913: { 914: 915: untty(); 916: _exit(i); 917: } 918: 919: printprompt() 920: { 921: register char *cp; 922: 923: if (!whyles) { 924: for (cp = value("prompt"); *cp; cp++) 925: if (*cp == HIST) 926: printf("%d", eventno + 1); 927: else { 928: if (*cp == '\\' && cp[1] == HIST) 929: cp++; 930: putchar(*cp | QUOTE); 931: } 932: } else 933: /* 934: * Prompt for forward reading loop 935: * body content. 936: */ 937: printf("? "); 938: flush(); 939: }