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