1 static char *sccsid = "@(#)csh.c 4.8 03/28/82"; 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) || parintr == SIG_DFL) 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 sigset(SIGCHLD, pchild); /* while signals not ready */ 316 317 /* 318 * Set an exit here in case of an interrupt or error reading 319 * the shell start-up scripts. 320 */ 321 setexit(); 322 haderr = 0; /* In case second time through */ 323 if (!fast && reenter == 0) { 324 reenter++; 325 /* Will have value("home") here because set fast if don't */ 326 srccat(value("home"), "/.cshrc"); 327 if (!fast && !arginp && !onelflg) 328 dohash(); 329 dosource(loadhist); 330 if (loginsh) { 331 srccat(value("home"), "/.login"); 332 } 333 } 334 335 /* 336 * Now are ready for the -v and -x flags 337 */ 338 if (nverbose) 339 setNS("verbose"); 340 if (nexececho) 341 setNS("echo"); 342 343 /* 344 * All the rest of the world is inside this call. 345 * The argument to process indicates whether it should 346 * catch "error unwinds". Thus if we are a interactive shell 347 * our call here will never return by being blown past on an error. 348 */ 349 process(setintr); 350 351 /* 352 * Mop-up. 353 */ 354 if (loginsh) { 355 printf("logout\n"); 356 close(SHIN); 357 child++; 358 goodbye(); 359 } 360 rechist(); 361 exitstat(); 362 } 363 364 untty() 365 { 366 367 if (tpgrp > 0) { 368 setpgrp(0, opgrp); 369 ioctl(FSHTTY, TIOCSPGRP, &opgrp); 370 if (oldisc != -1 && oldisc != NTTYDISC) { 371 #ifdef DEBUG 372 printf("\nReverting to old tty driver...\n"); 373 #endif DEBUG 374 ioctl(FSHTTY, TIOCSETD, &oldisc); 375 } 376 } 377 } 378 379 importpath(cp) 380 char *cp; 381 { 382 register int i = 0; 383 register char *dp; 384 register char **pv; 385 int c; 386 static char dot[2] = {'.', 0}; 387 388 for (dp = cp; *dp; dp++) 389 if (*dp == ':') 390 i++; 391 /* 392 * i+2 where i is the number of colons in the path. 393 * There are i+1 directories in the path plus we need 394 * room for a zero terminator. 395 */ 396 pv = (char **) calloc(i+2, sizeof (char **)); 397 dp = cp; 398 i = 0; 399 if (*dp) 400 for (;;) { 401 if ((c = *dp) == ':' || c == 0) { 402 *dp = 0; 403 pv[i++] = savestr(*cp ? cp : dot); 404 if (c) { 405 cp = dp + 1; 406 *dp = ':'; 407 } else 408 break; 409 } 410 dp++; 411 } 412 pv[i] = 0; 413 set1("path", pv, &shvhed); 414 } 415 416 /* 417 * Source to the file which is the catenation of the argument names. 418 */ 419 srccat(cp, dp) 420 char *cp, *dp; 421 { 422 register char *ep = strspl(cp, dp); 423 register int unit = dmove(open(ep, 0), -1); 424 425 /* ioctl(unit, FIOCLEX, NULL); */ 426 xfree(ep); 427 #ifdef INGRES 428 srcunit(unit, 0, 0); 429 #else 430 srcunit(unit, 1, 0); 431 #endif 432 } 433 434 /* 435 * Source to a unit. If onlyown it must be our file or our group or 436 * we don't chance it. This occurs on ".cshrc"s and the like. 437 */ 438 srcunit(unit, onlyown, hflg) 439 register int unit; 440 bool onlyown; 441 bool hflg; 442 { 443 /* We have to push down a lot of state here */ 444 /* All this could go into a structure */ 445 int oSHIN = -1, oldintty = intty; 446 struct whyle *oldwhyl = whyles; 447 char *ogointr = gointr, *oarginp = arginp; 448 char *oevalp = evalp, **oevalvec = evalvec; 449 int oonelflg = onelflg; 450 bool oenterhist = enterhist; 451 char OHIST = HIST; 452 #ifdef TELL 453 bool otell = cantell; 454 #endif 455 struct Bin saveB; 456 457 /* The (few) real local variables */ 458 jmp_buf oldexit; 459 int reenter; 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 || (stb.st_uid != uid && stb.st_gid != getgid())) { 469 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 sighold(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 sigrelse(SIGINT); 508 #ifdef TELL 509 settell(); 510 #endif 511 process(0); /* 0 -> blow away on errors */ 512 } 513 if (setintr) 514 sigrelse(SIGINT); 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 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 strcpy(buf, value("home")); 557 strcat(buf, "/.history"); 558 fp = creat(buf, 0777); 559 if (fp == -1) 560 return; 561 oldidfds = didfds; 562 didfds = 0; 563 ftmp = SHOUT; 564 SHOUT = fp; 565 strcpy(buf, value("savehist")); 566 dumphist[2] = buf; 567 dohist(dumphist); 568 close(fp); 569 SHOUT = ftmp; 570 didfds = oldidfds; 571 } 572 } 573 574 goodbye() 575 { 576 if (loginsh) { 577 signal(SIGQUIT, SIG_IGN); 578 sigset(SIGINT, SIG_IGN); 579 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 /* 592 * Note that if STATUS is corrupted (i.e. getn bombs) 593 * then error will exit directly because we poke child here. 594 * Otherwise we might continue unwarrantedly (sic). 595 */ 596 child++; 597 exit(getn(value("status"))); 598 } 599 600 /* 601 * in the event of a HUP we want to save the history 602 */ 603 phup() 604 { 605 rechist(); 606 exit(1); 607 } 608 609 char *jobargv[2] = { "jobs", 0 }; 610 /* 611 * Catch an interrupt, e.g. during lexical input. 612 * If we are an interactive shell, we reset the interrupt catch 613 * immediately. In any case we drain the shell output, 614 * and finally go through the normal error mechanism, which 615 * gets a chance to make the shell go away. 616 */ 617 pintr() 618 { 619 pintr1(1); 620 } 621 622 pintr1(wantnl) 623 bool wantnl; 624 { 625 register char **v; 626 627 if (setintr) { 628 sigrelse(SIGINT); 629 if (pjobs) { 630 pjobs = 0; 631 printf("\n"); 632 dojobs(jobargv); 633 bferr("Interrupted"); 634 } 635 } 636 if (setintr) 637 sighold(SIGINT); 638 sigrelse(SIGCHLD); 639 draino(); 640 641 /* 642 * If we have an active "onintr" then we search for the label. 643 * Note that if one does "onintr -" then we shan't be interruptible 644 * so we needn't worry about that here. 645 */ 646 if (gointr) { 647 search(ZGOTO, 0, gointr); 648 timflg = 0; 649 if (v = pargv) 650 pargv = 0, blkfree(v); 651 if (v = gargv) 652 gargv = 0, blkfree(v); 653 reset(); 654 } else if (intty && wantnl) 655 printf("\n"); /* Some like this, others don't */ 656 error(NOSTR); 657 } 658 659 /* 660 * Process is the main driving routine for the shell. 661 * It runs all command processing, except for those within { ... } 662 * in expressions (which is run by a routine evalav in sh.exp.c which 663 * is a stripped down process), and `...` evaluation which is run 664 * also by a subset of this code in sh.glob.c in the routine backeval. 665 * 666 * The code here is a little strange because part of it is interruptible 667 * and hence freeing of structures appears to occur when none is necessary 668 * if this is ignored. 669 * 670 * Note that if catch is not set then we will unwind on any error. 671 * If an end-of-file occurs, we return. 672 */ 673 process(catch) 674 bool catch; 675 { 676 register char *cp; 677 jmp_buf osetexit; 678 struct command *t; 679 680 getexit(osetexit); 681 for (;;) { 682 pendjob(); 683 paraml.next = paraml.prev = ¶ml; 684 paraml.word = ""; 685 t = 0; 686 setexit(); 687 justpr = enterhist; /* execute if not entering history */ 688 689 /* 690 * Interruptible during interactive reads 691 */ 692 if (setintr) 693 sigrelse(SIGINT); 694 695 /* 696 * For the sake of reset() 697 */ 698 freelex(¶ml), freesyn(t), t = 0; 699 700 if (haderr) { 701 if (!catch) { 702 /* unwind */ 703 doneinp = 0; 704 resexit(osetexit); 705 reset(); 706 } 707 haderr = 0; 708 /* 709 * Every error is eventually caught here or 710 * the shell dies. It is at this 711 * point that we clean up any left-over open 712 * files, by closing all but a fixed number 713 * of pre-defined files. Thus routines don't 714 * have to worry about leaving files open due 715 * to deeper errors... they will get closed here. 716 */ 717 closem(); 718 continue; 719 } 720 if (doneinp) { 721 doneinp = 0; 722 break; 723 } 724 if (chkstop) 725 chkstop--; 726 if (neednote) 727 pnote(); 728 if (intty && evalvec == 0) { 729 mailchk(); 730 /* 731 * If we are at the end of the input buffer 732 * then we are going to read fresh stuff. 733 * Otherwise, we are rereading input and don't 734 * need or want to prompt. 735 */ 736 if (fseekp == feobp) 737 if (!whyles) 738 for (cp = value("prompt"); *cp; cp++) 739 if (*cp == HIST) 740 printf("%d", eventno + 1); 741 else { 742 if (*cp == '\\' && cp[1] == HIST) 743 cp++; 744 putchar(*cp | QUOTE); 745 } 746 else 747 /* 748 * Prompt for forward reading loop 749 * body content. 750 */ 751 printf("? "); 752 flush(); 753 } 754 err = 0; 755 756 /* 757 * Echo not only on VERBOSE, but also with history expansion. 758 * If there is a lexical error then we forego history echo. 759 */ 760 if (lex(¶ml) && !err && intty || 761 adrof("verbose")) { 762 haderr = 1; 763 prlex(¶ml); 764 haderr = 0; 765 } 766 767 /* 768 * The parser may lose space if interrupted. 769 */ 770 if (setintr) 771 sighold(SIGINT); 772 773 /* 774 * Save input text on the history list if 775 * reading in old history, or it 776 * is from the terminal at the top level and not 777 * in a loop. 778 */ 779 if (enterhist || catch && intty && !whyles) 780 savehist(¶ml); 781 782 /* 783 * Print lexical error messages, except when sourcing 784 * history lists. 785 */ 786 if (!enterhist && err) 787 error(err); 788 789 /* 790 * If had a history command :p modifier then 791 * this is as far as we should go 792 */ 793 if (justpr) 794 reset(); 795 796 alias(¶ml); 797 798 /* 799 * Parse the words of the input into a parse tree. 800 */ 801 t = syntax(paraml.next, ¶ml, 0); 802 if (err) 803 error(err); 804 805 /* 806 * Execute the parse tree 807 */ 808 execute(t, tpgrp); 809 810 /* 811 * Made it! 812 */ 813 freelex(¶ml), freesyn(t); 814 } 815 resexit(osetexit); 816 } 817 818 dosource(t) 819 register char **t; 820 { 821 register char *f; 822 register int u; 823 bool hflg = 0; 824 char buf[BUFSIZ]; 825 826 t++; 827 if (*t && eq(*t, "-h")) { 828 t++; 829 hflg++; 830 } 831 strcpy(buf, *t); 832 f = globone(buf); 833 u = dmove(open(f, 0), -1); 834 xfree(f); 835 if (u < 0 && !hflg) 836 Perror(f); 837 srcunit(u, 0, hflg); 838 } 839 840 /* 841 * Check for mail. 842 * If we are a login shell, then we don't want to tell 843 * about any mail file unless its been modified 844 * after the time we started. 845 * This prevents us from telling the user things he already 846 * knows, since the login program insists on saying 847 * "You have mail." 848 */ 849 mailchk() 850 { 851 register struct varent *v; 852 register char **vp; 853 time_t t; 854 int intvl, cnt; 855 struct stat stb; 856 bool new; 857 858 v = adrof("mail"); 859 if (v == 0) 860 return; 861 time(&t); 862 vp = v->vec; 863 cnt = blklen(vp); 864 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 865 if (intvl < 1) 866 intvl = 1; 867 if (chktim + intvl > t) 868 return; 869 for (; *vp; vp++) { 870 if (stat(*vp, &stb) < 0) 871 continue; 872 new = stb.st_mtime > time0; 873 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 874 (stb.st_atime < chktim && stb.st_mtime < chktim) || 875 loginsh && !new) 876 continue; 877 if (cnt == 1) 878 printf("You have %smail.\n", new ? "new " : ""); 879 else 880 printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 881 } 882 chktim = t; 883 } 884 885 #include <pwd.h> 886 /* 887 * Extract a home directory from the password file 888 * The argument points to a buffer where the name of the 889 * user whose home directory is sought is currently. 890 * We write the home directory of the user back there. 891 */ 892 gethdir(home) 893 char *home; 894 { 895 register struct passwd *pp = getpwnam(home); 896 897 if (pp == 0) 898 return (1); 899 strcpy(home, pp->pw_dir); 900 return (0); 901 } 902 903 /* 904 * Move the initial descriptors to their eventual 905 * resting places, closin all other units. 906 */ 907 initdesc() 908 { 909 910 didcch = 0; /* Havent closed for child */ 911 didfds = 0; /* 0, 1, 2 aren't set up */ 912 SHIN = dcopy(0, FSHIN); 913 SHOUT = dcopy(1, FSHOUT); 914 SHDIAG = dcopy(2, FSHDIAG); 915 OLDSTD = dcopy(SHIN, FOLDSTD); 916 closem(); 917 } 918 919 exit(i) 920 int i; 921 { 922 923 untty(); 924 #ifdef PROF 925 IEH3exit(i); 926 #else 927 _exit(i); 928 #endif 929 } 930