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