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