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