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