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: #ifndef lint 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, omask; 459: 460: if (unit < 0) 461: return; 462: if (didfds) 463: donefds(); 464: if (onlyown) { 465: struct stat stb; 466: 467: if (fstat(unit, &stb) < 0 || 468: (stb.st_uid != uid && stb.st_gid != getgid())) { 469: (void) close(unit); 470: return; 471: } 472: } 473: 474: /* 475: * There is a critical section here while we are pushing down the 476: * input stream since we have stuff in different structures. 477: * If we weren't careful an interrupt could corrupt SHIN's Bin 478: * structure and kill the shell. 479: * 480: * We could avoid the critical region by grouping all the stuff 481: * in a single structure and pointing at it to move it all at 482: * once. This is less efficient globally on many variable references 483: * however. 484: */ 485: getexit(oldexit); 486: reenter = 0; 487: if (setintr) 488: omask = sigblock(sigmask(SIGINT)); 489: setexit(); 490: reenter++; 491: if (reenter == 1) { 492: /* Setup the new values of the state stuff saved above */ 493: copy((char *)&saveB, (char *)&B, sizeof saveB); 494: fbuf = (char **) 0; 495: fseekp = feobp = fblocks = 0; 496: oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 497: intty = isatty(SHIN), whyles = 0, gointr = 0; 498: evalvec = 0; evalp = 0; 499: enterhist = hflg; 500: if (enterhist) 501: HIST = '\0'; 502: /* 503: * Now if we are allowing commands to be interrupted, 504: * we let ourselves be interrupted. 505: */ 506: if (setintr) 507: (void) sigsetmask(omask); 508: #ifdef TELL 509: settell(); 510: #endif 511: process(0); /* 0 -> blow away on errors */ 512: } 513: if (setintr) 514: (void) sigsetmask(omask); 515: if (oSHIN >= 0) { 516: register int i; 517: 518: /* We made it to the new state... free up its storage */ 519: /* This code could get run twice but xfree doesn't care */ 520: for (i = 0; i < fblocks; i++) 521: xfree(fbuf[i]); 522: xfree((char *)fbuf); 523: 524: /* Reset input arena */ 525: copy((char *)&B, (char *)&saveB, sizeof B); 526: 527: (void) close(SHIN), SHIN = oSHIN; 528: arginp = oarginp, onelflg = oonelflg; 529: evalp = oevalp, evalvec = oevalvec; 530: intty = oldintty, whyles = oldwhyl, gointr = ogointr; 531: if (enterhist) 532: HIST = OHIST; 533: enterhist = oenterhist; 534: #ifdef TELL 535: cantell = otell; 536: #endif 537: } 538: 539: resexit(oldexit); 540: /* 541: * If process reset() (effectively an unwind) then 542: * we must also unwind. 543: */ 544: if (reenter >= 2) 545: error(NOSTR); 546: } 547: 548: rechist() 549: { 550: char buf[BUFSIZ]; 551: int fp, ftmp, oldidfds; 552: 553: if (!fast) { 554: if (value("savehist")[0] == '\0') 555: return; 556: (void) strcpy(buf, value("home")); 557: (void) strcat(buf, "/.history"); 558: fp = creat(buf, 0666); 559: if (fp == -1) 560: return; 561: oldidfds = didfds; 562: didfds = 0; 563: ftmp = SHOUT; 564: SHOUT = fp; 565: (void) strcpy(buf, value("savehist")); 566: dumphist[2] = buf; 567: dohist(dumphist); 568: (void) close(fp); 569: SHOUT = ftmp; 570: didfds = oldidfds; 571: } 572: } 573: 574: goodbye() 575: { 576: if (loginsh) { 577: (void) signal(SIGQUIT, SIG_IGN); 578: (void) signal(SIGINT, SIG_IGN); 579: (void) signal(SIGTERM, SIG_IGN); 580: setintr = 0; /* No interrupts after "logout" */ 581: if (adrof("home")) 582: srccat(value("home"), "/.logout"); 583: } 584: rechist(); 585: exitstat(); 586: } 587: 588: exitstat() 589: { 590: 591: #ifdef PROF 592: monitor(0); 593: #endif 594: /* 595: * Note that if STATUS is corrupted (i.e. getn bombs) 596: * then error will exit directly because we poke child here. 597: * Otherwise we might continue unwarrantedly (sic). 598: */ 599: child++; 600: exit(getn(value("status"))); 601: } 602: 603: /* 604: * in the event of a HUP we want to save the history 605: */ 606: phup() 607: { 608: rechist(); 609: exit(1); 610: } 611: 612: char *jobargv[2] = { "jobs", 0 }; 613: /* 614: * Catch an interrupt, e.g. during lexical input. 615: * If we are an interactive shell, we reset the interrupt catch 616: * immediately. In any case we drain the shell output, 617: * and finally go through the normal error mechanism, which 618: * gets a chance to make the shell go away. 619: */ 620: pintr() 621: { 622: pintr1(1); 623: } 624: 625: pintr1(wantnl) 626: bool wantnl; 627: { 628: register char **v; 629: int omask; 630: 631: omask = sigblock(0); 632: if (setintr) { 633: (void) sigsetmask(omask & ~sigmask(SIGINT)); 634: if (pjobs) { 635: pjobs = 0; 636: printf("\n"); 637: dojobs(jobargv); 638: bferr("Interrupted"); 639: } 640: } 641: (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 642: draino(); 643: 644: /* 645: * If we have an active "onintr" then we search for the label. 646: * Note that if one does "onintr -" then we shan't be interruptible 647: * so we needn't worry about that here. 648: */ 649: if (gointr) { 650: search(ZGOTO, 0, gointr); 651: timflg = 0; 652: if (v = pargv) 653: pargv = 0, blkfree(v); 654: if (v = gargv) 655: gargv = 0, blkfree(v); 656: reset(); 657: } else if (intty && wantnl) 658: printf("\n"); /* Some like this, others don't */ 659: error(NOSTR); 660: } 661: 662: /* 663: * Process is the main driving routine for the shell. 664: * It runs all command processing, except for those within { ... } 665: * in expressions (which is run by a routine evalav in sh.exp.c which 666: * is a stripped down process), and `...` evaluation which is run 667: * also by a subset of this code in sh.glob.c in the routine backeval. 668: * 669: * The code here is a little strange because part of it is interruptible 670: * and hence freeing of structures appears to occur when none is necessary 671: * if this is ignored. 672: * 673: * Note that if catch is not set then we will unwind on any error. 674: * If an end-of-file occurs, we return. 675: */ 676: process(catch) 677: bool catch; 678: { 679: jmp_buf osetexit; 680: register struct command *t; 681: 682: getexit(osetexit); 683: for (;;) { 684: pendjob(); 685: paraml.next = paraml.prev = ¶ml; 686: paraml.word = ""; 687: t = 0; 688: setexit(); 689: justpr = enterhist; /* execute if not entering history */ 690: 691: /* 692: * Interruptible during interactive reads 693: */ 694: if (setintr) 695: (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 696: 697: /* 698: * For the sake of reset() 699: */ 700: freelex(¶ml), freesyn(t), t = 0; 701: 702: if (haderr) { 703: if (!catch) { 704: /* unwind */ 705: doneinp = 0; 706: resexit(osetexit); 707: reset(); 708: } 709: haderr = 0; 710: /* 711: * Every error is eventually caught here or 712: * the shell dies. It is at this 713: * point that we clean up any left-over open 714: * files, by closing all but a fixed number 715: * of pre-defined files. Thus routines don't 716: * have to worry about leaving files open due 717: * to deeper errors... they will get closed here. 718: */ 719: closem(); 720: continue; 721: } 722: if (doneinp) { 723: doneinp = 0; 724: break; 725: } 726: if (chkstop) 727: chkstop--; 728: if (neednote) 729: pnote(); 730: if (intty && prompt && evalvec == 0) { 731: mailchk(); 732: /* 733: * If we are at the end of the input buffer 734: * then we are going to read fresh stuff. 735: * Otherwise, we are rereading input and don't 736: * need or want to prompt. 737: */ 738: if (fseekp == feobp) 739: printprompt(); 740: } 741: err = 0; 742: 743: /* 744: * Echo not only on VERBOSE, but also with history expansion. 745: * If there is a lexical error then we forego history echo. 746: */ 747: if (lex(¶ml) && !err && intty || 748: adrof("verbose")) { 749: haderr = 1; 750: prlex(¶ml); 751: haderr = 0; 752: } 753: 754: /* 755: * The parser may lose space if interrupted. 756: */ 757: if (setintr) 758: (void) sigblock(sigmask(SIGINT)); 759: 760: /* 761: * Save input text on the history list if 762: * reading in old history, or it 763: * is from the terminal at the top level and not 764: * in a loop. 765: */ 766: if (enterhist || catch && intty && !whyles) 767: savehist(¶ml); 768: 769: /* 770: * Print lexical error messages, except when sourcing 771: * history lists. 772: */ 773: if (!enterhist && err) 774: error(err); 775: 776: /* 777: * If had a history command :p modifier then 778: * this is as far as we should go 779: */ 780: if (justpr) 781: reset(); 782: 783: alias(¶ml); 784: 785: /* 786: * Parse the words of the input into a parse tree. 787: */ 788: t = syntax(paraml.next, ¶ml, 0); 789: if (err) 790: error(err); 791: 792: /* 793: * Execute the parse tree 794: */ 795: execute(t, tpgrp); 796: 797: /* 798: * Made it! 799: */ 800: freelex(¶ml), freesyn(t); 801: } 802: resexit(osetexit); 803: } 804: 805: dosource(t) 806: register char **t; 807: { 808: register char *f; 809: register int u; 810: bool hflg = 0; 811: char buf[BUFSIZ]; 812: 813: t++; 814: if (*t && eq(*t, "-h")) { 815: t++; 816: hflg++; 817: } 818: (void) strcpy(buf, *t); 819: f = globone(buf); 820: u = dmove(open(f, 0), -1); 821: xfree(f); 822: if (u < 0 && !hflg) 823: Perror(f); 824: (void) ioctl(u, FIOCLEX, (char *)0); 825: srcunit(u, 0, hflg); 826: } 827: 828: /* 829: * Check for mail. 830: * If we are a login shell, then we don't want to tell 831: * about any mail file unless its been modified 832: * after the time we started. 833: * This prevents us from telling the user things he already 834: * knows, since the login program insists on saying 835: * "You have mail." 836: */ 837: mailchk() 838: { 839: register struct varent *v; 840: register char **vp; 841: time_t t; 842: int intvl, cnt; 843: struct stat stb; 844: bool new; 845: 846: v = adrof("mail"); 847: if (v == 0) 848: return; 849: (void) time(&t); 850: vp = v->vec; 851: cnt = blklen(vp); 852: intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 853: if (intvl < 1) 854: intvl = 1; 855: if (chktim + intvl > t) 856: return; 857: for (; *vp; vp++) { 858: if (stat(*vp, &stb) < 0) 859: continue; 860: new = stb.st_mtime > time0.tv_sec; 861: if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 862: (stb.st_atime < chktim && stb.st_mtime < chktim) || 863: loginsh && !new) 864: continue; 865: if (cnt == 1) 866: printf("You have %smail.\n", new ? "new " : ""); 867: else 868: printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 869: } 870: chktim = t; 871: } 872: 873: #include <pwd.h> 874: /* 875: * Extract a home directory from the password file 876: * The argument points to a buffer where the name of the 877: * user whose home directory is sought is currently. 878: * We write the home directory of the user back there. 879: */ 880: gethdir(home) 881: char *home; 882: { 883: register struct passwd *pp = getpwnam(home); 884: 885: if (pp == 0) 886: return (1); 887: (void) strcpy(home, pp->pw_dir); 888: return (0); 889: } 890: 891: /* 892: * Move the initial descriptors to their eventual 893: * resting places, closin all other units. 894: */ 895: initdesc() 896: { 897: 898: didfds = 0; /* 0, 1, 2 aren't set up */ 899: (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0); 900: (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0); 901: (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0); 902: (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0); 903: closem(); 904: } 905: 906: #ifdef PROF 907: done(i) 908: #else 909: exit(i) 910: #endif 911: int i; 912: { 913: 914: untty(); 915: _exit(i); 916: } 917: 918: printprompt() 919: { 920: register char *cp; 921: 922: if (!whyles) { 923: for (cp = value("prompt"); *cp; cp++) 924: if (*cp == HIST) 925: printf("%d", eventno + 1); 926: else { 927: if (*cp == '\\' && cp[1] == HIST) 928: cp++; 929: putchar(*cp | QUOTE); 930: } 931: } else 932: /* 933: * Prompt for forward reading loop 934: * body content. 935: */ 936: printf("? "); 937: flush(); 938: }