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.13 (Berkeley) 09/24/89"; 9 #endif 10 11 #include "sh.h" 12 #include <sys/ioctl.h> 13 #include <sys/file.h> 14 #include "pathnames.h" 15 16 /* 17 * C Shell 18 * 19 * Bill Joy, UC Berkeley, California, USA 20 * October 1978, May 1980 21 * 22 * Jim Kulp, IIASA, Laxenburg, Austria 23 * April 1980 24 */ 25 26 char *pathlist[] = { ".", _PATH_BIN, _PATH_USRBIN, 0 }; 27 char *dumphist[] = { "history", "-h", 0, 0 }; 28 char *loadhist[] = { "source", "-h", "~/.history", 0 }; 29 char HIST = '!'; 30 char HISTSUB = '^'; 31 bool mflag; 32 bool nofile; 33 bool reenter; 34 bool nverbose; 35 bool nexececho; 36 bool quitit; 37 bool fast; 38 bool batch; 39 bool prompt = 1; 40 bool enterhist = 0; 41 42 extern gid_t getegid(), getgid(); 43 extern uid_t geteuid(), getuid(); 44 45 main(c, av) 46 int c; 47 char **av; 48 { 49 register char **v, *cp; 50 register int f; 51 struct sigvec osv; 52 53 settimes(); /* Immed. estab. timing base */ 54 v = av; 55 if (eq(v[0], "a.out")) /* A.out's are quittable */ 56 quitit = 1; 57 uid = getuid(); 58 loginsh = **v == '-' && c == 1; 59 if (loginsh) 60 (void) time(&chktim); 61 62 /* 63 * Move the descriptors to safe places. 64 * The variable didfds is 0 while we have only FSH* to work with. 65 * When didfds is true, we have 0,1,2 and prefer to use these. 66 */ 67 initdesc(); 68 69 /* 70 * Initialize the shell variables. 71 * ARGV and PROMPT are initialized later. 72 * STATUS is also munged in several places. 73 * CHILD is munged when forking/waiting 74 */ 75 76 set("status", "0"); 77 dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a 78 * login shell */ 79 if (cp == NOSTR) 80 fast++; /* No home -> can't read scripts */ 81 else 82 set("home", savestr(cp)); 83 /* 84 * Grab other useful things from the environment. 85 * Should we grab everything?? 86 */ 87 if ((cp = getenv("USER")) != NOSTR) 88 set("user", savestr(cp)); 89 if ((cp = getenv("TERM")) != NOSTR) 90 set("term", savestr(cp)); 91 /* 92 * Re-initialize path if set in environment 93 */ 94 if ((cp = getenv("PATH")) == NOSTR) 95 set1("path", saveblk(pathlist), &shvhed); 96 else 97 importpath(cp); 98 set("shell", _PATH_CSHELL); 99 100 doldol = putn(getpid()); /* For $$ */ 101 shtemp = strspl("/tmp/sh", doldol); /* For << */ 102 103 /* 104 * Record the interrupt states from the parent process. 105 * If the parent is non-interruptible our hand must be forced 106 * or we (and our children) won't be either. 107 * Our children inherit termination from our parent. 108 * We catch it only if we are the login shell. 109 */ 110 /* parents interruptibility */ 111 (void) sigvec(SIGINT, (struct sigvec *)0, &osv); 112 parintr = osv.sv_handler; 113 /* parents terminability */ 114 (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); 115 parterm = osv.sv_handler; 116 if (loginsh) { 117 sig_t phup; 118 119 (void) signal(SIGHUP, phup); /* exit processing on HUP */ 120 (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 121 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 122 } 123 124 /* 125 * Process the arguments. 126 * 127 * Note that processing of -v/-x is actually delayed till after 128 * script processing. 129 */ 130 c--, v++; 131 while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { 132 do switch (*cp++) { 133 134 case 'b': /* -b Next arg is input file */ 135 batch++; 136 break; 137 138 case 'c': /* -c Command input from arg */ 139 if (c == 1) 140 exit(0); 141 c--, v++; 142 arginp = v[0]; 143 prompt = 0; 144 nofile++; 145 break; 146 147 case 'e': /* -e Exit on any error */ 148 exiterr++; 149 break; 150 151 case 'f': /* -f Fast start */ 152 fast++; 153 break; 154 155 case 'i': /* -i Interactive, even if !intty */ 156 intact++; 157 nofile++; 158 break; 159 160 case 'm': /* -m read .cshrc (from su) */ 161 mflag++; 162 break; 163 164 case 'n': /* -n Don't execute */ 165 noexec++; 166 break; 167 168 case 'q': /* -q (Undoc'd) ... die on quit */ 169 quitit = 1; 170 break; 171 172 case 's': /* -s Read from std input */ 173 nofile++; 174 break; 175 176 case 't': /* -t Read one line from input */ 177 onelflg = 2; 178 prompt = 0; 179 nofile++; 180 break; 181 182 case 'v': /* -v Echo hist expanded input */ 183 nverbose = 1; /* ... later */ 184 break; 185 186 case 'x': /* -x Echo just before execution */ 187 nexececho = 1; /* ... later */ 188 break; 189 190 case 'V': /* -V Echo hist expanded input */ 191 setNS("verbose"); /* NOW! */ 192 break; 193 194 case 'X': /* -X Echo just before execution */ 195 setNS("echo"); /* NOW! */ 196 break; 197 198 } while (*cp); 199 v++, c--; 200 } 201 202 if (quitit) /* With all due haste, for debugging */ 203 (void) signal(SIGQUIT, SIG_DFL); 204 205 /* 206 * Unless prevented by -c, -i, -s, or -t, if there 207 * are remaining arguments the first of them is the name 208 * of a shell file from which to read commands. 209 */ 210 if (nofile == 0 && c > 0) { 211 nofile = open(v[0], 0); 212 if (nofile < 0) { 213 child++; /* So this ... */ 214 Perror(v[0]); /* ... doesn't return */ 215 } 216 file = v[0]; 217 SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 218 (void) ioctl(SHIN, FIOCLEX, (char *)0); 219 prompt = 0; 220 c--, v++; 221 } 222 if (!batch && (uid != geteuid() || getgid() != getegid())) { 223 errno = EACCES; 224 child++; /* So this ... */ 225 Perror("csh"); /* ... doesn't return */ 226 } 227 /* 228 * Consider input a tty if it really is or we are interactive. 229 */ 230 intty = intact || isatty(SHIN); 231 /* 232 * Decide whether we should play with signals or not. 233 * If we are explicitly told (via -i, or -) or we are a login 234 * shell (arg0 starts with -) or the input and output are both 235 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 236 * Note that in only the login shell is it likely that parent 237 * may have set signals to be ignored 238 */ 239 if (loginsh || intact || intty && isatty(SHOUT)) 240 setintr = 1; 241 #ifdef TELL 242 settell(); 243 #endif 244 /* 245 * Save the remaining arguments in argv. 246 */ 247 setq("argv", v, &shvhed); 248 249 /* 250 * Set up the prompt. 251 */ 252 if (prompt) 253 set("prompt", uid == 0 ? "# " : "% "); 254 255 /* 256 * If we are an interactive shell, then start fiddling 257 * with the signals; this is a tricky game. 258 */ 259 shpgrp = getpgrp(0); 260 opgrp = tpgrp = -1; 261 if (setintr) { 262 **av = '-'; 263 if (!quitit) /* Wary! */ 264 (void) signal(SIGQUIT, SIG_IGN); 265 (void) signal(SIGINT, pintr); 266 (void) sigblock(sigmask(SIGINT)); 267 (void) signal(SIGTERM, SIG_IGN); 268 if (quitit == 0 && arginp == 0) { 269 (void) signal(SIGTSTP, SIG_IGN); 270 (void) signal(SIGTTIN, SIG_IGN); 271 (void) signal(SIGTTOU, SIG_IGN); 272 /* 273 * Wait till in foreground, in case someone 274 * stupidly runs 275 * csh & 276 * dont want to try to grab away the tty. 277 */ 278 if (isatty(FSHDIAG)) 279 f = FSHDIAG; 280 else if (isatty(FSHOUT)) 281 f = FSHOUT; 282 else if (isatty(OLDSTD)) 283 f = OLDSTD; 284 else 285 f = -1; 286 retry: 287 if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && 288 tpgrp != -1) { 289 int ldisc; 290 if (tpgrp != shpgrp) { 291 sig_t old = signal(SIGTTIN, SIG_DFL); 292 (void) kill(0, SIGTTIN); 293 (void) signal(SIGTTIN, old); 294 goto retry; 295 } 296 opgrp = shpgrp; 297 shpgrp = getpid(); 298 tpgrp = shpgrp; 299 (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); 300 (void) setpgrp(0, shpgrp); 301 (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, 302 (char *)0); 303 } else { 304 notty: 305 printf("Warning: no access to tty; thus no job control in this shell...\n"); 306 tpgrp = -1; 307 } 308 } 309 } 310 if (setintr == 0 && parintr == SIG_DFL) 311 setintr++; 312 (void) signal(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 { 323 int osetintr, omask; 324 osetintr = setintr; 325 omask = sigblock(sigmask(SIGINT)); 326 setintr = 0; 327 srcunit(open(_PATH_DOTCSHRC, O_RDONLY), 0, 0); 328 if (!fast && !arginp && !onelflg) 329 dohash(); 330 if (loginsh) 331 srcunit(open(_PATH_DOTLOGIN, O_RDONLY), 0, 0); 332 (void)sigsetmask(omask); 333 setintr = osetintr; 334 } 335 /* Will have value("home") here because set fast if don't */ 336 srccat(value("home"), "/.cshrc"); 337 if (!fast && !arginp && !onelflg && !havhash) 338 dohash(); 339 if (loginsh) { 340 srccat(value("home"), "/.login"); 341 } 342 dosource(loadhist); 343 } 344 345 /* 346 * Now are ready for the -v and -x flags 347 */ 348 if (nverbose) 349 setNS("verbose"); 350 if (nexececho) 351 setNS("echo"); 352 353 /* 354 * All the rest of the world is inside this call. 355 * The argument to process indicates whether it should 356 * catch "error unwinds". Thus if we are a interactive shell 357 * our call here will never return by being blown past on an error. 358 */ 359 process(setintr); 360 361 /* 362 * Mop-up. 363 */ 364 if (loginsh) { 365 printf("logout\n"); 366 (void) close(SHIN); 367 child++; 368 goodbye(); 369 } 370 rechist(); 371 exitstat(); 372 } 373 374 untty() 375 { 376 377 if (tpgrp > 0) { 378 (void) setpgrp(0, opgrp); 379 (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 380 } 381 } 382 383 importpath(cp) 384 char *cp; 385 { 386 register int i = 0; 387 register char *dp; 388 register char **pv; 389 int c; 390 static char dot[2] = {'.', 0}; 391 392 for (dp = cp; *dp; dp++) 393 if (*dp == ':') 394 i++; 395 /* 396 * i+2 where i is the number of colons in the path. 397 * There are i+1 directories in the path plus we need 398 * room for a zero terminator. 399 */ 400 pv = (char **) calloc((unsigned) (i + 2), sizeof (char **)); 401 dp = cp; 402 i = 0; 403 if (*dp) 404 for (;;) { 405 if ((c = *dp) == ':' || c == 0) { 406 *dp = 0; 407 pv[i++] = savestr(*cp ? cp : dot); 408 if (c) { 409 cp = dp + 1; 410 *dp = ':'; 411 } else 412 break; 413 } 414 dp++; 415 } 416 pv[i] = 0; 417 set1("path", pv, &shvhed); 418 } 419 420 /* 421 * Source to the file which is the catenation of the argument names. 422 */ 423 srccat(cp, dp) 424 char *cp, *dp; 425 { 426 register char *ep = strspl(cp, dp); 427 register int unit = dmove(open(ep, 0), -1); 428 429 (void) ioctl(unit, FIOCLEX, (char *)0); 430 xfree(ep); 431 srcunit(unit, mflag ? 0 : 1, 0); 432 } 433 434 /* 435 * Source to a unit. 436 * This occurs on ".cshrc"s and the like. 437 */ 438 int insource; 439 static 440 srcunit(unit, onlyown, hflg) 441 register int unit; 442 bool onlyown, 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 srcunit(open(_PATH_DOTLOGOUT, O_RDONLY), 0, 0); 587 if (adrof("home")) 588 srccat(value("home"), "/.logout"); 589 } 590 rechist(); 591 exitstat(); 592 } 593 594 exitstat() 595 { 596 597 #ifdef PROF 598 monitor(0); 599 #endif 600 /* 601 * Note that if STATUS is corrupted (i.e. getn bombs) 602 * then error will exit directly because we poke child here. 603 * Otherwise we might continue unwarrantedly (sic). 604 */ 605 child++; 606 exit(getn(value("status"))); 607 } 608 609 /* 610 * in the event of a HUP we want to save the history 611 */ 612 phup() 613 { 614 rechist(); 615 exit(1); 616 } 617 618 char *jobargv[2] = { "jobs", 0 }; 619 /* 620 * Catch an interrupt, e.g. during lexical input. 621 * If we are an interactive shell, we reset the interrupt catch 622 * immediately. In any case we drain the shell output, 623 * and finally go through the normal error mechanism, which 624 * gets a chance to make the shell go away. 625 */ 626 pintr() 627 { 628 pintr1(1); 629 } 630 631 pintr1(wantnl) 632 bool wantnl; 633 { 634 register char **v; 635 long omask; 636 637 omask = sigblock(0L); 638 if (setintr) { 639 (void) sigsetmask(omask & ~sigmask(SIGINT)); 640 if (pjobs) { 641 pjobs = 0; 642 printf("\n"); 643 dojobs(jobargv); 644 bferr("Interrupted"); 645 } 646 } 647 (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 648 draino(); 649 650 /* 651 * If we have an active "onintr" then we search for the label. 652 * Note that if one does "onintr -" then we shan't be interruptible 653 * so we needn't worry about that here. 654 */ 655 if (gointr) { 656 search(ZGOTO, 0, gointr); 657 timflg = 0; 658 if (v = pargv) 659 pargv = 0, blkfree(v); 660 if (v = gargv) 661 gargv = 0, blkfree(v); 662 reset(); 663 } else if (intty && wantnl) 664 printf("\n"); /* Some like this, others don't */ 665 error(NOSTR); 666 } 667 668 /* 669 * Process is the main driving routine for the shell. 670 * It runs all command processing, except for those within { ... } 671 * in expressions (which is run by a routine evalav in sh.exp.c which 672 * is a stripped down process), and `...` evaluation which is run 673 * also by a subset of this code in sh.glob.c in the routine backeval. 674 * 675 * The code here is a little strange because part of it is interruptible 676 * and hence freeing of structures appears to occur when none is necessary 677 * if this is ignored. 678 * 679 * Note that if catch is not set then we will unwind on any error. 680 * If an end-of-file occurs, we return. 681 */ 682 process(catch) 683 bool catch; 684 { 685 jmp_buf osetexit; 686 register struct command *t; 687 688 getexit(osetexit); 689 for (;;) { 690 pendjob(); 691 paraml.next = paraml.prev = ¶ml; 692 paraml.word = ""; 693 t = 0; 694 setexit(); 695 justpr = enterhist; /* execute if not entering history */ 696 697 /* 698 * Interruptible during interactive reads 699 */ 700 if (setintr) 701 (void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT)); 702 703 /* 704 * For the sake of reset() 705 */ 706 freelex(¶ml), freesyn(t), t = 0; 707 708 if (haderr) { 709 if (!catch) { 710 /* unwind */ 711 doneinp = 0; 712 resexit(osetexit); 713 reset(); 714 } 715 haderr = 0; 716 /* 717 * Every error is eventually caught here or 718 * the shell dies. It is at this 719 * point that we clean up any left-over open 720 * files, by closing all but a fixed number 721 * of pre-defined files. Thus routines don't 722 * have to worry about leaving files open due 723 * to deeper errors... they will get closed here. 724 */ 725 closem(); 726 continue; 727 } 728 if (doneinp) { 729 doneinp = 0; 730 break; 731 } 732 if (chkstop) 733 chkstop--; 734 if (neednote) 735 pnote(); 736 if (intty && prompt && evalvec == 0) { 737 mailchk(); 738 /* 739 * If we are at the end of the input buffer 740 * then we are going to read fresh stuff. 741 * Otherwise, we are rereading input and don't 742 * need or want to prompt. 743 */ 744 if (fseekp == feobp) 745 printprompt(); 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 (void) sigblock(sigmask(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 (void) 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 (void) ioctl(u, FIOCLEX, (char *)0); 831 srcunit(u, 0, hflg); 832 } 833 834 /* 835 * Check for mail. 836 * If we are a login shell, then we don't want to tell 837 * about any mail file unless its been modified 838 * after the time we started. 839 * This prevents us from telling the user things he already 840 * knows, since the login program insists on saying 841 * "You have mail." 842 */ 843 mailchk() 844 { 845 register struct varent *v; 846 register char **vp; 847 time_t t; 848 int intvl, cnt; 849 struct stat stb; 850 bool new; 851 852 v = adrof("mail"); 853 if (v == 0) 854 return; 855 (void) time(&t); 856 vp = v->vec; 857 cnt = blklen(vp); 858 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 859 if (intvl < 1) 860 intvl = 1; 861 if (chktim + intvl > t) 862 return; 863 for (; *vp; vp++) { 864 if (stat(*vp, &stb) < 0) 865 continue; 866 new = stb.st_mtime > time0.tv_sec; 867 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 868 (stb.st_atime < chktim && stb.st_mtime < chktim) || 869 loginsh && !new) 870 continue; 871 if (cnt == 1) 872 printf("You have %smail.\n", new ? "new " : ""); 873 else 874 printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 875 } 876 chktim = t; 877 } 878 879 #include <pwd.h> 880 /* 881 * Extract a home directory from the password file 882 * The argument points to a buffer where the name of the 883 * user whose home directory is sought is currently. 884 * We write the home directory of the user back there. 885 */ 886 gethdir(home) 887 char *home; 888 { 889 register struct passwd *pp = getpwnam(home); 890 891 if (pp == 0) 892 return (1); 893 (void) strcpy(home, pp->pw_dir); 894 return (0); 895 } 896 897 /* 898 * Move the initial descriptors to their eventual 899 * resting places, closin all other units. 900 */ 901 initdesc() 902 { 903 904 didfds = 0; /* 0, 1, 2 aren't set up */ 905 (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0); 906 (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0); 907 (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0); 908 (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0); 909 closem(); 910 } 911 912 #ifdef PROF 913 done(i) 914 #else 915 exit(i) 916 #endif 917 int i; 918 { 919 920 untty(); 921 _exit(i); 922 } 923 924 printprompt() 925 { 926 register char *cp; 927 928 if (!whyles) { 929 for (cp = value("prompt"); *cp; cp++) 930 if (*cp == HIST) 931 printf("%d", eventno + 1); 932 else { 933 if (*cp == '\\' && cp[1] == HIST) 934 cp++; 935 cshputchar(*cp | QUOTE); 936 } 937 } else 938 /* 939 * Prompt for forward reading loop 940 * body content. 941 */ 942 printf("? "); 943 flush(); 944 } 945