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