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.1 (Berkeley) 06/04/85"; 9 #endif not lint 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 main(c, av) 39 int c; 40 char **av; 41 { 42 register char **v, *cp; 43 register int f; 44 struct sigvec osv; 45 46 settimes(); /* Immed. estab. timing base */ 47 v = av; 48 if (eq(v[0], "a.out")) /* A.out's are quittable */ 49 quitit = 1; 50 uid = getuid(); 51 loginsh = **v == '-' && c == 1; 52 if (loginsh) 53 (void) time(&chktim); 54 55 /* 56 * Move the descriptors to safe places. 57 * The variable didfds is 0 while we have only FSH* to work with. 58 * When didfds is true, we have 0,1,2 and prefer to use these. 59 */ 60 initdesc(); 61 62 /* 63 * Initialize the shell variables. 64 * ARGV and PROMPT are initialized later. 65 * STATUS is also munged in several places. 66 * CHILD is munged when forking/waiting 67 */ 68 69 set("status", "0"); 70 dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a 71 * login shell */ 72 if (cp == NOSTR) 73 fast++; /* No home -> can't read scripts */ 74 else 75 set("home", savestr(cp)); 76 /* 77 * Grab other useful things from the environment. 78 * Should we grab everything?? 79 */ 80 if ((cp = getenv("USER")) != NOSTR) 81 set("user", savestr(cp)); 82 if ((cp = getenv("TERM")) != NOSTR) 83 set("term", savestr(cp)); 84 /* 85 * Re-initialize path if set in environment 86 */ 87 if ((cp = getenv("PATH")) == NOSTR) 88 set1("path", saveblk(pathlist), &shvhed); 89 else 90 importpath(cp); 91 set("shell", SHELLPATH); 92 93 doldol = putn(getpid()); /* For $$ */ 94 shtemp = strspl("/tmp/sh", doldol); /* For << */ 95 96 /* 97 * Record the interrupt states from the parent process. 98 * If the parent is non-interruptible our hand must be forced 99 * or we (and our children) won't be either. 100 * Our children inherit termination from our parent. 101 * We catch it only if we are the login shell. 102 */ 103 /* parents interruptibility */ 104 (void) sigvec(SIGINT, (struct sigvec *)0, &osv); 105 parintr = osv.sv_handler; 106 /* parents terminability */ 107 (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); 108 parterm = osv.sv_handler; 109 if (loginsh) { 110 (void) signal(SIGHUP, phup); /* exit processing on HUP */ 111 (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 112 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 113 } 114 115 /* 116 * Process the arguments. 117 * 118 * Note that processing of -v/-x is actually delayed till after 119 * script processing. 120 */ 121 c--, v++; 122 while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { 123 do switch (*cp++) { 124 125 case 'b': /* -b Next arg is input file */ 126 batch++; 127 break; 128 129 case 'c': /* -c Command input from arg */ 130 if (c == 1) 131 exit(0); 132 c--, v++; 133 arginp = v[0]; 134 prompt = 0; 135 nofile++; 136 break; 137 138 case 'e': /* -e Exit on any error */ 139 exiterr++; 140 break; 141 142 case 'f': /* -f Fast start */ 143 fast++; 144 break; 145 146 case 'i': /* -i Interactive, even if !intty */ 147 intact++; 148 nofile++; 149 break; 150 151 case 'n': /* -n Don't execute */ 152 noexec++; 153 break; 154 155 case 'q': /* -q (Undoc'd) ... die on quit */ 156 quitit = 1; 157 break; 158 159 case 's': /* -s Read from std input */ 160 nofile++; 161 break; 162 163 case 't': /* -t Read one line from input */ 164 onelflg = 2; 165 prompt = 0; 166 nofile++; 167 break; 168 169 case 'v': /* -v Echo hist expanded input */ 170 nverbose = 1; /* ... later */ 171 break; 172 173 case 'x': /* -x Echo just before execution */ 174 nexececho = 1; /* ... later */ 175 break; 176 177 case 'V': /* -V Echo hist expanded input */ 178 setNS("verbose"); /* NOW! */ 179 break; 180 181 case 'X': /* -X Echo just before execution */ 182 setNS("echo"); /* NOW! */ 183 break; 184 185 } while (*cp); 186 v++, c--; 187 } 188 189 if (quitit) /* With all due haste, for debugging */ 190 (void) signal(SIGQUIT, SIG_DFL); 191 192 /* 193 * Unless prevented by -c, -i, -s, or -t, if there 194 * are remaining arguments the first of them is the name 195 * of a shell file from which to read commands. 196 */ 197 if (nofile == 0 && c > 0) { 198 nofile = open(v[0], 0); 199 if (nofile < 0) { 200 child++; /* So this ... */ 201 Perror(v[0]); /* ... doesn't return */ 202 } 203 file = v[0]; 204 SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 205 (void) ioctl(SHIN, FIOCLEX, (char *)0); 206 prompt = 0; 207 c--, v++; 208 } 209 if (!batch && (uid != geteuid() || getgid() != getegid())) { 210 errno = EACCES; 211 child++; /* So this ... */ 212 Perror("csh"); /* ... doesn't return */ 213 } 214 /* 215 * Consider input a tty if it really is or we are interactive. 216 */ 217 intty = intact || isatty(SHIN); 218 /* 219 * Decide whether we should play with signals or not. 220 * If we are explicitly told (via -i, or -) or we are a login 221 * shell (arg0 starts with -) or the input and output are both 222 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 223 * Note that in only the login shell is it likely that parent 224 * may have set signals to be ignored 225 */ 226 if (loginsh || intact || intty && isatty(SHOUT)) 227 setintr = 1; 228 #ifdef TELL 229 settell(); 230 #endif 231 /* 232 * Save the remaining arguments in argv. 233 */ 234 setq("argv", v, &shvhed); 235 236 /* 237 * Set up the prompt. 238 */ 239 if (prompt) 240 set("prompt", uid == 0 ? "# " : "% "); 241 242 /* 243 * If we are an interactive shell, then start fiddling 244 * with the signals; this is a tricky game. 245 */ 246 shpgrp = getpgrp(0); 247 opgrp = tpgrp = -1; 248 oldisc = -1; 249 if (setintr) { 250 **av = '-'; 251 if (!quitit) /* Wary! */ 252 (void) signal(SIGQUIT, SIG_IGN); 253 (void) signal(SIGINT, pintr); 254 (void) sigblock(sigmask(SIGINT)); 255 (void) signal(SIGTERM, SIG_IGN); 256 if (quitit == 0 && arginp == 0) { 257 (void) signal(SIGTSTP, SIG_IGN); 258 (void) signal(SIGTTIN, SIG_IGN); 259 (void) signal(SIGTTOU, SIG_IGN); 260 /* 261 * Wait till in foreground, in case someone 262 * stupidly runs 263 * csh & 264 * dont want to try to grab away the tty. 265 */ 266 if (isatty(FSHDIAG)) 267 f = FSHDIAG; 268 else if (isatty(FSHOUT)) 269 f = FSHOUT; 270 else if (isatty(OLDSTD)) 271 f = OLDSTD; 272 else 273 f = -1; 274 retry: 275 if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && 276 tpgrp != -1) { 277 int ldisc; 278 if (tpgrp != shpgrp) { 279 int (*old)() = signal(SIGTTIN, SIG_DFL); 280 (void) kill(0, SIGTTIN); 281 (void) signal(SIGTTIN, old); 282 goto retry; 283 } 284 if (ioctl(f, TIOCGETD, (char *)&oldisc) != 0) 285 goto notty; 286 if (oldisc != NTTYDISC) { 287 #ifdef DEBUG 288 printf("Switching to new tty driver...\n"); 289 #endif DEBUG 290 ldisc = NTTYDISC; 291 (void) ioctl(f, TIOCSETD, 292 (char *)&ldisc); 293 } else 294 oldisc = -1; 295 opgrp = shpgrp; 296 shpgrp = getpid(); 297 tpgrp = shpgrp; 298 (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); 299 (void) setpgrp(0, shpgrp); 300 (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, 301 (char *)0); 302 } else { 303 notty: 304 printf("Warning: no access to tty; thus no job control in this shell...\n"); 305 tpgrp = -1; 306 } 307 } 308 } 309 if (setintr == 0 && parintr == SIG_DFL) 310 setintr++; 311 (void) signal(SIGCHLD, pchild); /* while signals not ready */ 312 313 /* 314 * Set an exit here in case of an interrupt or error reading 315 * the shell start-up scripts. 316 */ 317 setexit(); 318 haderr = 0; /* In case second time through */ 319 if (!fast && reenter == 0) { 320 reenter++; 321 /* Will have value("home") here because set fast if don't */ 322 srccat(value("home"), "/.cshrc"); 323 if (!fast && !arginp && !onelflg && !havhash) 324 dohash(); 325 if (loginsh) { 326 srccat(value("home"), "/.login"); 327 } 328 dosource(loadhist); 329 } 330 331 /* 332 * Now are ready for the -v and -x flags 333 */ 334 if (nverbose) 335 setNS("verbose"); 336 if (nexececho) 337 setNS("echo"); 338 339 /* 340 * All the rest of the world is inside this call. 341 * The argument to process indicates whether it should 342 * catch "error unwinds". Thus if we are a interactive shell 343 * our call here will never return by being blown past on an error. 344 */ 345 process(setintr); 346 347 /* 348 * Mop-up. 349 */ 350 if (loginsh) { 351 printf("logout\n"); 352 (void) close(SHIN); 353 child++; 354 goodbye(); 355 } 356 rechist(); 357 exitstat(); 358 } 359 360 untty() 361 { 362 363 if (tpgrp > 0) { 364 (void) setpgrp(0, opgrp); 365 (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 366 if (oldisc != -1 && oldisc != NTTYDISC) { 367 #ifdef DEBUG 368 printf("\nReverting to old tty driver...\n"); 369 #endif DEBUG 370 (void) ioctl(FSHTTY, TIOCSETD, (char *)&oldisc); 371 } 372 } 373 } 374 375 importpath(cp) 376 char *cp; 377 { 378 register int i = 0; 379 register char *dp; 380 register char **pv; 381 int c; 382 static char dot[2] = {'.', 0}; 383 384 for (dp = cp; *dp; dp++) 385 if (*dp == ':') 386 i++; 387 /* 388 * i+2 where i is the number of colons in the path. 389 * There are i+1 directories in the path plus we need 390 * room for a zero terminator. 391 */ 392 pv = (char **) calloc((unsigned) (i + 2), sizeof (char **)); 393 dp = cp; 394 i = 0; 395 if (*dp) 396 for (;;) { 397 if ((c = *dp) == ':' || c == 0) { 398 *dp = 0; 399 pv[i++] = savestr(*cp ? cp : dot); 400 if (c) { 401 cp = dp + 1; 402 *dp = ':'; 403 } else 404 break; 405 } 406 dp++; 407 } 408 pv[i] = 0; 409 set1("path", pv, &shvhed); 410 } 411 412 /* 413 * Source to the file which is the catenation of the argument names. 414 */ 415 srccat(cp, dp) 416 char *cp, *dp; 417 { 418 register char *ep = strspl(cp, dp); 419 register int unit = dmove(open(ep, 0), -1); 420 421 (void) ioctl(unit, FIOCLEX, (char *)0); 422 xfree(ep); 423 #ifdef INGRES 424 srcunit(unit, 0, 0); 425 #else 426 srcunit(unit, 1, 0); 427 #endif 428 } 429 430 /* 431 * Source to a unit. If onlyown it must be our file or our group or 432 * we don't chance it. This occurs on ".cshrc"s and the like. 433 */ 434 srcunit(unit, onlyown, hflg) 435 register int unit; 436 bool onlyown; 437 bool hflg; 438 { 439 /* We have to push down a lot of state here */ 440 /* All this could go into a structure */ 441 int oSHIN = -1, oldintty = intty; 442 struct whyle *oldwhyl = whyles; 443 char *ogointr = gointr, *oarginp = arginp; 444 char *oevalp = evalp, **oevalvec = evalvec; 445 int oonelflg = onelflg; 446 bool oenterhist = enterhist; 447 char OHIST = HIST; 448 #ifdef TELL 449 bool otell = cantell; 450 #endif 451 struct Bin saveB; 452 453 /* The (few) real local variables */ 454 jmp_buf oldexit; 455 int reenter, omask; 456 457 if (unit < 0) 458 return; 459 if (didfds) 460 donefds(); 461 if (onlyown) { 462 struct stat stb; 463 464 if (fstat(unit, &stb) < 0 || 465 (stb.st_uid != uid && stb.st_gid != getgid())) { 466 (void) 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 omask = sigblock(sigmask(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 (void) sigsetmask(omask); 505 #ifdef TELL 506 settell(); 507 #endif 508 process(0); /* 0 -> blow away on errors */ 509 } 510 if (setintr) 511 (void) sigsetmask(omask); 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 (void) 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 if (value("savehist")[0] == '\0') 552 return; 553 (void) strcpy(buf, value("home")); 554 (void) strcat(buf, "/.history"); 555 fp = creat(buf, 0666); 556 if (fp == -1) 557 return; 558 oldidfds = didfds; 559 didfds = 0; 560 ftmp = SHOUT; 561 SHOUT = fp; 562 (void) strcpy(buf, value("savehist")); 563 dumphist[2] = buf; 564 dohist(dumphist); 565 (void) close(fp); 566 SHOUT = ftmp; 567 didfds = oldidfds; 568 } 569 } 570 571 goodbye() 572 { 573 if (loginsh) { 574 (void) signal(SIGQUIT, SIG_IGN); 575 (void) signal(SIGINT, SIG_IGN); 576 (void) signal(SIGTERM, SIG_IGN); 577 setintr = 0; /* No interrupts after "logout" */ 578 if (adrof("home")) 579 srccat(value("home"), "/.logout"); 580 } 581 rechist(); 582 exitstat(); 583 } 584 585 exitstat() 586 { 587 588 #ifdef PROF 589 monitor(0); 590 #endif 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 int omask; 627 628 omask = sigblock(0); 629 if (setintr) { 630 (void) sigsetmask(omask & ~sigmask(SIGINT)); 631 if (pjobs) { 632 pjobs = 0; 633 printf("\n"); 634 dojobs(jobargv); 635 bferr("Interrupted"); 636 } 637 } 638 (void) sigsetmask(omask & ~sigmask(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 jmp_buf osetexit; 677 register struct command *t; 678 679 getexit(osetexit); 680 for (;;) { 681 pendjob(); 682 paraml.next = paraml.prev = ¶ml; 683 paraml.word = ""; 684 t = 0; 685 setexit(); 686 justpr = enterhist; /* execute if not entering history */ 687 688 /* 689 * Interruptible during interactive reads 690 */ 691 if (setintr) 692 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 693 694 /* 695 * For the sake of reset() 696 */ 697 freelex(¶ml), freesyn(t), t = 0; 698 699 if (haderr) { 700 if (!catch) { 701 /* unwind */ 702 doneinp = 0; 703 resexit(osetexit); 704 reset(); 705 } 706 haderr = 0; 707 /* 708 * Every error is eventually caught here or 709 * the shell dies. It is at this 710 * point that we clean up any left-over open 711 * files, by closing all but a fixed number 712 * of pre-defined files. Thus routines don't 713 * have to worry about leaving files open due 714 * to deeper errors... they will get closed here. 715 */ 716 closem(); 717 continue; 718 } 719 if (doneinp) { 720 doneinp = 0; 721 break; 722 } 723 if (chkstop) 724 chkstop--; 725 if (neednote) 726 pnote(); 727 if (intty && prompt && evalvec == 0) { 728 mailchk(); 729 /* 730 * If we are at the end of the input buffer 731 * then we are going to read fresh stuff. 732 * Otherwise, we are rereading input and don't 733 * need or want to prompt. 734 */ 735 if (fseekp == feobp) 736 printprompt(); 737 } 738 err = 0; 739 740 /* 741 * Echo not only on VERBOSE, but also with history expansion. 742 * If there is a lexical error then we forego history echo. 743 */ 744 if (lex(¶ml) && !err && intty || 745 adrof("verbose")) { 746 haderr = 1; 747 prlex(¶ml); 748 haderr = 0; 749 } 750 751 /* 752 * The parser may lose space if interrupted. 753 */ 754 if (setintr) 755 (void) sigblock(sigmask(SIGINT)); 756 757 /* 758 * Save input text on the history list if 759 * reading in old history, or it 760 * is from the terminal at the top level and not 761 * in a loop. 762 */ 763 if (enterhist || catch && intty && !whyles) 764 savehist(¶ml); 765 766 /* 767 * Print lexical error messages, except when sourcing 768 * history lists. 769 */ 770 if (!enterhist && err) 771 error(err); 772 773 /* 774 * If had a history command :p modifier then 775 * this is as far as we should go 776 */ 777 if (justpr) 778 reset(); 779 780 alias(¶ml); 781 782 /* 783 * Parse the words of the input into a parse tree. 784 */ 785 t = syntax(paraml.next, ¶ml, 0); 786 if (err) 787 error(err); 788 789 /* 790 * Execute the parse tree 791 */ 792 execute(t, tpgrp); 793 794 /* 795 * Made it! 796 */ 797 freelex(¶ml), freesyn(t); 798 } 799 resexit(osetexit); 800 } 801 802 dosource(t) 803 register char **t; 804 { 805 register char *f; 806 register int u; 807 bool hflg = 0; 808 char buf[BUFSIZ]; 809 810 t++; 811 if (*t && eq(*t, "-h")) { 812 t++; 813 hflg++; 814 } 815 (void) strcpy(buf, *t); 816 f = globone(buf); 817 u = dmove(open(f, 0), -1); 818 xfree(f); 819 if (u < 0 && !hflg) 820 Perror(f); 821 (void) ioctl(u, FIOCLEX, (char *)0); 822 srcunit(u, 0, hflg); 823 } 824 825 /* 826 * Check for mail. 827 * If we are a login shell, then we don't want to tell 828 * about any mail file unless its been modified 829 * after the time we started. 830 * This prevents us from telling the user things he already 831 * knows, since the login program insists on saying 832 * "You have mail." 833 */ 834 mailchk() 835 { 836 register struct varent *v; 837 register char **vp; 838 time_t t; 839 int intvl, cnt; 840 struct stat stb; 841 bool new; 842 843 v = adrof("mail"); 844 if (v == 0) 845 return; 846 (void) time(&t); 847 vp = v->vec; 848 cnt = blklen(vp); 849 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 850 if (intvl < 1) 851 intvl = 1; 852 if (chktim + intvl > t) 853 return; 854 for (; *vp; vp++) { 855 if (stat(*vp, &stb) < 0) 856 continue; 857 new = stb.st_mtime > time0.tv_sec; 858 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 859 (stb.st_atime < chktim && stb.st_mtime < chktim) || 860 loginsh && !new) 861 continue; 862 if (cnt == 1) 863 printf("You have %smail.\n", new ? "new " : ""); 864 else 865 printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 866 } 867 chktim = t; 868 } 869 870 #include <pwd.h> 871 /* 872 * Extract a home directory from the password file 873 * The argument points to a buffer where the name of the 874 * user whose home directory is sought is currently. 875 * We write the home directory of the user back there. 876 */ 877 gethdir(home) 878 char *home; 879 { 880 register struct passwd *pp = getpwnam(home); 881 882 if (pp == 0) 883 return (1); 884 (void) strcpy(home, pp->pw_dir); 885 return (0); 886 } 887 888 /* 889 * Move the initial descriptors to their eventual 890 * resting places, closin all other units. 891 */ 892 initdesc() 893 { 894 895 didfds = 0; /* 0, 1, 2 aren't set up */ 896 (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0); 897 (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0); 898 (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0); 899 (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0); 900 closem(); 901 } 902 903 #ifdef PROF 904 done(i) 905 #else 906 exit(i) 907 #endif 908 int i; 909 { 910 911 untty(); 912 _exit(i); 913 } 914 915 printprompt() 916 { 917 register char *cp; 918 919 if (!whyles) { 920 for (cp = value("prompt"); *cp; cp++) 921 if (*cp == HIST) 922 printf("%d", eventno + 1); 923 else { 924 if (*cp == '\\' && cp[1] == HIST) 925 cp++; 926 putchar(*cp | QUOTE); 927 } 928 } else 929 /* 930 * Prompt for forward reading loop 931 * body content. 932 */ 933 printf("? "); 934 flush(); 935 } 936