1 /*- 2 * Copyright (c) 1980, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1980, 1991, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)csh.c 8.3 (Berkeley) 03/22/95"; 16 #endif /* not lint */ 17 18 #include <sys/types.h> 19 #include <sys/ioctl.h> 20 #include <sys/stat.h> 21 #include <fcntl.h> 22 #include <errno.h> 23 #include <pwd.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <locale.h> 27 #include <unistd.h> 28 #include <vis.h> 29 #if __STDC__ 30 # include <stdarg.h> 31 #else 32 # include <varargs.h> 33 #endif 34 35 #include "csh.h" 36 #include "proc.h" 37 #include "extern.h" 38 #include "pathnames.h" 39 40 extern bool MapsAreInited; 41 extern bool NLSMapsAreInited; 42 43 /* 44 * C Shell 45 * 46 * Bill Joy, UC Berkeley, California, USA 47 * October 1978, May 1980 48 * 49 * Jim Kulp, IIASA, Laxenburg, Austria 50 * April 1980 51 * 52 * Christos Zoulas, Cornell University 53 * June, 1991 54 */ 55 56 Char *dumphist[] = {STRhistory, STRmh, 0, 0}; 57 Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0}; 58 59 int nofile = 0; 60 bool reenter = 0; 61 bool nverbose = 0; 62 bool nexececho = 0; 63 bool quitit = 0; 64 bool fast = 0; 65 bool batch = 0; 66 bool mflag = 0; 67 bool prompt = 1; 68 bool enterhist = 0; 69 bool tellwhat = 0; 70 71 extern char **environ; 72 73 static int readf __P((void *, char *, int)); 74 static fpos_t seekf __P((void *, fpos_t, int)); 75 static int writef __P((void *, const char *, int)); 76 static int closef __P((void *)); 77 static int srccat __P((Char *, Char *)); 78 static int srcfile __P((char *, bool, bool)); 79 static void phup __P((int)); 80 static void srcunit __P((int, bool, bool)); 81 static void mailchk __P((void)); 82 static Char **defaultpath __P((void)); 83 84 int 85 main(argc, argv) 86 int argc; 87 char **argv; 88 { 89 register Char *cp; 90 register char *tcp; 91 register int f; 92 register char **tempv; 93 struct sigaction oact; 94 sigset_t sigset; 95 96 cshin = stdin; 97 cshout = stdout; 98 csherr = stderr; 99 100 settimes(); /* Immed. estab. timing base */ 101 102 /* 103 * Initialize non constant strings 104 */ 105 #ifdef _PATH_BSHELL 106 STR_BSHELL = SAVE(_PATH_BSHELL); 107 #endif 108 #ifdef _PATH_CSHELL 109 STR_SHELLPATH = SAVE(_PATH_CSHELL); 110 #endif 111 STR_environ = blk2short(environ); 112 environ = short2blk(STR_environ); /* So that we can free it */ 113 STR_WORD_CHARS = SAVE(WORD_CHARS); 114 115 HIST = '!'; 116 HISTSUB = '^'; 117 word_chars = STR_WORD_CHARS; 118 119 tempv = argv; 120 if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ 121 quitit = 1; 122 uid = getuid(); 123 gid = getgid(); 124 euid = geteuid(); 125 egid = getegid(); 126 /* 127 * We are a login shell if: 1. we were invoked as -<something> and we had 128 * no arguments 2. or we were invoked only with the -l flag 129 */ 130 loginsh = (**tempv == '-' && argc == 1) || 131 (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && 132 tempv[1][2] == '\0'); 133 134 if (loginsh && **tempv != '-') { 135 /* 136 * Mangle the argv space 137 */ 138 tempv[1][0] = '\0'; 139 tempv[1][1] = '\0'; 140 tempv[1] = NULL; 141 for (tcp = *tempv; *tcp++;) 142 continue; 143 for (tcp--; tcp >= *tempv; tcp--) 144 tcp[1] = tcp[0]; 145 *++tcp = '-'; 146 argc--; 147 } 148 if (loginsh) 149 (void) time(&chktim); 150 151 AsciiOnly = 1; 152 #ifdef NLS 153 (void) setlocale(LC_ALL, ""); 154 { 155 int k; 156 157 for (k = 0200; k <= 0377 && !Isprint(k); k++) 158 continue; 159 AsciiOnly = k > 0377; 160 } 161 #else 162 AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; 163 #endif /* NLS */ 164 165 /* 166 * Move the descriptors to safe places. The variable didfds is 0 while we 167 * have only FSH* to work with. When didfds is true, we have 0,1,2 and 168 * prefer to use these. 169 */ 170 initdesc(); 171 /* 172 * XXX: This is to keep programs that use stdio happy. 173 * what we really want is freunopen() .... 174 * Closing cshin cshout and csherr (which are really stdin stdout 175 * and stderr at this point and then reopening them in the same order 176 * gives us again stdin == cshin stdout == cshout and stderr == csherr. 177 * If that was not the case builtins like printf that use stdio 178 * would break. But in any case we could fix that with memcpy and 179 * a bit of pointer manipulation... 180 * Fortunately this is not needed under the current implementation 181 * of stdio. 182 */ 183 (void) fclose(cshin); 184 (void) fclose(cshout); 185 (void) fclose(csherr); 186 if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef))) 187 exit(1); 188 if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef))) 189 exit(1); 190 if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef))) 191 exit(1); 192 (void) setvbuf(cshin, NULL, _IOLBF, 0); 193 (void) setvbuf(cshout, NULL, _IOLBF, 0); 194 (void) setvbuf(csherr, NULL, _IOLBF, 0); 195 196 /* 197 * Initialize the shell variables. ARGV and PROMPT are initialized later. 198 * STATUS is also munged in several places. CHILD is munged when 199 * forking/waiting 200 */ 201 set(STRstatus, Strsave(STR0)); 202 203 if ((tcp = getenv("HOME")) != NULL) 204 cp = SAVE(tcp); 205 else 206 cp = NULL; 207 208 if (cp == NULL) 209 fast = 1; /* No home -> can't read scripts */ 210 else 211 set(STRhome, cp); 212 dinit(cp); /* dinit thinks that HOME == cwd in a login 213 * shell */ 214 /* 215 * Grab other useful things from the environment. Should we grab 216 * everything?? 217 */ 218 if ((tcp = getenv("LOGNAME")) != NULL || 219 (tcp = getenv("USER")) != NULL) 220 set(STRuser, SAVE(tcp)); 221 if ((tcp = getenv("TERM")) != NULL) 222 set(STRterm, SAVE(tcp)); 223 224 /* 225 * Re-initialize path if set in environment 226 */ 227 if ((tcp = getenv("PATH")) == NULL) 228 set1(STRpath, defaultpath(), &shvhed); 229 else 230 importpath(SAVE(tcp)); 231 232 set(STRshell, Strsave(STR_SHELLPATH)); 233 234 doldol = putn((int) getpid()); /* For $$ */ 235 shtemp = Strspl(STRtmpsh, doldol); /* For << */ 236 237 /* 238 * Record the interrupt states from the parent process. If the parent is 239 * non-interruptible our hand must be forced or we (and our children) won't 240 * be either. Our children inherit termination from our parent. We catch it 241 * only if we are the login shell. 242 */ 243 /* parents interruptibility */ 244 (void) sigaction(SIGINT, NULL, &oact); 245 parintr = oact.sa_handler; 246 (void) sigaction(SIGTERM, NULL, &oact); 247 parterm = oact.sa_handler; 248 249 if (loginsh) { 250 (void) signal(SIGHUP, phup); /* exit processing on HUP */ 251 (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 252 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 253 } 254 255 /* 256 * Process the arguments. 257 * 258 * Note that processing of -v/-x is actually delayed till after script 259 * processing. 260 * 261 * We set the first character of our name to be '-' if we are a shell 262 * running interruptible commands. Many programs which examine ps'es 263 * use this to filter such shells out. 264 */ 265 argc--, tempv++; 266 while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { 267 do 268 switch (*tcp++) { 269 270 case 0: /* - Interruptible, no prompt */ 271 prompt = 0; 272 setintr = 1; 273 nofile = 1; 274 break; 275 276 case 'b': /* -b Next arg is input file */ 277 batch = 1; 278 break; 279 280 case 'c': /* -c Command input from arg */ 281 if (argc == 1) 282 xexit(0); 283 argc--, tempv++; 284 arginp = SAVE(tempv[0]); 285 prompt = 0; 286 nofile = 1; 287 break; 288 289 case 'e': /* -e Exit on any error */ 290 exiterr = 1; 291 break; 292 293 case 'f': /* -f Fast start */ 294 fast = 1; 295 break; 296 297 case 'i': /* -i Interactive, even if !intty */ 298 intact = 1; 299 nofile = 1; 300 break; 301 302 case 'm': /* -m read .cshrc (from su) */ 303 mflag = 1; 304 break; 305 306 case 'n': /* -n Don't execute */ 307 noexec = 1; 308 break; 309 310 case 'q': /* -q (Undoc'd) ... die on quit */ 311 quitit = 1; 312 break; 313 314 case 's': /* -s Read from std input */ 315 nofile = 1; 316 break; 317 318 case 't': /* -t Read one line from input */ 319 onelflg = 2; 320 prompt = 0; 321 nofile = 1; 322 break; 323 324 case 'v': /* -v Echo hist expanded input */ 325 nverbose = 1; /* ... later */ 326 break; 327 328 case 'x': /* -x Echo just before execution */ 329 nexececho = 1; /* ... later */ 330 break; 331 332 case 'V': /* -V Echo hist expanded input */ 333 setNS(STRverbose); /* NOW! */ 334 break; 335 336 case 'X': /* -X Echo just before execution */ 337 setNS(STRecho); /* NOW! */ 338 break; 339 340 } while (*tcp); 341 tempv++, argc--; 342 } 343 344 if (quitit) /* With all due haste, for debugging */ 345 (void) signal(SIGQUIT, SIG_DFL); 346 347 /* 348 * Unless prevented by -, -c, -i, -s, or -t, if there are remaining 349 * arguments the first of them is the name of a shell file from which to 350 * read commands. 351 */ 352 if (nofile == 0 && argc > 0) { 353 nofile = open(tempv[0], O_RDONLY); 354 if (nofile < 0) { 355 child = 1; /* So this doesn't return */ 356 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 357 } 358 ffile = SAVE(tempv[0]); 359 /* 360 * Replace FSHIN. Handle /dev/std{in,out,err} specially 361 * since once they are closed we cannot open them again. 362 * In that case we use our own saved descriptors 363 */ 364 if ((SHIN = dmove(nofile, FSHIN)) < 0) 365 switch(nofile) { 366 case 0: 367 SHIN = FSHIN; 368 break; 369 case 1: 370 SHIN = FSHOUT; 371 break; 372 case 2: 373 SHIN = FSHERR; 374 break; 375 default: 376 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 377 break; 378 } 379 (void) ioctl(SHIN, FIOCLEX, NULL); 380 prompt = 0; 381 /* argc not used any more */ tempv++; 382 } 383 384 intty = isatty(SHIN); 385 intty |= intact; 386 if (intty || (intact && isatty(SHOUT))) { 387 if (!batch && (uid != euid || gid != egid)) { 388 errno = EACCES; 389 child = 1; /* So this doesn't return */ 390 stderror(ERR_SYSTEM, "csh", strerror(errno)); 391 } 392 } 393 /* 394 * Decide whether we should play with signals or not. If we are explicitly 395 * told (via -i, or -) or we are a login shell (arg0 starts with -) or the 396 * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 397 * Note that in only the login shell is it likely that parent may have set 398 * signals to be ignored 399 */ 400 if (loginsh || intact || (intty && isatty(SHOUT))) 401 setintr = 1; 402 settell(); 403 /* 404 * Save the remaining arguments in argv. 405 */ 406 setq(STRargv, blk2short(tempv), &shvhed); 407 408 /* 409 * Set up the prompt. 410 */ 411 if (prompt) { 412 set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); 413 /* that's a meta-questionmark */ 414 set(STRprompt2, Strsave(STRmquestion)); 415 } 416 417 /* 418 * If we are an interactive shell, then start fiddling with the signals; 419 * this is a tricky game. 420 */ 421 shpgrp = getpgrp(); 422 opgrp = tpgrp = -1; 423 if (setintr) { 424 **argv = '-'; 425 if (!quitit) /* Wary! */ 426 (void) signal(SIGQUIT, SIG_IGN); 427 (void) signal(SIGINT, pintr); 428 sigemptyset(&sigset); 429 sigaddset(&sigset, SIGINT); 430 sigprocmask(SIG_BLOCK, &sigset, NULL); 431 (void) signal(SIGTERM, SIG_IGN); 432 if (quitit == 0 && arginp == 0) { 433 (void) signal(SIGTSTP, SIG_IGN); 434 (void) signal(SIGTTIN, SIG_IGN); 435 (void) signal(SIGTTOU, SIG_IGN); 436 /* 437 * Wait till in foreground, in case someone stupidly runs csh & 438 * dont want to try to grab away the tty. 439 */ 440 if (isatty(FSHERR)) 441 f = FSHERR; 442 else if (isatty(FSHOUT)) 443 f = FSHOUT; 444 else if (isatty(OLDSTD)) 445 f = OLDSTD; 446 else 447 f = -1; 448 retry: 449 if ((tpgrp = tcgetpgrp(f)) != -1) { 450 if (tpgrp != shpgrp) { 451 sig_t old = signal(SIGTTIN, SIG_DFL); 452 (void) kill(0, SIGTTIN); 453 (void) signal(SIGTTIN, old); 454 goto retry; 455 } 456 opgrp = shpgrp; 457 shpgrp = getpid(); 458 tpgrp = shpgrp; 459 /* 460 * Setpgid will fail if we are a session leader and 461 * mypid == mypgrp (POSIX 4.3.3) 462 */ 463 if (opgrp != shpgrp) 464 if (setpgid(0, shpgrp) == -1) 465 goto notty; 466 /* 467 * We do that after we set our process group, to make sure 468 * that the process group belongs to a process in the same 469 * session as the tty (our process and our group) (POSIX 7.2.4) 470 */ 471 if (tcsetpgrp(f, shpgrp) == -1) 472 goto notty; 473 (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); 474 } 475 if (tpgrp == -1) { 476 notty: 477 (void) fprintf(csherr, "Warning: no access to tty (%s).\n", 478 strerror(errno)); 479 (void) fprintf(csherr, "Thus no job control in this shell.\n"); 480 } 481 } 482 } 483 if ((setintr == 0) && (parintr == SIG_DFL)) 484 setintr = 1; 485 (void) signal(SIGCHLD, pchild); /* while signals not ready */ 486 487 /* 488 * Set an exit here in case of an interrupt or error reading the shell 489 * start-up scripts. 490 */ 491 reenter = setexit(); /* PWP */ 492 haderr = 0; /* In case second time through */ 493 if (!fast && reenter == 0) { 494 /* Will have value(STRhome) here because set fast if don't */ 495 { 496 int osetintr = setintr; 497 sig_t oparintr = parintr; 498 sigset_t osigset; 499 500 sigemptyset(&sigset); 501 sigaddset(&sigset, SIGINT); 502 sigprocmask(SIG_BLOCK, &sigset, &osigset); 503 504 setintr = 0; 505 parintr = SIG_IGN; /* Disable onintr */ 506 #ifdef _PATH_DOTCSHRC 507 (void) srcfile(_PATH_DOTCSHRC, 0, 0); 508 #endif 509 if (!fast && !arginp && !onelflg) 510 dohash(NULL, NULL); 511 #ifdef _PATH_DOTLOGIN 512 if (loginsh) 513 (void) srcfile(_PATH_DOTLOGIN, 0, 0); 514 #endif 515 sigprocmask(SIG_SETMASK, &osigset, NULL); 516 setintr = osetintr; 517 parintr = oparintr; 518 } 519 (void) srccat(value(STRhome), STRsldotcshrc); 520 521 if (!fast && !arginp && !onelflg && !havhash) 522 dohash(NULL, NULL); 523 /* 524 * Source history before .login so that it is available in .login 525 */ 526 if ((cp = value(STRhistfile)) != STRNULL) 527 loadhist[2] = cp; 528 dosource(loadhist, NULL); 529 if (loginsh) 530 (void) srccat(value(STRhome), STRsldotlogin); 531 } 532 533 /* 534 * Now are ready for the -v and -x flags 535 */ 536 if (nverbose) 537 setNS(STRverbose); 538 if (nexececho) 539 setNS(STRecho); 540 541 /* 542 * All the rest of the world is inside this call. The argument to process 543 * indicates whether it should catch "error unwinds". Thus if we are a 544 * interactive shell our call here will never return by being blown past on 545 * an error. 546 */ 547 process(setintr); 548 549 /* 550 * Mop-up. 551 */ 552 if (intty) { 553 if (loginsh) { 554 (void) fprintf(cshout, "logout\n"); 555 (void) close(SHIN); 556 child = 1; 557 goodbye(); 558 } 559 else { 560 (void) fprintf(cshout, "exit\n"); 561 } 562 } 563 rechist(); 564 exitstat(); 565 return (0); 566 } 567 568 void 569 untty() 570 { 571 if (tpgrp > 0) { 572 (void) setpgid(0, opgrp); 573 (void) tcsetpgrp(FSHTTY, opgrp); 574 } 575 } 576 577 void 578 importpath(cp) 579 Char *cp; 580 { 581 register int i = 0; 582 register Char *dp; 583 register Char **pv; 584 int c; 585 586 for (dp = cp; *dp; dp++) 587 if (*dp == ':') 588 i++; 589 /* 590 * i+2 where i is the number of colons in the path. There are i+1 591 * directories in the path plus we need room for a zero terminator. 592 */ 593 pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); 594 dp = cp; 595 i = 0; 596 if (*dp) 597 for (;;) { 598 if ((c = *dp) == ':' || c == 0) { 599 *dp = 0; 600 if ((*cp != '/' || *cp == '\0') && (euid == 0 || uid == 0)) 601 (void) fprintf(csherr, 602 "Warning: imported path contains relative components\n"); 603 pv[i++] = Strsave(*cp ? cp : STRdot); 604 if (c) { 605 cp = dp + 1; 606 *dp = ':'; 607 } 608 else 609 break; 610 } 611 dp++; 612 } 613 pv[i] = 0; 614 set1(STRpath, pv, &shvhed); 615 } 616 617 /* 618 * Source to the file which is the catenation of the argument names. 619 */ 620 static int 621 srccat(cp, dp) 622 Char *cp, *dp; 623 { 624 register Char *ep = Strspl(cp, dp); 625 char *ptr = short2str(ep); 626 627 xfree((ptr_t) ep); 628 return srcfile(ptr, mflag ? 0 : 1, 0); 629 } 630 631 /* 632 * Source to a file putting the file descriptor in a safe place (> 2). 633 */ 634 static int 635 srcfile(f, onlyown, flag) 636 char *f; 637 bool onlyown, flag; 638 { 639 register int unit; 640 641 if ((unit = open(f, O_RDONLY)) == -1) 642 return 0; 643 unit = dmove(unit, -1); 644 645 (void) ioctl(unit, FIOCLEX, NULL); 646 srcunit(unit, onlyown, flag); 647 return 1; 648 } 649 650 /* 651 * Source to a unit. If onlyown it must be our file or our group or 652 * we don't chance it. This occurs on ".cshrc"s and the like. 653 */ 654 int insource; 655 static void 656 srcunit(unit, onlyown, hflg) 657 register int unit; 658 bool onlyown, hflg; 659 { 660 /* We have to push down a lot of state here */ 661 /* All this could go into a structure */ 662 int oSHIN = -1, oldintty = intty, oinsource = insource; 663 struct whyle *oldwhyl = whyles; 664 Char *ogointr = gointr, *oarginp = arginp; 665 Char *oevalp = evalp, **oevalvec = evalvec; 666 int oonelflg = onelflg; 667 bool oenterhist = enterhist; 668 char OHIST = HIST; 669 bool otell = cantell; 670 671 struct Bin saveB; 672 sigset_t sigset, osigset; 673 jmp_buf oldexit; 674 675 /* The (few) real local variables */ 676 int my_reenter; 677 678 if (unit < 0) 679 return; 680 if (didfds) 681 donefds(); 682 if (onlyown) { 683 struct stat stb; 684 685 if (fstat(unit, &stb) < 0) { 686 (void) close(unit); 687 return; 688 } 689 } 690 691 /* 692 * There is a critical section here while we are pushing down the input 693 * stream since we have stuff in different structures. If we weren't 694 * careful an interrupt could corrupt SHIN's Bin structure and kill the 695 * shell. 696 * 697 * We could avoid the critical region by grouping all the stuff in a single 698 * structure and pointing at it to move it all at once. This is less 699 * efficient globally on many variable references however. 700 */ 701 insource = 1; 702 getexit(oldexit); 703 704 if (setintr) { 705 sigemptyset(&sigset); 706 sigaddset(&sigset, SIGINT); 707 sigprocmask(SIG_BLOCK, &sigset, &osigset); 708 } 709 /* Setup the new values of the state stuff saved above */ 710 bcopy((char *) &B, (char *) &(saveB), sizeof(B)); 711 fbuf = NULL; 712 fseekp = feobp = fblocks = 0; 713 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 714 intty = isatty(SHIN), whyles = 0, gointr = 0; 715 evalvec = 0; 716 evalp = 0; 717 enterhist = hflg; 718 if (enterhist) 719 HIST = '\0'; 720 721 /* 722 * Now if we are allowing commands to be interrupted, we let ourselves be 723 * interrupted. 724 */ 725 if (setintr) 726 sigprocmask(SIG_SETMASK, &osigset, NULL); 727 settell(); 728 729 if ((my_reenter = setexit()) == 0) 730 process(0); /* 0 -> blow away on errors */ 731 732 if (setintr) 733 sigprocmask(SIG_SETMASK, &osigset, NULL); 734 if (oSHIN >= 0) { 735 register int i; 736 737 /* We made it to the new state... free up its storage */ 738 /* This code could get run twice but xfree doesn't care */ 739 for (i = 0; i < fblocks; i++) 740 xfree((ptr_t) fbuf[i]); 741 xfree((ptr_t) fbuf); 742 743 /* Reset input arena */ 744 bcopy((char *) &(saveB), (char *) &B, sizeof(B)); 745 746 (void) close(SHIN), SHIN = oSHIN; 747 arginp = oarginp, onelflg = oonelflg; 748 evalp = oevalp, evalvec = oevalvec; 749 intty = oldintty, whyles = oldwhyl, gointr = ogointr; 750 if (enterhist) 751 HIST = OHIST; 752 enterhist = oenterhist; 753 cantell = otell; 754 } 755 756 resexit(oldexit); 757 /* 758 * If process reset() (effectively an unwind) then we must also unwind. 759 */ 760 if (my_reenter) 761 stderror(ERR_SILENT); 762 insource = oinsource; 763 } 764 765 void 766 rechist() 767 { 768 Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile; 769 int fp, ftmp, oldidfds; 770 struct varent *shist; 771 772 if (!fast) { 773 /* 774 * If $savehist is just set, we use the value of $history 775 * else we use the value in $savehist 776 */ 777 if ((shist = adrof(STRsavehist)) != NULL) { 778 if (shist->vec[0][0] != '\0') 779 (void) Strcpy(hbuf, shist->vec[0]); 780 else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0') 781 (void) Strcpy(hbuf, shist->vec[0]); 782 else 783 return; 784 } 785 else 786 return; 787 788 if ((hfile = value(STRhistfile)) == STRNULL) { 789 hfile = Strcpy(buf, value(STRhome)); 790 (void) Strcat(buf, STRsldthist); 791 } 792 793 if ((fp = creat(short2str(hfile), 0600)) == -1) 794 return; 795 796 oldidfds = didfds; 797 didfds = 0; 798 ftmp = SHOUT; 799 SHOUT = fp; 800 dumphist[2] = hbuf; 801 dohist(dumphist, NULL); 802 SHOUT = ftmp; 803 (void) close(fp); 804 didfds = oldidfds; 805 } 806 } 807 808 void 809 goodbye() 810 { 811 rechist(); 812 813 if (loginsh) { 814 (void) signal(SIGQUIT, SIG_IGN); 815 (void) signal(SIGINT, SIG_IGN); 816 (void) signal(SIGTERM, SIG_IGN); 817 setintr = 0; /* No interrupts after "logout" */ 818 if (!(adrof(STRlogout))) 819 set(STRlogout, STRnormal); 820 #ifdef _PATH_DOTLOGOUT 821 (void) srcfile(_PATH_DOTLOGOUT, 0, 0); 822 #endif 823 if (adrof(STRhome)) 824 (void) srccat(value(STRhome), STRsldtlogout); 825 } 826 exitstat(); 827 } 828 829 void 830 exitstat() 831 { 832 Char *s; 833 #ifdef PROF 834 monitor(0); 835 #endif 836 /* 837 * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit 838 * directly because we poke child here. Otherwise we might continue 839 * unwarrantedly (sic). 840 */ 841 child = 1; 842 s = value(STRstatus); 843 xexit(s ? getn(s) : 0); 844 } 845 846 /* 847 * in the event of a HUP we want to save the history 848 */ 849 static void 850 phup(sig) 851 int sig; 852 { 853 rechist(); 854 855 /* 856 * We kill the last foreground process group. It then becomes 857 * responsible to propagate the SIGHUP to its progeny. 858 */ 859 { 860 struct process *pp, *np; 861 862 for (pp = proclist.p_next; pp; pp = pp->p_next) { 863 np = pp; 864 /* 865 * Find if this job is in the foreground. It could be that 866 * the process leader has exited and the foreground flag 867 * is cleared for it. 868 */ 869 do 870 /* 871 * If a process is in the foreground; we try to kill 872 * it's process group. If we succeed, then the 873 * whole job is gone. Otherwise we keep going... 874 * But avoid sending HUP to the shell again. 875 */ 876 if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp && 877 killpg(np->p_jobid, SIGHUP) != -1) { 878 /* In case the job was suspended... */ 879 (void) killpg(np->p_jobid, SIGCONT); 880 break; 881 } 882 while ((np = np->p_friends) != pp); 883 } 884 } 885 _exit(sig); 886 } 887 888 Char *jobargv[2] = {STRjobs, 0}; 889 890 /* 891 * Catch an interrupt, e.g. during lexical input. 892 * If we are an interactive shell, we reset the interrupt catch 893 * immediately. In any case we drain the shell output, 894 * and finally go through the normal error mechanism, which 895 * gets a chance to make the shell go away. 896 */ 897 /* ARGSUSED */ 898 void 899 pintr(notused) 900 int notused; 901 { 902 pintr1(1); 903 } 904 905 void 906 pintr1(wantnl) 907 bool wantnl; 908 { 909 Char **v; 910 sigset_t sigset, osigset; 911 912 sigemptyset(&sigset); 913 sigprocmask(SIG_BLOCK, &sigset, &osigset); 914 if (setintr) { 915 sigset = osigset; 916 sigdelset(&sigset, SIGINT); 917 sigprocmask(SIG_SETMASK, &sigset, NULL); 918 if (pjobs) { 919 pjobs = 0; 920 (void) fprintf(cshout, "\n"); 921 dojobs(jobargv, NULL); 922 stderror(ERR_NAME | ERR_INTR); 923 } 924 } 925 sigdelset(&osigset, SIGCHLD); 926 sigprocmask(SIG_SETMASK, &osigset, NULL); 927 (void) fpurge(cshout); 928 (void) endpwent(); 929 930 /* 931 * If we have an active "onintr" then we search for the label. Note that if 932 * one does "onintr -" then we shan't be interruptible so we needn't worry 933 * about that here. 934 */ 935 if (gointr) { 936 gotolab(gointr); 937 timflg = 0; 938 if ((v = pargv) != NULL) 939 pargv = 0, blkfree(v); 940 if ((v = gargv) != NULL) 941 gargv = 0, blkfree(v); 942 reset(); 943 } 944 else if (intty && wantnl) { 945 (void) fputc('\r', cshout); 946 (void) fputc('\n', cshout); 947 } 948 stderror(ERR_SILENT); 949 } 950 951 /* 952 * Process is the main driving routine for the shell. 953 * It runs all command processing, except for those within { ... } 954 * in expressions (which is run by a routine evalav in sh.exp.c which 955 * is a stripped down process), and `...` evaluation which is run 956 * also by a subset of this code in sh.glob.c in the routine backeval. 957 * 958 * The code here is a little strange because part of it is interruptible 959 * and hence freeing of structures appears to occur when none is necessary 960 * if this is ignored. 961 * 962 * Note that if catch is not set then we will unwind on any error. 963 * If an end-of-file occurs, we return. 964 */ 965 static struct command *savet = NULL; 966 void 967 process(catch) 968 bool catch; 969 { 970 jmp_buf osetexit; 971 struct command *t = savet; 972 sigset_t sigset; 973 974 savet = NULL; 975 getexit(osetexit); 976 for (;;) { 977 pendjob(); 978 paraml.next = paraml.prev = ¶ml; 979 paraml.word = STRNULL; 980 (void) setexit(); 981 justpr = enterhist; /* execute if not entering history */ 982 983 /* 984 * Interruptible during interactive reads 985 */ 986 if (setintr) { 987 sigemptyset(&sigset); 988 sigaddset(&sigset, SIGINT); 989 sigprocmask(SIG_UNBLOCK, &sigset, NULL); 990 } 991 992 /* 993 * For the sake of reset() 994 */ 995 freelex(¶ml); 996 if (savet) 997 freesyn(savet), savet = NULL; 998 999 if (haderr) { 1000 if (!catch) { 1001 /* unwind */ 1002 doneinp = 0; 1003 resexit(osetexit); 1004 savet = t; 1005 reset(); 1006 } 1007 haderr = 0; 1008 /* 1009 * Every error is eventually caught here or the shell dies. It is 1010 * at this point that we clean up any left-over open files, by 1011 * closing all but a fixed number of pre-defined files. Thus 1012 * routines don't have to worry about leaving files open due to 1013 * deeper errors... they will get closed here. 1014 */ 1015 closem(); 1016 continue; 1017 } 1018 if (doneinp) { 1019 doneinp = 0; 1020 break; 1021 } 1022 if (chkstop) 1023 chkstop--; 1024 if (neednote) 1025 pnote(); 1026 if (intty && prompt && evalvec == 0) { 1027 mailchk(); 1028 /* 1029 * If we are at the end of the input buffer then we are going to 1030 * read fresh stuff. Otherwise, we are rereading input and don't 1031 * need or want to prompt. 1032 */ 1033 if (aret == F_SEEK && fseekp == feobp) 1034 printprompt(); 1035 (void) fflush(cshout); 1036 } 1037 if (seterr) { 1038 xfree((ptr_t) seterr); 1039 seterr = NULL; 1040 } 1041 1042 /* 1043 * Echo not only on VERBOSE, but also with history expansion. If there 1044 * is a lexical error then we forego history echo. 1045 */ 1046 if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) { 1047 prlex(csherr, ¶ml); 1048 } 1049 1050 /* 1051 * The parser may lose space if interrupted. 1052 */ 1053 if (setintr) 1054 sigprocmask(SIG_BLOCK, &sigset, NULL); 1055 1056 /* 1057 * Save input text on the history list if reading in old history, or it 1058 * is from the terminal at the top level and not in a loop. 1059 * 1060 * PWP: entry of items in the history list while in a while loop is done 1061 * elsewhere... 1062 */ 1063 if (enterhist || (catch && intty && !whyles)) 1064 savehist(¶ml); 1065 1066 /* 1067 * Print lexical error messages, except when sourcing history lists. 1068 */ 1069 if (!enterhist && seterr) 1070 stderror(ERR_OLD); 1071 1072 /* 1073 * If had a history command :p modifier then this is as far as we 1074 * should go 1075 */ 1076 if (justpr) 1077 reset(); 1078 1079 alias(¶ml); 1080 1081 /* 1082 * Parse the words of the input into a parse tree. 1083 */ 1084 savet = syntax(paraml.next, ¶ml, 0); 1085 if (seterr) 1086 stderror(ERR_OLD); 1087 1088 execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); 1089 1090 /* 1091 * Made it! 1092 */ 1093 freelex(¶ml); 1094 freesyn((struct command *) savet), savet = NULL; 1095 } 1096 resexit(osetexit); 1097 savet = t; 1098 } 1099 1100 void 1101 /*ARGSUSED*/ 1102 dosource(v, t) 1103 Char **v; 1104 struct command *t; 1105 1106 { 1107 register Char *f; 1108 bool hflg = 0; 1109 Char buf[BUFSIZ]; 1110 1111 v++; 1112 if (*v && eq(*v, STRmh)) { 1113 if (*++v == NULL) 1114 stderror(ERR_NAME | ERR_HFLAG); 1115 hflg++; 1116 } 1117 (void) Strcpy(buf, *v); 1118 f = globone(buf, G_ERROR); 1119 (void) strcpy((char *) buf, short2str(f)); 1120 xfree((ptr_t) f); 1121 if (!srcfile((char *) buf, 0, hflg) && !hflg) 1122 stderror(ERR_SYSTEM, (char *) buf, strerror(errno)); 1123 } 1124 1125 /* 1126 * Check for mail. 1127 * If we are a login shell, then we don't want to tell 1128 * about any mail file unless its been modified 1129 * after the time we started. 1130 * This prevents us from telling the user things he already 1131 * knows, since the login program insists on saying 1132 * "You have mail." 1133 */ 1134 static void 1135 mailchk() 1136 { 1137 register struct varent *v; 1138 register Char **vp; 1139 time_t t; 1140 int intvl, cnt; 1141 struct stat stb; 1142 bool new; 1143 1144 v = adrof(STRmail); 1145 if (v == 0) 1146 return; 1147 (void) time(&t); 1148 vp = v->vec; 1149 cnt = blklen(vp); 1150 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 1151 if (intvl < 1) 1152 intvl = 1; 1153 if (chktim + intvl > t) 1154 return; 1155 for (; *vp; vp++) { 1156 if (stat(short2str(*vp), &stb) < 0) 1157 continue; 1158 new = stb.st_mtime > time0.tv_sec; 1159 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 1160 (stb.st_atime < chktim && stb.st_mtime < chktim) || 1161 (loginsh && !new)) 1162 continue; 1163 if (cnt == 1) 1164 (void) fprintf(cshout, "You have %smail.\n", new ? "new " : ""); 1165 else 1166 (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail", 1167 vis_str(*vp)); 1168 } 1169 chktim = t; 1170 } 1171 1172 /* 1173 * Extract a home directory from the password file 1174 * The argument points to a buffer where the name of the 1175 * user whose home directory is sought is currently. 1176 * We write the home directory of the user back there. 1177 */ 1178 int 1179 gethdir(home) 1180 Char *home; 1181 { 1182 Char *h; 1183 struct passwd *pw; 1184 1185 /* 1186 * Is it us? 1187 */ 1188 if (*home == '\0') { 1189 if ((h = value(STRhome)) != NULL) { 1190 (void) Strcpy(home, h); 1191 return 0; 1192 } 1193 else 1194 return 1; 1195 } 1196 1197 if ((pw = getpwnam(short2str(home))) != NULL) { 1198 (void) Strcpy(home, str2short(pw->pw_dir)); 1199 return 0; 1200 } 1201 else 1202 return 1; 1203 } 1204 1205 /* 1206 * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17 1207 * We also check if the shell has already changed the decriptor to point to 1208 * 0, 1, 2 when didfds is set. 1209 */ 1210 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0)) 1211 1212 static int 1213 readf(oreo, buf, siz) 1214 void *oreo; 1215 char *buf; 1216 int siz; 1217 { 1218 return read(DESC(oreo), buf, siz); 1219 } 1220 1221 1222 static int 1223 writef(oreo, buf, siz) 1224 void *oreo; 1225 const char *buf; 1226 int siz; 1227 { 1228 return write(DESC(oreo), buf, siz); 1229 } 1230 1231 static fpos_t 1232 seekf(oreo, off, whence) 1233 void *oreo; 1234 fpos_t off; 1235 int whence; 1236 { 1237 return lseek(DESC(oreo), off, whence); 1238 } 1239 1240 1241 static int 1242 closef(oreo) 1243 void *oreo; 1244 { 1245 return close(DESC(oreo)); 1246 } 1247 1248 1249 /* 1250 * Print the visible version of a string. 1251 */ 1252 int 1253 vis_fputc(ch, fp) 1254 int ch; 1255 FILE *fp; 1256 { 1257 char uenc[5]; /* 4 + NULL */ 1258 1259 if (ch & QUOTE) 1260 return fputc(ch & TRIM, fp); 1261 /* 1262 * XXX: When we are in AsciiOnly we want all characters >= 0200 to 1263 * be encoded, but currently there is no way in vis to do that. 1264 */ 1265 (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0); 1266 return fputs(uenc, fp); 1267 } 1268 1269 /* 1270 * Move the initial descriptors to their eventual 1271 * resting places, closin all other units. 1272 */ 1273 void 1274 initdesc() 1275 { 1276 1277 didfds = 0; /* 0, 1, 2 aren't set up */ 1278 (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); 1279 (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); 1280 (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); 1281 (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); 1282 closem(); 1283 } 1284 1285 1286 void 1287 #ifdef PROF 1288 done(i) 1289 #else 1290 xexit(i) 1291 #endif 1292 int i; 1293 { 1294 untty(); 1295 _exit(i); 1296 } 1297 1298 static Char ** 1299 defaultpath() 1300 { 1301 char *ptr; 1302 Char **blk, **blkp; 1303 struct stat stb; 1304 1305 blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); 1306 1307 #define DIRAPPEND(a) \ 1308 if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \ 1309 *blkp++ = SAVE(ptr) 1310 1311 DIRAPPEND(_PATH_BIN); 1312 DIRAPPEND(_PATH_USRBIN); 1313 1314 #undef DIRAPPEND 1315 1316 if (euid != 0 && uid != 0) 1317 *blkp++ = Strsave(STRdot); 1318 *blkp = NULL; 1319 return (blk); 1320 } 1321 1322 void 1323 printprompt() 1324 { 1325 register Char *cp; 1326 1327 if (!whyles) { 1328 for (cp = value(STRprompt); *cp; cp++) 1329 if (*cp == HIST) 1330 (void) fprintf(cshout, "%d", eventno + 1); 1331 else { 1332 if (*cp == '\\' && cp[1] == HIST) 1333 cp++; 1334 (void) vis_fputc(*cp | QUOTE, cshout); 1335 } 1336 } 1337 else 1338 /* 1339 * Prompt for forward reading loop body content. 1340 */ 1341 (void) fprintf(cshout, "? "); 1342 (void) fflush(cshout); 1343 } 1344