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 = "@(#)csh.c 5.6 (Berkeley) 05/19/88"; 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 int insource; 438 static 439 srcunit(unit, onlyown, hflg) 440 register int unit; 441 bool onlyown; 442 bool hflg; 443 { 444 /* We have to push down a lot of state here */ 445 /* All this could go into a structure */ 446 int oSHIN = -1, oldintty = intty; 447 struct whyle *oldwhyl = whyles; 448 char *ogointr = gointr, *oarginp = arginp; 449 char *oevalp = evalp, **oevalvec = evalvec; 450 int oonelflg = onelflg; 451 bool oenterhist = enterhist; 452 char OHIST = HIST; 453 #ifdef TELL 454 bool otell = cantell; 455 #endif 456 struct Bin saveB; 457 458 /* The (few) real local variables */ 459 jmp_buf oldexit; 460 int reenter; 461 long omask; 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 (void) 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 insource = 1; 489 getexit(oldexit); 490 reenter = 0; 491 if (setintr) 492 omask = sigblock(sigmask(SIGINT)); 493 setexit(); 494 reenter++; 495 if (reenter == 1) { 496 /* Setup the new values of the state stuff saved above */ 497 copy((char *)&saveB, (char *)&B, sizeof saveB); 498 fbuf = (char **) 0; 499 fseekp = feobp = fblocks = 0; 500 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 501 intty = isatty(SHIN), whyles = 0, gointr = 0; 502 evalvec = 0; evalp = 0; 503 enterhist = hflg; 504 if (enterhist) 505 HIST = '\0'; 506 /* 507 * Now if we are allowing commands to be interrupted, 508 * we let ourselves be interrupted. 509 */ 510 if (setintr) 511 (void) sigsetmask(omask); 512 #ifdef TELL 513 settell(); 514 #endif 515 process(0); /* 0 -> blow away on errors */ 516 } 517 if (setintr) 518 (void) sigsetmask(omask); 519 if (oSHIN >= 0) { 520 register int i; 521 522 /* We made it to the new state... free up its storage */ 523 /* This code could get run twice but xfree doesn't care */ 524 for (i = 0; i < fblocks; i++) 525 xfree(fbuf[i]); 526 xfree((char *)fbuf); 527 528 /* Reset input arena */ 529 copy((char *)&B, (char *)&saveB, sizeof B); 530 531 (void) close(SHIN), SHIN = oSHIN; 532 arginp = oarginp, onelflg = oonelflg; 533 evalp = oevalp, evalvec = oevalvec; 534 intty = oldintty, whyles = oldwhyl, gointr = ogointr; 535 if (enterhist) 536 HIST = OHIST; 537 enterhist = oenterhist; 538 #ifdef TELL 539 cantell = otell; 540 #endif 541 } 542 543 resexit(oldexit); 544 /* 545 * If process reset() (effectively an unwind) then 546 * we must also unwind. 547 */ 548 if (reenter >= 2) 549 error(NOSTR); 550 insource = 0; 551 } 552 553 rechist() 554 { 555 char buf[BUFSIZ]; 556 int fp, ftmp, oldidfds; 557 558 if (!fast) { 559 if (value("savehist")[0] == '\0') 560 return; 561 (void) strcpy(buf, value("home")); 562 (void) strcat(buf, "/.history"); 563 fp = creat(buf, 0666); 564 if (fp == -1) 565 return; 566 oldidfds = didfds; 567 didfds = 0; 568 ftmp = SHOUT; 569 SHOUT = fp; 570 (void) strcpy(buf, value("savehist")); 571 dumphist[2] = buf; 572 dohist(dumphist); 573 (void) close(fp); 574 SHOUT = ftmp; 575 didfds = oldidfds; 576 } 577 } 578 579 goodbye() 580 { 581 if (loginsh) { 582 (void) signal(SIGQUIT, SIG_IGN); 583 (void) signal(SIGINT, SIG_IGN); 584 (void) signal(SIGTERM, SIG_IGN); 585 setintr = 0; /* No interrupts after "logout" */ 586 if (adrof("home")) 587 srccat(value("home"), "/.logout"); 588 } 589 rechist(); 590 exitstat(); 591 } 592 593 exitstat() 594 { 595 596 #ifdef PROF 597 monitor(0); 598 #endif 599 /* 600 * Note that if STATUS is corrupted (i.e. getn bombs) 601 * then error will exit directly because we poke child here. 602 * Otherwise we might continue unwarrantedly (sic). 603 */ 604 child++; 605 exit(getn(value("status"))); 606 } 607 608 /* 609 * in the event of a HUP we want to save the history 610 */ 611 phup() 612 { 613 rechist(); 614 exit(1); 615 } 616 617 char *jobargv[2] = { "jobs", 0 }; 618 /* 619 * Catch an interrupt, e.g. during lexical input. 620 * If we are an interactive shell, we reset the interrupt catch 621 * immediately. In any case we drain the shell output, 622 * and finally go through the normal error mechanism, which 623 * gets a chance to make the shell go away. 624 */ 625 pintr() 626 { 627 pintr1(1); 628 } 629 630 pintr1(wantnl) 631 bool wantnl; 632 { 633 register char **v; 634 long omask; 635 636 omask = sigblock(0L); 637 if (setintr) { 638 (void) sigsetmask(omask & ~sigmask(SIGINT)); 639 if (pjobs) { 640 pjobs = 0; 641 printf("\n"); 642 dojobs(jobargv); 643 bferr("Interrupted"); 644 } 645 } 646 (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 647 draino(); 648 649 /* 650 * If we have an active "onintr" then we search for the label. 651 * Note that if one does "onintr -" then we shan't be interruptible 652 * so we needn't worry about that here. 653 */ 654 if (gointr) { 655 search(ZGOTO, 0, gointr); 656 timflg = 0; 657 if (v = pargv) 658 pargv = 0, blkfree(v); 659 if (v = gargv) 660 gargv = 0, blkfree(v); 661 reset(); 662 } else if (intty && wantnl) 663 printf("\n"); /* Some like this, others don't */ 664 error(NOSTR); 665 } 666 667 /* 668 * Process is the main driving routine for the shell. 669 * It runs all command processing, except for those within { ... } 670 * in expressions (which is run by a routine evalav in sh.exp.c which 671 * is a stripped down process), and `...` evaluation which is run 672 * also by a subset of this code in sh.glob.c in the routine backeval. 673 * 674 * The code here is a little strange because part of it is interruptible 675 * and hence freeing of structures appears to occur when none is necessary 676 * if this is ignored. 677 * 678 * Note that if catch is not set then we will unwind on any error. 679 * If an end-of-file occurs, we return. 680 */ 681 process(catch) 682 bool catch; 683 { 684 jmp_buf osetexit; 685 register struct command *t; 686 687 getexit(osetexit); 688 for (;;) { 689 pendjob(); 690 paraml.next = paraml.prev = ¶ml; 691 paraml.word = ""; 692 t = 0; 693 setexit(); 694 justpr = enterhist; /* execute if not entering history */ 695 696 /* 697 * Interruptible during interactive reads 698 */ 699 if (setintr) 700 (void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT)); 701 702 /* 703 * For the sake of reset() 704 */ 705 freelex(¶ml), freesyn(t), t = 0; 706 707 if (haderr) { 708 if (!catch) { 709 /* unwind */ 710 doneinp = 0; 711 resexit(osetexit); 712 reset(); 713 } 714 haderr = 0; 715 /* 716 * Every error is eventually caught here or 717 * the shell dies. It is at this 718 * point that we clean up any left-over open 719 * files, by closing all but a fixed number 720 * of pre-defined files. Thus routines don't 721 * have to worry about leaving files open due 722 * to deeper errors... they will get closed here. 723 */ 724 closem(); 725 continue; 726 } 727 if (doneinp) { 728 doneinp = 0; 729 break; 730 } 731 if (chkstop) 732 chkstop--; 733 if (neednote) 734 pnote(); 735 if (intty && prompt && evalvec == 0) { 736 mailchk(); 737 /* 738 * If we are at the end of the input buffer 739 * then we are going to read fresh stuff. 740 * Otherwise, we are rereading input and don't 741 * need or want to prompt. 742 */ 743 if (fseekp == feobp) 744 printprompt(); 745 } 746 err = 0; 747 748 /* 749 * Echo not only on VERBOSE, but also with history expansion. 750 * If there is a lexical error then we forego history echo. 751 */ 752 if (lex(¶ml) && !err && intty || 753 adrof("verbose")) { 754 haderr = 1; 755 prlex(¶ml); 756 haderr = 0; 757 } 758 759 /* 760 * The parser may lose space if interrupted. 761 */ 762 if (setintr) 763 (void) sigblock(sigmask(SIGINT)); 764 765 /* 766 * Save input text on the history list if 767 * reading in old history, or it 768 * is from the terminal at the top level and not 769 * in a loop. 770 */ 771 if (enterhist || catch && intty && !whyles) 772 savehist(¶ml); 773 774 /* 775 * Print lexical error messages, except when sourcing 776 * history lists. 777 */ 778 if (!enterhist && err) 779 error(err); 780 781 /* 782 * If had a history command :p modifier then 783 * this is as far as we should go 784 */ 785 if (justpr) 786 reset(); 787 788 alias(¶ml); 789 790 /* 791 * Parse the words of the input into a parse tree. 792 */ 793 t = syntax(paraml.next, ¶ml, 0); 794 if (err) 795 error(err); 796 797 /* 798 * Execute the parse tree 799 */ 800 execute(t, tpgrp); 801 802 /* 803 * Made it! 804 */ 805 freelex(¶ml), freesyn(t); 806 } 807 resexit(osetexit); 808 } 809 810 dosource(t) 811 register char **t; 812 { 813 register char *f; 814 register int u; 815 bool hflg = 0; 816 char buf[BUFSIZ]; 817 818 t++; 819 if (*t && eq(*t, "-h")) { 820 t++; 821 hflg++; 822 } 823 (void) strcpy(buf, *t); 824 f = globone(buf); 825 u = dmove(open(f, 0), -1); 826 xfree(f); 827 if (u < 0 && !hflg) 828 Perror(f); 829 (void) ioctl(u, FIOCLEX, (char *)0); 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 (void) 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.tv_sec; 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 (void) 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 didfds = 0; /* 0, 1, 2 aren't set up */ 904 (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0); 905 (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0); 906 (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0); 907 (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0); 908 closem(); 909 } 910 911 #ifdef PROF 912 done(i) 913 #else 914 exit(i) 915 #endif 916 int i; 917 { 918 919 untty(); 920 _exit(i); 921 } 922 923 printprompt() 924 { 925 register char *cp; 926 927 if (!whyles) { 928 for (cp = value("prompt"); *cp; cp++) 929 if (*cp == HIST) 930 printf("%d", eventno + 1); 931 else { 932 if (*cp == '\\' && cp[1] == HIST) 933 cp++; 934 cshputchar(*cp | QUOTE); 935 } 936 } else 937 /* 938 * Prompt for forward reading loop 939 * body content. 940 */ 941 printf("? "); 942 flush(); 943 } 944