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