1 /* $Header: /src/pub/tcsh/sh.c,v 3.92 2000/11/11 23:03:35 christos Exp $ */ 2 /* 3 * sh.c: Main shell routines 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #define EXTERN /* Intern */ 38 #include "sh.h" 39 40 #ifndef lint 41 char copyright[] = 42 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 43 All rights reserved.\n"; 44 #endif /* not lint */ 45 46 RCSID("$Id: sh.c,v 3.92 2000/11/11 23:03:35 christos Exp $") 47 48 #include "tc.h" 49 #include "ed.h" 50 #include "tw.h" 51 52 extern bool MapsAreInited; 53 extern bool NLSMapsAreInited; 54 extern bool NoNLSRebind; 55 56 /* 57 * C Shell 58 * 59 * Bill Joy, UC Berkeley, California, USA 60 * October 1978, May 1980 61 * 62 * Jim Kulp, IIASA, Laxenburg, Austria 63 * April 1980 64 * 65 * Filename recognition added: 66 * Ken Greer, Ind. Consultant, Palo Alto CA 67 * October 1983. 68 * 69 * Karl Kleinpaste, Computer Consoles, Inc. 70 * Added precmd, periodic/tperiod, prompt changes, 71 * directory stack hack, and login watch. 72 * Sometime March 1983 - Feb 1984. 73 * 74 * Added scheduled commands, including the "sched" command, 75 * plus the call to sched_run near the precmd et al 76 * routines. 77 * Upgraded scheduled events for running events while 78 * sitting idle at command input. 79 * 80 * Paul Placeway, Ohio State 81 * added stuff for running with twenex/inputl 9 Oct 1984. 82 * 83 * ported to Apple Unix (TM) (OREO) 26 -- 29 Jun 1987 84 */ 85 86 jmp_buf_t reslab INIT_ZERO_STRUCT; 87 88 static const char tcshstr[] = "tcsh"; 89 #ifdef WINNT_NATIVE 90 static const char tcshstr_nt[] = "tcsh.exe"; 91 #endif /* WINNT_NATIVE */ 92 93 signalfun_t parintr = 0; /* Parents interrupt catch */ 94 signalfun_t parterm = 0; /* Parents terminate catch */ 95 96 #ifdef TESLA 97 int do_logout = 0; 98 #endif /* TESLA */ 99 100 101 bool use_fork = 0; /* use fork() instead of vfork()? */ 102 103 /* 104 * Magic pointer values. Used to specify other invalid conditions aside 105 * from null. 106 */ 107 static Char INVCHAR; 108 Char *INVPTR = &INVCHAR; 109 Char **INVPPTR = &INVPTR; 110 111 static int nofile = 0; 112 static bool reenter = 0; 113 static bool nverbose = 0; 114 static bool nexececho = 0; 115 static bool quitit = 0; 116 static bool rdirs = 0; 117 bool fast = 0; 118 static bool batch = 0; 119 static bool mflag = 0; 120 static bool prompt = 1; 121 static int enterhist = 0; 122 bool tellwhat = 0; 123 time_t t_period; 124 Char *ffile = NULL; 125 bool dolzero = 0; 126 int insource = 0; 127 int exitset = 0; 128 static time_t chktim; /* Time mail last checked */ 129 char *progname; 130 int tcsh; 131 extern char **environ; 132 133 /* 134 * This preserves the input state of the shell. It is used by 135 * st_save and st_restore to manupulate shell state. 136 */ 137 struct saved_state { 138 int insource; 139 int SHIN; 140 int intty; 141 struct whyle *whyles; 142 Char *gointr; 143 Char *arginp; 144 Char *evalp; 145 Char **evalvec; 146 Char *alvecp; 147 Char **alvec; 148 int onelflg; 149 bool enterhist; 150 Char **argv; 151 Char HIST; 152 bool cantell; 153 struct Bin B; 154 /* These keep signal state and setjump state */ 155 #ifdef BSDSIGS 156 sigmask_t mask; 157 #endif 158 jmp_buf_t oldexit; 159 int reenter; 160 }; 161 162 static int srccat __P((Char *, Char *)); 163 static int srcfile __P((char *, bool, int, Char **)); 164 static sigret_t phup __P((int)); 165 static void srcunit __P((int, bool, int, Char **)); 166 static void mailchk __P((void)); 167 #ifndef _PATH_DEFPATH 168 static Char **defaultpath __P((void)); 169 #endif 170 static void record __P((void)); 171 static void st_save __P((struct saved_state *, int, int, 172 Char **, Char **)); 173 static void st_restore __P((struct saved_state *, Char **)); 174 175 int main __P((int, char **)); 176 177 int 178 main(argc, argv) 179 int argc; 180 char **argv; 181 { 182 register Char *cp; 183 #ifdef AUTOLOGOUT 184 register Char *cp2; 185 #endif 186 register char *tcp, *ttyn; 187 register int f; 188 register char **tempv; 189 190 #ifdef BSDSIGS 191 sigvec_t osv; 192 #endif /* BSDSIGS */ 193 194 #ifdef WINNT_NATIVE 195 nt_init(); 196 #endif /* WINNT_NATIVE */ 197 #if defined(NLS_CATALOGS) && defined(LC_MESSAGES) 198 (void) setlocale(LC_MESSAGES, ""); 199 #endif /* NLS_CATALOGS && LC_MESSAGES */ 200 201 #ifdef NLS 202 # ifdef LC_CTYPE 203 (void) setlocale(LC_CTYPE, ""); /* for iscntrl */ 204 # endif /* LC_CTYPE */ 205 #endif /* NLS */ 206 207 nlsinit(); 208 209 #ifdef MALLOC_TRACE 210 mal_setstatsfile(fdopen(dup2(open("/tmp/tcsh.trace", 211 O_WRONLY|O_CREAT, 0666), 25), "w")); 212 mal_trace(1); 213 #endif /* MALLOC_TRACE */ 214 215 #if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX) 216 # ifdef _SC_CLK_TCK 217 clk_tck = (clock_t) sysconf(_SC_CLK_TCK); 218 # else /* ! _SC_CLK_TCK */ 219 # ifdef CLK_TCK 220 clk_tck = CLK_TCK; 221 # else /* !CLK_TCK */ 222 clk_tck = HZ; 223 # endif /* CLK_TCK */ 224 # endif /* _SC_CLK_TCK */ 225 #endif /* !BSDTIMES && POSIX */ 226 227 settimes(); /* Immed. estab. timing base */ 228 #ifdef TESLA 229 do_logout = 0; 230 #endif /* TESLA */ 231 232 /* 233 * Make sure we have 0, 1, 2 open 234 * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr) 235 */ 236 { 237 do 238 if ((f = open(_PATH_DEVNULL, O_RDONLY)) == -1 && 239 (f = open("/", O_RDONLY)) == -1) 240 exit(1); 241 while (f < 3); 242 (void) close(f); 243 } 244 245 osinit(); /* Os dependent initialization */ 246 247 248 { 249 char *t; 250 251 t = strrchr(argv[0], '/'); 252 #ifdef WINNT_NATIVE 253 { 254 char *s = strrchr(argv[0], '\\'); 255 if (s) 256 t = s; 257 } 258 #endif /* WINNT_NATIVE */ 259 t = t ? t + 1 : argv[0]; 260 if (*t == '-') t++; 261 progname = strsave((t && *t) ? t : tcshstr); /* never want a null */ 262 tcsh = strcmp(progname, tcshstr) == 0; 263 } 264 265 /* 266 * Initialize non constant strings 267 */ 268 #ifdef _PATH_BSHELL 269 STR_BSHELL = SAVE(_PATH_BSHELL); 270 #endif 271 #ifdef _PATH_TCSHELL 272 STR_SHELLPATH = SAVE(_PATH_TCSHELL); 273 #else 274 # ifdef _PATH_CSHELL 275 STR_SHELLPATH = SAVE(_PATH_CSHELL); 276 # endif 277 #endif 278 STR_environ = blk2short(environ); 279 environ = short2blk(STR_environ); /* So that we can free it */ 280 STR_WORD_CHARS = SAVE(WORD_CHARS); 281 282 HIST = '!'; 283 HISTSUB = '^'; 284 PRCH = '>'; 285 PRCHROOT = '#'; 286 word_chars = STR_WORD_CHARS; 287 bslash_quote = 0; /* PWP: do tcsh-style backslash quoting? */ 288 289 /* Default history size to 100 */ 290 set(STRhistory, SAVE("100"), VAR_READWRITE); 291 292 tempv = argv; 293 ffile = SAVE(tempv[0]); 294 dolzero = 0; 295 if (eq(ffile, STRaout)) /* A.out's are quittable */ 296 quitit = 1; 297 uid = getuid(); 298 gid = getgid(); 299 euid = geteuid(); 300 egid = getegid(); 301 #if defined(OREO) || defined(DT_SUPPORT) 302 /* 303 * We are a login shell if: 1. we were invoked as -<something> with 304 * optional arguments 2. or we were invoked only with the -l flag 305 */ 306 loginsh = (**tempv == '-') || (argc == 2 && 307 tempv[1][0] == '-' && tempv[1][1] == 'l' && 308 tempv[1][2] == '\0'); 309 #else 310 /* 311 * We are a login shell if: 1. we were invoked as -<something> and we had 312 * no arguments 2. or we were invoked only with the -l flag 313 */ 314 loginsh = (**tempv == '-' && argc == 1) || (argc == 2 && 315 tempv[1][0] == '-' && tempv[1][1] == 'l' && 316 tempv[1][2] == '\0'); 317 #endif 318 319 #ifdef _VMS_POSIX 320 /* No better way to find if we are a login shell */ 321 if (!loginsh) { 322 loginsh = (argc == 1 && getppid() == 1); 323 **tempv = '-'; /* Avoid giving VMS an acidic stomach */ 324 } 325 #endif /* _VMS_POSIX */ 326 327 if (loginsh && **tempv != '-') { 328 /* 329 * Mangle the argv space 330 */ 331 tempv[1][0] = '\0'; 332 tempv[1][1] = '\0'; 333 tempv[1] = NULL; 334 for (tcp = *tempv; *tcp++;) 335 continue; 336 for (tcp--; tcp >= *tempv; tcp--) 337 tcp[1] = tcp[0]; 338 *++tcp = '-'; 339 argc--; 340 } 341 if (loginsh) { 342 (void) time(&chktim); 343 set(STRloginsh, Strsave(STRNULL), VAR_READWRITE); 344 } 345 346 AsciiOnly = 1; 347 NoNLSRebind = getenv("NOREBIND") != NULL; 348 #ifdef NLS 349 # ifdef SETLOCALEBUG 350 dont_free = 1; 351 # endif /* SETLOCALEBUG */ 352 (void) setlocale(LC_ALL, ""); 353 # ifdef LC_COLLATE 354 (void) setlocale(LC_COLLATE, ""); 355 # endif 356 # ifdef SETLOCALEBUG 357 dont_free = 0; 358 # endif /* SETLOCALEBUG */ 359 # ifdef STRCOLLBUG 360 fix_strcoll_bug(); 361 # endif /* STRCOLLBUG */ 362 363 { 364 int k; 365 366 for (k = 0200; k <= 0377 && !Isprint(k); k++) 367 continue; 368 AsciiOnly = k > 0377; 369 } 370 #else 371 AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; 372 #endif /* NLS */ 373 if (MapsAreInited && !NLSMapsAreInited) 374 ed_InitNLSMaps(); 375 ResetArrowKeys(); 376 377 /* 378 * Initialize for periodic command intervals. Also, initialize the dummy 379 * tty list for login-watch. 380 */ 381 (void) time(&t_period); 382 #ifndef HAVENOUTMP 383 initwatch(); 384 #endif /* !HAVENOUTMP */ 385 386 #if defined(alliant) 387 /* 388 * From: Jim Pace <jdp@research.att.com> 389 * tcsh does not work properly on the alliants through an rlogin session. 390 * The shell generally hangs. Also, reference to the controlling terminal 391 * does not work ( ie: echo foo > /dev/tty ). 392 * 393 * A security feature was added to rlogind affecting FX/80's Concentrix 394 * from revision 5.5.xx upwards (through 5.7 where this fix was implemented) 395 * This security change also affects the FX/2800 series. 396 * The security change to rlogind requires the process group of an rlogin 397 * session become disassociated with the tty in rlogind. 398 * 399 * The changes needed are: 400 * 1. set the process group 401 * 2. reenable the control terminal 402 */ 403 if (loginsh && isatty(SHIN)) { 404 ttyn = (char *) ttyname(SHIN); 405 (void) close(SHIN); 406 SHIN = open(ttyn, O_RDWR); 407 shpgrp = getpid(); 408 (void) ioctl (SHIN, TIOCSPGRP, (ioctl_t) &shpgrp); 409 (void) setpgid(0, shpgrp); 410 } 411 #endif /* alliant */ 412 413 /* 414 * Move the descriptors to safe places. The variable didfds is 0 while we 415 * have only FSH* to work with. When didfds is true, we have 0,1,2 and 416 * prefer to use these. 417 */ 418 initdesc(); 419 420 /* 421 * Get and set the tty now 422 */ 423 if ((ttyn = ttyname(SHIN)) != NULL) { 424 /* 425 * Could use rindex to get rid of other possible path components, but 426 * hpux preserves the subdirectory /pty/ when storing the tty name in 427 * utmp, so we keep it too. 428 */ 429 if (strncmp(ttyn, "/dev/", 5) == 0) 430 set(STRtty, cp = SAVE(ttyn + 5), VAR_READWRITE); 431 else 432 set(STRtty, cp = SAVE(ttyn), VAR_READWRITE); 433 } 434 else 435 set(STRtty, cp = SAVE(""), VAR_READWRITE); 436 /* 437 * Initialize the shell variables. ARGV and PROMPT are initialized later. 438 * STATUS is also munged in several places. CHILD is munged when 439 * forking/waiting 440 */ 441 442 /* 443 * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and 444 * on shells running as root. Out of these, autologout should NOT be set 445 * for any psudo-terminals (this catches most window systems) and not for 446 * any terminal running X windows. 447 * 448 * At Ohio State, we have had problems with a user having his X session 449 * drop out from under him (on a Sun) because the shell in his master 450 * xterm timed out and exited. 451 * 452 * Really, this should be done with a program external to the shell, that 453 * watches for no activity (and NO running programs, such as dump) on a 454 * terminal for a long peroid of time, and then SIGHUPS the shell on that 455 * terminal. 456 * 457 * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things 458 * allways first check to see if loginsh or really root, then do things 459 * with ttyname() 460 * 461 * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the 462 * value of cp before using it! ("root can rsh too") 463 * 464 * PWP: keep the nested ifs; the order of the tests matters and a good 465 * (smart) C compiler might re-arange things wrong. 466 */ 467 #ifdef AUTOLOGOUT 468 # ifdef convex 469 if (uid == 0) { 470 /* root always has a 15 minute autologout */ 471 set(STRautologout, Strsave(STRrootdefautologout), VAR_READWRITE); 472 } 473 else 474 if (loginsh) 475 /* users get autologout set to 0 */ 476 set(STRautologout, Strsave(STR0), VAR_READWRITE); 477 # else /* convex */ 478 if (loginsh || (uid == 0)) { 479 if (*cp) { 480 /* only for login shells or root and we must have a tty */ 481 if ((cp2 = Strrchr(cp, (Char) '/')) != NULL) { 482 cp = cp2 + 1; 483 } 484 else 485 cp2 = cp; 486 if (!(((Strncmp(cp2, STRtty, 3) == 0) && Isalpha(cp2[3])) || 487 ((Strncmp(cp, STRpts, 3) == 0) && cp[3] == '/'))) { 488 if (getenv("DISPLAY") == NULL) { 489 /* NOT on X window shells */ 490 set(STRautologout, Strsave(STRdefautologout), 491 VAR_READWRITE); 492 } 493 } 494 } 495 } 496 # endif /* convex */ 497 #endif /* AUTOLOGOUT */ 498 499 (void) sigset(SIGALRM, alrmcatch); 500 501 set(STRstatus, Strsave(STR0), VAR_READWRITE); 502 503 /* 504 * get and set machine specific envirnment variables 505 */ 506 getmachine(); 507 508 fix_version(); /* publish the shell version */ 509 510 /* 511 * Publish the selected echo style 512 */ 513 #if ECHO_STYLE == NONE_ECHO 514 set(STRecho_style, Strsave(STRnone), VAR_READWRITE); 515 #endif /* ECHO_STYLE == NONE_ECHO */ 516 #if ECHO_STYLE == BSD_ECHO 517 set(STRecho_style, Strsave(STRbsd), VAR_READWRITE); 518 #endif /* ECHO_STYLE == BSD_ECHO */ 519 #if ECHO_STYLE == SYSV_ECHO 520 set(STRecho_style, Strsave(STRsysv), VAR_READWRITE); 521 #endif /* ECHO_STYLE == SYSV_ECHO */ 522 #if ECHO_STYLE == BOTH_ECHO 523 set(STRecho_style, Strsave(STRboth), VAR_READWRITE); 524 #endif /* ECHO_STYLE == BOTH_ECHO */ 525 526 /* 527 * increment the shell level. 528 */ 529 shlvl(1); 530 531 if ((tcp = getenv("HOME")) != NULL) 532 cp = quote(SAVE(tcp)); 533 else 534 cp = NULL; 535 if (cp == NULL) 536 fast = 1; /* No home -> can't read scripts */ 537 else 538 set(STRhome, cp, VAR_READWRITE); 539 dinit(cp); /* dinit thinks that HOME == cwd in a login 540 * shell */ 541 /* 542 * Grab other useful things from the environment. Should we grab 543 * everything?? 544 */ 545 { 546 char *cln, *cus, *cgr; 547 Char buff[BUFSIZE]; 548 struct passwd *pw; 549 struct group *gr; 550 551 552 #ifdef apollo 553 int oid = getoid(); 554 555 (void) Itoa(oid, buff, 0, 0); 556 set(STRoid, Strsave(buff), VAR_READWRITE); 557 #endif /* apollo */ 558 559 (void) Itoa(uid, buff, 0, 0); 560 set(STRuid, Strsave(buff), VAR_READWRITE); 561 562 (void) Itoa(gid, buff, 0, 0); 563 set(STRgid, Strsave(buff), VAR_READWRITE); 564 565 cln = getenv("LOGNAME"); 566 cus = getenv("USER"); 567 if (cus != NULL) 568 set(STRuser, quote(SAVE(cus)), VAR_READWRITE); 569 else if (cln != NULL) 570 set(STRuser, quote(SAVE(cln)), VAR_READWRITE); 571 else if ((pw = getpwuid(uid)) == NULL) 572 set(STRuser, SAVE("unknown"), VAR_READWRITE); 573 else 574 set(STRuser, SAVE(pw->pw_name), VAR_READWRITE); 575 if (cln == NULL) 576 tsetenv(STRLOGNAME, varval(STRuser)); 577 if (cus == NULL) 578 tsetenv(STRKUSER, varval(STRuser)); 579 580 cgr = getenv("GROUP"); 581 if (cgr != NULL) 582 set(STRgroup, quote(SAVE(cgr)), VAR_READWRITE); 583 else if ((gr = getgrgid(gid)) == NULL) 584 set(STRgroup, SAVE("unknown"), VAR_READWRITE); 585 else 586 set(STRgroup, SAVE(gr->gr_name), VAR_READWRITE); 587 if (cgr == NULL) 588 tsetenv(STRKGROUP, varval(STRgroup)); 589 } 590 591 /* 592 * HOST may be wrong, since rexd transports the entire environment on sun 593 * 3.x Just set it again 594 */ 595 { 596 char cbuff[MAXHOSTNAMELEN]; 597 598 if (gethostname(cbuff, sizeof(cbuff)) >= 0) { 599 cbuff[sizeof(cbuff) - 1] = '\0'; /* just in case */ 600 tsetenv(STRHOST, str2short(cbuff)); 601 } 602 else 603 tsetenv(STRHOST, str2short("unknown")); 604 } 605 606 607 #ifdef REMOTEHOST 608 /* 609 * Try to determine the remote host we were logged in from. 610 */ 611 remotehost(); 612 #endif /* REMOTEHOST */ 613 614 #ifdef apollo 615 if ((tcp = getenv("SYSTYPE")) == NULL) 616 tcp = "bsd4.3"; 617 tsetenv(STRSYSTYPE, quote(str2short(tcp))); 618 #endif /* apollo */ 619 620 /* 621 * set editing on by default, unless running under Emacs as an inferior 622 * shell. 623 * We try to do this intelligently. If $TERM is available, then it 624 * should determine if we should edit or not. $TERM is preserved 625 * across rlogin sessions, so we will not get confused if we rlogin 626 * under an emacs shell. Another advantage is that if we run an 627 * xterm under an emacs shell, then the $TERM will be set to 628 * xterm, so we are going to want to edit. Unfortunately emacs 629 * does not restore all the tty modes, so xterm is not very well 630 * set up. But this is not the shell's fault. 631 * Also don't edit if $TERM == wm, for when we're running under an ATK app. 632 * Finally, emacs compiled under terminfo, sets the terminal to dumb, 633 * so disable editing for that too. 634 * 635 * Unfortunately, in some cases the initial $TERM setting is "unknown", 636 * "dumb", or "network" which is then changed in the user's startup files. 637 * We fix this by setting noediting here if $TERM is unknown/dumb and 638 * if noediting is set, we switch on editing if $TERM is changed. 639 */ 640 if ((tcp = getenv("TERM")) != NULL) { 641 set(STRterm, quote(SAVE(tcp)), VAR_READWRITE); 642 noediting = strcmp(tcp, "unknown") == 0 || strcmp(tcp, "dumb") == 0 || 643 strcmp(tcp, "network") == 0; 644 editing = strcmp(tcp, "emacs") != 0 && strcmp(tcp, "wm") != 0 && 645 !noediting; 646 } 647 else { 648 noediting = 0; 649 editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0); 650 } 651 652 /* 653 * The 'edit' variable is either set or unset. It doesn't 654 * need a value. Making it 'emacs' might be confusing. 655 */ 656 if (editing) 657 set(STRedit, Strsave(STRNULL), VAR_READWRITE); 658 659 660 /* 661 * still more mutability: make the complete routine automatically add the 662 * suffix of file names... 663 */ 664 set(STRaddsuffix, Strsave(STRNULL), VAR_READWRITE); 665 666 /* 667 * Re-initialize path if set in environment 668 */ 669 if ((tcp = getenv("PATH")) == NULL) 670 #ifdef _PATH_DEFPATH 671 importpath(str2short(_PATH_DEFPATH)); 672 #else /* !_PATH_DEFPATH */ 673 setq(STRpath, defaultpath(), &shvhed, VAR_READWRITE); 674 #endif /* _PATH_DEFPATH */ 675 else 676 /* Importpath() allocates memory for the path, and the 677 * returned pointer from SAVE() was discarded, so 678 * this was a memory leak.. (sg) 679 * 680 * importpath(SAVE(tcp)); 681 */ 682 importpath(str2short(tcp)); 683 684 685 { 686 /* If the SHELL environment variable ends with "tcsh", set 687 * STRshell to the same path. This is to facilitate using 688 * the executable in environments where the compiled-in 689 * default isn't appropriate (sg). 690 */ 691 692 int sh_len = 0; 693 694 if ((tcp = getenv("SHELL")) != NULL) { 695 sh_len = strlen(tcp); 696 if ((sh_len >= 5 && strcmp(tcp + (sh_len - 5), "/tcsh") == 0) || 697 (!tcsh && sh_len >= 4 && strcmp(tcp + (sh_len - 4), "/csh") == 0)) 698 set(STRshell, quote(SAVE(tcp)), VAR_READWRITE); 699 else 700 sh_len = 0; 701 } 702 if (sh_len == 0) 703 set(STRshell, Strsave(STR_SHELLPATH), VAR_READWRITE); 704 } 705 706 #ifdef COLOR_LS_F 707 if ((tcp = getenv("LS_COLORS")) != NULL) 708 parseLS_COLORS(str2short(tcp)); 709 #endif /* COLOR_LS_F */ 710 711 doldol = putn((int) getpid()); /* For $$ */ 712 #ifdef WINNT_NATIVE 713 { 714 char *strtmp1, strtmp2[MAXPATHLEN]; 715 if ((strtmp1 = getenv("TMP")) != NULL) 716 wsprintf(strtmp2, "%s/%s", strtmp1, "sh"); 717 shtemp = Strspl(SAVE(strtmp2), doldol); /* For << */ 718 } 719 #else /* !WINNT_NATIVE */ 720 shtemp = Strspl(STRtmpsh, doldol); /* For << */ 721 #endif /* WINNT_NATIVE */ 722 723 /* 724 * Record the interrupt states from the parent process. If the parent is 725 * non-interruptible our hand must be forced or we (and our children) won't 726 * be either. Our children inherit termination from our parent. We catch it 727 * only if we are the login shell. 728 */ 729 #ifdef BSDSIGS 730 /* 731 * PURIFY-2 claims that osv does not get 732 * initialized after the sigvec call 733 */ 734 setzero((char*) &osv, sizeof(osv)); 735 /* parents interruptibility */ 736 (void) mysigvec(SIGINT, NULL, &osv); 737 parintr = (signalfun_t) osv.sv_handler; 738 (void) mysigvec(SIGTERM, NULL, &osv); 739 parterm = (signalfun_t) osv.sv_handler; 740 #else /* BSDSIGS */ 741 parintr = signal(SIGINT, SIG_IGN); /* parents interruptibility */ 742 (void) sigset(SIGINT, parintr); /* ... restore */ 743 744 # ifdef COHERENT 745 if (loginsh) /* it seems that SIGTERM is always set to SIG_IGN by */ 746 /* init/getty so it should be set to SIG_DFL - there may be */ 747 /* a better fix for this. */ 748 parterm = SIG_DFL; 749 else 750 # else /* !COHERENT */ 751 parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */ 752 # endif /* COHERENT */ 753 (void) sigset(SIGTERM, parterm); /* ... restore */ 754 755 #endif /* BSDSIGS */ 756 757 758 #ifdef TCF 759 /* Enable process migration on ourselves and our progeny */ 760 (void) signal(SIGMIGRATE, SIG_DFL); 761 #endif /* TCF */ 762 763 /* 764 * dspkanji/dspmbyte autosetting 765 */ 766 /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */ 767 #if defined(DSPMBYTE) 768 #if defined(NLS) && defined(LC_CTYPE) 769 if (((tcp = setlocale(LC_CTYPE, NULL)) != NULL || (tcp = getenv("LANG")) != NULL) && !adrof(CHECK_MBYTEVAR)) { 770 #else 771 if ((tcp = getenv("LANG")) != NULL && !adrof(CHECK_MBYTEVAR)) { 772 #endif 773 autoset_dspmbyte(str2short(tcp)); 774 } 775 #if defined(WINNT_NATIVE) 776 else if (!adrof(CHECK_MBYTEVAR)) 777 nt_autoset_dspmbyte(); 778 #endif /* WINNT_NATIVE */ 779 #endif 780 781 /* 782 * Process the arguments. 783 * 784 * Note that processing of -v/-x is actually delayed till after script 785 * processing. 786 * 787 * We set the first character of our name to be '-' if we are a shell 788 * running interruptible commands. Many programs which examine ps'es 789 * use this to filter such shells out. 790 */ 791 argc--, tempv++; 792 while (argc > 0 && (tcp = tempv[0])[0] == '-' && 793 *++tcp != '\0' && !batch) { 794 do 795 switch (*tcp++) { 796 797 case 0: /* - Interruptible, no prompt */ 798 prompt = 0; 799 setintr = 1; 800 nofile = 1; 801 break; 802 803 case 'b': /* -b Next arg is input file */ 804 batch = 1; 805 break; 806 807 case 'c': /* -c Command input from arg */ 808 if (argc == 1) 809 xexit(0); 810 argc--, tempv++; 811 #ifdef M_XENIX 812 /* Xenix Vi bug: 813 it relies on a 7 bit environment (/bin/sh), so it 814 pass ascii arguments with the 8th bit set */ 815 if (!strcmp(argv[0], "sh")) 816 { 817 char *p; 818 819 for (p = tempv[0]; *p; ++p) 820 *p &= ASCII; 821 } 822 #endif 823 arginp = SAVE(tempv[0]); 824 825 /* 826 * we put the command into a variable 827 */ 828 if (arginp != NULL) 829 set(STRcommand, quote(Strsave(arginp)), VAR_READWRITE); 830 831 /* 832 * * Give an error on -c arguments that end in * backslash to 833 * ensure that you don't make * nonportable csh scripts. 834 */ 835 { 836 register int count; 837 838 cp = arginp + Strlen(arginp); 839 count = 0; 840 while (cp > arginp && *--cp == '\\') 841 ++count; 842 if ((count & 1) != 0) { 843 exiterr = 1; 844 stderror(ERR_ARGC); 845 } 846 } 847 prompt = 0; 848 nofile = 1; 849 break; 850 case 'd': /* -d Load directory stack from file */ 851 rdirs = 1; 852 break; 853 854 #ifdef apollo 855 case 'D': /* -D Define environment variable */ 856 { 857 register Char *dp; 858 859 cp = str2short(tcp); 860 if (dp = Strchr(cp, '=')) { 861 *dp++ = '\0'; 862 tsetenv(cp, dp); 863 } 864 else 865 tsetenv(cp, STRNULL); 866 } 867 *tcp = '\0'; /* done with this argument */ 868 break; 869 #endif /* apollo */ 870 871 case 'e': /* -e Exit on any error */ 872 exiterr = 1; 873 break; 874 875 case 'f': /* -f Fast start */ 876 fast = 1; 877 break; 878 879 case 'i': /* -i Interactive, even if !intty */ 880 intact = 1; 881 nofile = 1; 882 break; 883 884 case 'm': /* -m read .cshrc (from su) */ 885 mflag = 1; 886 break; 887 888 case 'n': /* -n Don't execute */ 889 noexec = 1; 890 break; 891 892 case 'q': /* -q (Undoc'd) ... die on quit */ 893 quitit = 1; 894 break; 895 896 case 's': /* -s Read from std input */ 897 nofile = 1; 898 break; 899 900 case 't': /* -t Read one line from input */ 901 onelflg = 2; 902 prompt = 0; 903 nofile = 1; 904 break; 905 906 case 'v': /* -v Echo hist expanded input */ 907 nverbose = 1; /* ... later */ 908 break; 909 910 case 'x': /* -x Echo just before execution */ 911 nexececho = 1; /* ... later */ 912 break; 913 914 case 'V': /* -V Echo hist expanded input */ 915 setNS(STRverbose); /* NOW! */ 916 break; 917 918 case 'X': /* -X Echo just before execution */ 919 setNS(STRecho); /* NOW! */ 920 break; 921 922 case 'F': /* Undocumented flag */ 923 /* 924 * This will cause children to be created using fork instead of 925 * vfork. 926 */ 927 use_fork = 1; 928 break; 929 930 case ' ': 931 case '\t': 932 /* 933 * for O/S's that don't do the argument parsing right in 934 * "#!/foo -f " scripts 935 */ 936 break; 937 938 default: /* Unknown command option */ 939 exiterr = 1; 940 stderror(ERR_TCSHUSAGE, tcp-1, progname); 941 break; 942 943 } while (*tcp); 944 tempv++, argc--; 945 } 946 947 if (quitit) /* With all due haste, for debugging */ 948 (void) signal(SIGQUIT, SIG_DFL); 949 950 /* 951 * Unless prevented by -, -c, -i, -s, or -t, if there are remaining 952 * arguments the first of them is the name of a shell file from which to 953 * read commands. 954 */ 955 if (nofile == 0 && argc > 0) { 956 nofile = open(tempv[0], O_RDONLY); 957 if (nofile < 0) { 958 child = 1; /* So this ... */ 959 /* ... doesn't return */ 960 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 961 } 962 if (ffile != NULL) 963 xfree((ptr_t) ffile); 964 dolzero = 1; 965 ffile = SAVE(tempv[0]); 966 /* 967 * Replace FSHIN. Handle /dev/std{in,out,err} specially 968 * since once they are closed we cannot open them again. 969 * In that case we use our own saved descriptors 970 */ 971 if ((SHIN = dmove(nofile, FSHIN)) < 0) 972 switch(nofile) { 973 case 0: 974 SHIN = FSHIN; 975 break; 976 case 1: 977 SHIN = FSHOUT; 978 break; 979 case 2: 980 SHIN = FSHDIAG; 981 break; 982 default: 983 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 984 break; 985 } 986 (void) close_on_exec(SHIN, 1); 987 prompt = 0; 988 /* argc not used any more */ tempv++; 989 } 990 991 992 /* 993 * Consider input a tty if it really is or we are interactive. but not for 994 * editing (christos) 995 */ 996 if (!(intty = isatty(SHIN))) { 997 if (adrof(STRedit)) 998 unsetv(STRedit); 999 editing = 0; 1000 } 1001 intty |= intact; 1002 #ifndef convex 1003 if (intty || (intact && isatty(SHOUT))) { 1004 if (!batch && (uid != euid || gid != egid)) { 1005 errno = EACCES; 1006 child = 1; /* So this ... */ 1007 /* ... doesn't return */ 1008 stderror(ERR_SYSTEM, progname, strerror(errno)); 1009 } 1010 } 1011 #endif /* convex */ 1012 isoutatty = isatty(SHOUT); 1013 isdiagatty = isatty(SHDIAG); 1014 /* 1015 * Decide whether we should play with signals or not. If we are explicitly 1016 * told (via -i, or -) or we are a login shell (arg0 starts with -) or the 1017 * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 1018 * Note that in only the login shell is it likely that parent may have set 1019 * signals to be ignored 1020 */ 1021 if (loginsh || intact || (intty && isatty(SHOUT))) 1022 setintr = 1; 1023 settell(); 1024 /* 1025 * Save the remaining arguments in argv. 1026 */ 1027 setq(STRargv, blk2short(tempv), &shvhed, VAR_READWRITE); 1028 1029 /* 1030 * Set up the prompt. 1031 */ 1032 if (prompt) { 1033 if (tcsh) 1034 set(STRprompt, Strsave(STRdeftcshprompt), VAR_READWRITE); 1035 else 1036 set(STRprompt, Strsave(STRdefcshprompt), VAR_READWRITE); 1037 /* that's a meta-questionmark */ 1038 set(STRprompt2, Strsave(STRmquestion), VAR_READWRITE); 1039 set(STRprompt3, Strsave(STRKCORRECT), VAR_READWRITE); 1040 } 1041 1042 /* 1043 * If we are an interactive shell, then start fiddling with the signals; 1044 * this is a tricky game. 1045 */ 1046 shpgrp = mygetpgrp(); 1047 opgrp = tpgrp = -1; 1048 if (setintr) { 1049 signalfun_t osig; 1050 **argv = '-'; 1051 if (!quitit) /* Wary! */ 1052 (void) signal(SIGQUIT, SIG_IGN); 1053 (void) sigset(SIGINT, pintr); 1054 (void) sighold(SIGINT); 1055 (void) signal(SIGTERM, SIG_IGN); 1056 1057 /* 1058 * No reason I can see not to save history on all these events.. 1059 * Most usual occurrence is in a window system, where we're not a login 1060 * shell, but might as well be... (sg) 1061 * But there might be races when lots of shells exit together... 1062 * [this is also incompatible]. 1063 * We have to be mre careful here. If the parent wants to 1064 * ignore the signals then we leave them untouched... 1065 * We also only setup the handlers for shells that are trully 1066 * interactive. 1067 */ 1068 osig = signal(SIGHUP, phup); /* exit processing on HUP */ 1069 if (!loginsh && osig == SIG_IGN) 1070 (void) signal(SIGHUP, osig); 1071 #ifdef SIGXCPU 1072 osig = signal(SIGXCPU, phup); /* exit processing on XCPU */ 1073 if (!loginsh && osig == SIG_IGN) 1074 (void) signal(SIGXCPU, osig); 1075 #endif 1076 #ifdef SIGXFSZ 1077 osig = signal(SIGXFSZ, phup); /* exit processing on XFSZ */ 1078 if (!loginsh && osig == SIG_IGN) 1079 (void) signal(SIGXFSZ, osig); 1080 #endif 1081 1082 if (quitit == 0 && arginp == 0) { 1083 #ifdef SIGTSTP 1084 (void) signal(SIGTSTP, SIG_IGN); 1085 #endif 1086 #ifdef SIGTTIN 1087 (void) signal(SIGTTIN, SIG_IGN); 1088 #endif 1089 #ifdef SIGTTOU 1090 (void) signal(SIGTTOU, SIG_IGN); 1091 #endif 1092 /* 1093 * Wait till in foreground, in case someone stupidly runs csh & 1094 * dont want to try to grab away the tty. 1095 */ 1096 if (isatty(FSHDIAG)) 1097 f = FSHDIAG; 1098 else if (isatty(FSHOUT)) 1099 f = FSHOUT; 1100 else if (isatty(OLDSTD)) 1101 f = OLDSTD; 1102 else 1103 f = -1; 1104 1105 #ifdef NeXT 1106 /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */ 1107 if (shpgrp == 0) { 1108 shpgrp = getpid(); 1109 (void) setpgid(0, shpgrp); 1110 (void) tcsetpgrp(f, shpgrp); 1111 } 1112 #endif /* NeXT */ 1113 #ifdef BSDJOBS /* if we have tty job control */ 1114 retry: 1115 if ((tpgrp = tcgetpgrp(f)) != -1) { 1116 if (tpgrp != shpgrp) { 1117 signalfun_t old = signal(SIGTTIN, SIG_DFL); 1118 (void) kill(0, SIGTTIN); 1119 (void) signal(SIGTTIN, old); 1120 goto retry; 1121 } 1122 /* 1123 * Thanks to Matt Day for the POSIX references, and to 1124 * Paul Close for the SGI clarification. 1125 */ 1126 if (setdisc(f) != -1) { 1127 opgrp = shpgrp; 1128 shpgrp = getpid(); 1129 tpgrp = shpgrp; 1130 if (tcsetpgrp(f, shpgrp) == -1) { 1131 /* 1132 * On hpux 7.03 this fails with EPERM. This happens on 1133 * the 800 when opgrp != shpgrp at this point. (we were 1134 * forked from a non job control shell) 1135 * POSIX 7.2.4, says we failed because the process 1136 * group specified did not belong to a process 1137 * in the same session with the tty. So we set our 1138 * process group and try again. 1139 */ 1140 if (setpgid(0, shpgrp) == -1) { 1141 xprintf("setpgid:"); 1142 goto notty; 1143 } 1144 if (tcsetpgrp(f, shpgrp) == -1) { 1145 xprintf("tcsetpgrp:"); 1146 goto notty; 1147 } 1148 } 1149 /* 1150 * We check the process group now. If it is the same, then 1151 * we don't need to set it again. On hpux 7.0 on the 300's 1152 * if we set it again it fails with EPERM. This is the 1153 * correct behavior according to POSIX 4.3.3 if the process 1154 * was a session leader . 1155 */ 1156 else if (shpgrp != mygetpgrp()) { 1157 if(setpgid(0, shpgrp) == -1) { 1158 xprintf("setpgid:"); 1159 goto notty; 1160 } 1161 } 1162 #ifdef IRIS4D 1163 /* 1164 * But on irix 3.3 we need to set it again, even if it is 1165 * the same. We do that to tell the system that we 1166 * need BSD process group compatibility. 1167 */ 1168 else 1169 (void) setpgid(0, shpgrp); 1170 #endif 1171 (void) close_on_exec(dcopy(f, FSHTTY), 1); 1172 } 1173 else 1174 tpgrp = -1; 1175 } 1176 if (tpgrp == -1) { 1177 notty: 1178 xprintf(CGETS(11, 1, "Warning: no access to tty (%s).\n"), 1179 strerror(errno)); 1180 xprintf(CGETS(11, 2, "Thus no job control in this shell.\n")); 1181 /* 1182 * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't 1183 * have access to tty, disable editing too 1184 */ 1185 if (adrof(STRedit)) 1186 unsetv(STRedit); 1187 editing = 0; 1188 } 1189 #else /* BSDJOBS */ /* don't have job control, so frotz it */ 1190 tpgrp = -1; 1191 #endif /* BSDJOBS */ 1192 } 1193 } 1194 if ((setintr == 0) && (parintr == SIG_DFL)) 1195 setintr = 1; 1196 1197 /* 1198 * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the 1199 * handler is installed with signal(2) or sigset(2). sigaction(2) must 1200 * be used instead. 1201 * 1202 * David Dawes (dawes@physics.su.oz.au) Sept 1991 1203 */ 1204 1205 #if SYSVREL > 3 1206 { 1207 struct sigaction act; 1208 act.sa_handler=pchild; 1209 (void) sigemptyset(&(act.sa_mask)); /* Don't block any extra sigs 1210 * when the handler is called 1211 */ 1212 act.sa_flags=0; /* want behaviour of sigset() without 1213 * SA_NOCLDSTOP 1214 */ 1215 1216 if ((sigaction(SIGCHLD,&act,(struct sigaction *)NULL)) == -1) 1217 stderror(ERR_SYSTEM, "sigaction", strerror(errno)); 1218 } 1219 #else /* SYSVREL <= 3 */ 1220 (void) sigset(SIGCHLD, pchild); /* while signals not ready */ 1221 #endif /* SYSVREL <= 3 */ 1222 1223 1224 if (intty && !arginp) 1225 (void) ed_Setup(editing);/* Get the tty state, and set defaults */ 1226 /* Only alter the tty state if editing */ 1227 1228 /* 1229 * Set an exit here in case of an interrupt or error reading the shell 1230 * start-up scripts. 1231 */ 1232 reenter = setexit(); /* PWP */ 1233 exitset++; 1234 haderr = 0; /* In case second time through */ 1235 if (!fast && reenter == 0) { 1236 /* Will have varval(STRhome) here because set fast if don't */ 1237 { 1238 int osetintr = setintr; 1239 signalfun_t oparintr = parintr; 1240 1241 #ifdef BSDSIGS 1242 sigmask_t omask = sigblock(sigmask(SIGINT)); 1243 #else 1244 (void) sighold(SIGINT); 1245 #endif 1246 setintr = 0; 1247 parintr = SIG_IGN; /* onintr in /etc/ files has no effect */ 1248 #ifdef LOGINFIRST 1249 #ifdef _PATH_DOTLOGIN 1250 if (loginsh) 1251 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL); 1252 #endif 1253 #endif 1254 1255 #ifdef _PATH_DOTCSHRC 1256 (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL); 1257 #endif 1258 if (!arginp && !onelflg && !havhash) 1259 dohash(NULL,NULL); 1260 #ifndef LOGINFIRST 1261 #ifdef _PATH_DOTLOGIN 1262 if (loginsh) 1263 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL); 1264 #endif 1265 #endif 1266 #ifdef BSDSIGS 1267 (void) sigsetmask(omask); 1268 #else 1269 (void) sigrelse(SIGINT); 1270 #endif 1271 setintr = osetintr; 1272 parintr = oparintr; 1273 } 1274 #ifdef LOGINFIRST 1275 if (loginsh) 1276 (void) srccat(varval(STRhome), STRsldotlogin); 1277 #endif 1278 /* upward compat. */ 1279 if (!srccat(varval(STRhome), STRsldottcshrc)) 1280 (void) srccat(varval(STRhome), STRsldotcshrc); 1281 1282 if (!fast && !arginp && !onelflg && !havhash) 1283 dohash(NULL,NULL); 1284 1285 /* 1286 * Source history before .login so that it is available in .login 1287 */ 1288 loadhist(NULL, 0); 1289 #ifndef LOGINFIRST 1290 if (loginsh) 1291 (void) srccat(varval(STRhome), STRsldotlogin); 1292 #endif 1293 if (!fast && (loginsh || rdirs)) 1294 loaddirs(NULL); 1295 } 1296 /* Initing AFTER .cshrc is the Right Way */ 1297 if (intty && !arginp) { /* PWP setup stuff */ 1298 ed_Init(); /* init the new line editor */ 1299 #ifdef SIG_WINDOW 1300 check_window_size(1); /* mung environment */ 1301 #endif /* SIG_WINDOW */ 1302 } 1303 1304 /* 1305 * Now are ready for the -v and -x flags 1306 */ 1307 if (nverbose) 1308 setNS(STRverbose); 1309 if (nexececho) 1310 setNS(STRecho); 1311 1312 /* 1313 * All the rest of the world is inside this call. The argument to process 1314 * indicates whether it should catch "error unwinds". Thus if we are a 1315 * interactive shell our call here will never return by being blown past on 1316 * an error. 1317 */ 1318 process(setintr); 1319 1320 /* 1321 * Mop-up. 1322 */ 1323 if (intty) { 1324 if (loginsh) { 1325 xprintf("logout\n"); 1326 (void) close(SHIN); 1327 child = 1; 1328 #ifdef TESLA 1329 do_logout = 1; 1330 #endif /* TESLA */ 1331 goodbye(NULL, NULL); 1332 } 1333 else { 1334 xprintf("exit\n"); 1335 } 1336 } 1337 record(); 1338 exitstat(); 1339 return (0); 1340 } 1341 1342 void 1343 untty() 1344 { 1345 #ifdef BSDJOBS 1346 if (tpgrp > 0 && opgrp != shpgrp) { 1347 (void) setpgid(0, opgrp); 1348 (void) tcsetpgrp(FSHTTY, opgrp); 1349 (void) resetdisc(FSHTTY); 1350 } 1351 #endif /* BSDJOBS */ 1352 } 1353 1354 void 1355 importpath(cp) 1356 Char *cp; 1357 { 1358 register int i = 0; 1359 register Char *dp; 1360 register Char **pv; 1361 int c; 1362 1363 for (dp = cp; *dp; dp++) 1364 if (*dp == PATHSEP) 1365 i++; 1366 /* 1367 * i+2 where i is the number of colons in the path. There are i+1 1368 * directories in the path plus we need room for a zero terminator. 1369 */ 1370 pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char *)); 1371 dp = cp; 1372 i = 0; 1373 if (*dp) 1374 for (;;) { 1375 if ((c = *dp) == PATHSEP || c == 0) { 1376 *dp = 0; 1377 pv[i++] = Strsave(*cp ? cp : STRdot); 1378 if (c) { 1379 cp = dp + 1; 1380 *dp = PATHSEP; 1381 } 1382 else 1383 break; 1384 } 1385 #ifdef WINNT_NATIVE 1386 else if (*dp == '\\') 1387 *dp = '/'; 1388 #endif /* WINNT_NATIVE */ 1389 dp++; 1390 } 1391 pv[i] = 0; 1392 setq(STRpath, pv, &shvhed, VAR_READWRITE); 1393 } 1394 1395 /* 1396 * Source to the file which is the catenation of the argument names. 1397 */ 1398 static int 1399 srccat(cp, dp) 1400 Char *cp, *dp; 1401 { 1402 if (cp[0] == '/' && cp[1] == '\0') 1403 return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL); 1404 else { 1405 register Char *ep; 1406 char *ptr; 1407 int rv; 1408 1409 #ifdef WINNT_NATIVE 1410 ep = cp; 1411 while(*ep) 1412 ep++; 1413 if (ep[-1] == '/' && dp[0] == '/') /* silly win95 */ 1414 dp++; 1415 #endif /* WINNT_NATIVE */ 1416 1417 ep = Strspl(cp, dp); 1418 ptr = short2str(ep); 1419 1420 rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL); 1421 xfree((ptr_t) ep); 1422 return rv; 1423 } 1424 } 1425 1426 /* 1427 * Source to a file putting the file descriptor in a safe place (> 2). 1428 */ 1429 static int 1430 srcfile(f, onlyown, flag, av) 1431 char *f; 1432 bool onlyown; 1433 int flag; 1434 Char **av; 1435 { 1436 register int unit; 1437 1438 if ((unit = open(f, O_RDONLY)) == -1) 1439 return 0; 1440 unit = dmove(unit, -1); 1441 1442 (void) close_on_exec(unit, 1); 1443 srcunit(unit, onlyown, flag, av); 1444 return 1; 1445 } 1446 1447 1448 /* 1449 * Save the shell state, and establish new argument vector, and new input 1450 * fd. 1451 */ 1452 static void 1453 st_save(st, unit, hflg, al, av) 1454 struct saved_state *st; 1455 int unit, hflg; 1456 Char **al, **av; 1457 { 1458 st->insource = insource; 1459 st->SHIN = SHIN; 1460 st->intty = intty; 1461 st->whyles = whyles; 1462 st->gointr = gointr; 1463 st->arginp = arginp; 1464 st->evalp = evalp; 1465 st->evalvec = evalvec; 1466 st->alvecp = alvecp; 1467 st->alvec = alvec; 1468 st->onelflg = onelflg; 1469 st->enterhist = enterhist; 1470 if (hflg) 1471 st->HIST = HIST; 1472 else 1473 st->HIST = '\0'; 1474 st->cantell = cantell; 1475 cpybin(st->B, B); 1476 1477 /* 1478 * we can now pass arguments to source. 1479 * For compatibility we do that only if arguments were really 1480 * passed, otherwise we keep the old, global $argv like before. 1481 */ 1482 if (av != NULL && *av != NULL) { 1483 struct varent *vp; 1484 if ((vp = adrof(STRargv)) != NULL) 1485 st->argv = saveblk(vp->vec); 1486 else 1487 st->argv = NULL; 1488 setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE); 1489 } 1490 else 1491 st->argv = NULL; 1492 1493 SHIN = unit; /* Do this first */ 1494 1495 /* Establish new input arena */ 1496 { 1497 fbuf = NULL; 1498 fseekp = feobp = fblocks = 0; 1499 settell(); 1500 } 1501 1502 arginp = 0; 1503 onelflg = 0; 1504 intty = isatty(SHIN); 1505 whyles = 0; 1506 gointr = 0; 1507 evalvec = 0; 1508 evalp = 0; 1509 alvec = al; 1510 alvecp = 0; 1511 enterhist = hflg; 1512 if (enterhist) 1513 HIST = '\0'; 1514 insource = 1; 1515 } 1516 1517 1518 /* 1519 * Restore the shell to a saved state 1520 */ 1521 static void 1522 st_restore(st, av) 1523 struct saved_state *st; 1524 Char **av; 1525 { 1526 if (st->SHIN == -1) 1527 return; 1528 1529 /* Reset input arena */ 1530 { 1531 register int i; 1532 register Char** nfbuf = fbuf; 1533 register int nfblocks = fblocks; 1534 1535 fblocks = 0; 1536 fbuf = NULL; 1537 for (i = 0; i < nfblocks; i++) 1538 xfree((ptr_t) nfbuf[i]); 1539 xfree((ptr_t) nfbuf); 1540 } 1541 cpybin(B, st->B); 1542 1543 (void) close(SHIN); 1544 1545 insource = st->insource; 1546 SHIN = st->SHIN; 1547 arginp = st->arginp; 1548 onelflg = st->onelflg; 1549 evalp = st->evalp; 1550 evalvec = st->evalvec; 1551 alvecp = st->alvecp; 1552 alvec = st->alvec; 1553 intty = st->intty; 1554 whyles = st->whyles; 1555 gointr = st->gointr; 1556 if (st->HIST != '\0') 1557 HIST = st->HIST; 1558 enterhist = st->enterhist; 1559 cantell = st->cantell; 1560 1561 if (st->argv != NULL) 1562 setq(STRargv, st->argv, &shvhed, VAR_READWRITE); 1563 else if (av != NULL && *av != NULL && adrof(STRargv) != NULL) 1564 unsetv(STRargv); 1565 } 1566 1567 /* 1568 * Source to a unit. If onlyown it must be our file or our group or 1569 * we don't chance it. This occurs on ".cshrc"s and the like. 1570 */ 1571 static void 1572 srcunit(unit, onlyown, hflg, av) 1573 register int unit; 1574 bool onlyown; 1575 int hflg; 1576 Char **av; 1577 { 1578 struct saved_state st; 1579 st.SHIN = -1; /* st_restore checks this */ 1580 1581 if (unit < 0) 1582 return; 1583 1584 if (didfds) 1585 donefds(); 1586 1587 if (onlyown) { 1588 struct stat stb; 1589 1590 if (fstat(unit, &stb) < 0) { 1591 (void) close(unit); 1592 return; 1593 } 1594 } 1595 1596 getexit(st.oldexit); 1597 1598 if (setintr) 1599 #ifdef BSDSIGS 1600 st.mask = sigblock(sigmask(SIGINT)); 1601 #else 1602 (void) sighold(SIGINT); 1603 #endif 1604 1605 /* Save the current state and move us to a new state */ 1606 st_save(&st, unit, hflg, NULL, av); 1607 1608 /* 1609 * Now if we are allowing commands to be interrupted, we let ourselves be 1610 * interrupted. 1611 */ 1612 if (setintr) 1613 #ifdef BSDSIGS 1614 (void) sigsetmask(st.mask); 1615 #else 1616 (void) sigrelse(SIGINT); 1617 #endif 1618 1619 /* 1620 * Bugfix for running out of memory by: Jak Kirman 1621 * <jak%cs.brown.edu@RELAY.CS.NET>. Solution: pay attention to what 1622 * setexit() is returning because reenter _may_ be in a register, and 1623 * thus restored to 0 on a longjump(). (PWP: insert flames about 1624 * compiler-dependant code here) PWP: THANKS LOTS !!! 1625 * 1626 * PWP: think of this as like a LISP (unwind-protect ...) 1627 * thanks to Diana Smetters for pointing out how this _should_ be written 1628 */ 1629 #ifdef cray 1630 st.reenter = 1; /* assume non-zero return val */ 1631 if (setexit() == 0) { 1632 st.reenter = 0; /* Oh well, we were wrong */ 1633 #else 1634 if ((st.reenter = setexit()) == 0) { 1635 #endif 1636 process(0); /* 0 -> blow away on errors */ 1637 } 1638 1639 if (setintr) 1640 #ifdef BSDSIGS 1641 (void) sigsetmask(st.mask); 1642 #else 1643 (void) sigrelse(SIGINT); 1644 #endif 1645 1646 /* Restore the old state */ 1647 st_restore(&st, av); 1648 resexit(st.oldexit); 1649 /* 1650 * If process reset() (effectively an unwind) then we must also unwind. 1651 */ 1652 if (st.reenter) 1653 stderror(ERR_SILENT); 1654 } 1655 1656 1657 /*ARGSUSED*/ 1658 void 1659 goodbye(v, c) 1660 Char **v; 1661 struct command *c; 1662 { 1663 USE(c); 1664 record(); 1665 1666 if (loginsh) { 1667 (void) sigset(SIGQUIT, SIG_IGN); 1668 (void) sigset(SIGINT, SIG_IGN); 1669 (void) sigset(SIGTERM, SIG_IGN); 1670 (void) sigset(SIGHUP, SIG_IGN); 1671 setintr = 0; /* No interrupts after "logout" */ 1672 /* Trap errors inside .logout */ 1673 reenter = setexit(); 1674 if (reenter != 0) 1675 exitstat(); 1676 if (!(adrof(STRlogout))) 1677 set(STRlogout, Strsave(STRnormal), VAR_READWRITE); 1678 #ifdef _PATH_DOTLOGOUT 1679 (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL); 1680 #endif 1681 if (adrof(STRhome)) 1682 (void) srccat(varval(STRhome), STRsldtlogout); 1683 #ifdef TESLA 1684 do_logout = 1; 1685 #endif /* TESLA */ 1686 } 1687 exitstat(); 1688 } 1689 1690 void 1691 exitstat() 1692 { 1693 #ifdef PROF 1694 monitor(0); 1695 #endif 1696 /* 1697 * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit 1698 * directly because we poke child here. Otherwise we might continue 1699 * unwarrantedly (sic). 1700 */ 1701 child = 1; 1702 1703 xexit(getn(varval(STRstatus))); 1704 } 1705 1706 /* 1707 * in the event of a HUP we want to save the history 1708 */ 1709 static sigret_t 1710 phup(snum) 1711 int snum; 1712 { 1713 /* 1714 * There is no return from here, 1715 * so we are not going to release SIGHUP 1716 * anymore 1717 */ 1718 #ifdef UNRELSIGS 1719 if (snum) 1720 (void) sigset(snum, SIG_IGN); 1721 #else 1722 # ifdef BSDSIGS 1723 (void) sigblock(sigmask(SIGHUP)); 1724 # else 1725 (void) sighold(SIGHUP); 1726 # endif /* BSDSIGS */ 1727 #endif /* UNRELSIGS */ 1728 1729 if (loginsh) { 1730 set(STRlogout, Strsave(STRhangup), VAR_READWRITE); 1731 #ifdef _PATH_DOTLOGOUT 1732 (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL); 1733 #endif 1734 if (adrof(STRhome)) 1735 (void) srccat(varval(STRhome), STRsldtlogout); 1736 } 1737 1738 record(); 1739 1740 #ifdef POSIXJOBS 1741 /* 1742 * We kill the last foreground process group. It then becomes 1743 * responsible to propagate the SIGHUP to its progeny. 1744 */ 1745 { 1746 struct process *pp, *np; 1747 1748 for (pp = proclist.p_next; pp; pp = pp->p_next) { 1749 np = pp; 1750 /* 1751 * Find if this job is in the foreground. It could be that 1752 * the process leader has exited and the foreground flag 1753 * is cleared for it. 1754 */ 1755 do 1756 /* 1757 * If a process is in the foreground we try to kill 1758 * it's process group. If we succeed, then the 1759 * whole job is gone. Otherwise we keep going... 1760 * But avoid sending HUP to the shell again. 1761 */ 1762 if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) { 1763 np->p_flags &= ~PHUP; 1764 if (killpg(np->p_jobid, SIGHUP) != -1) { 1765 /* In case the job was suspended... */ 1766 #ifdef SIGCONT 1767 (void) killpg(np->p_jobid, SIGCONT); 1768 #endif 1769 break; 1770 } 1771 } 1772 while ((np = np->p_friends) != pp); 1773 } 1774 } 1775 #endif /* POSIXJOBS */ 1776 1777 xexit(snum); 1778 #ifndef SIGVOID 1779 return (snum); 1780 #endif 1781 } 1782 1783 static Char *jobargv[2] = {STRjobs, 0}; 1784 1785 /* 1786 * Catch an interrupt, e.g. during lexical input. 1787 * If we are an interactive shell, we reset the interrupt catch 1788 * immediately. In any case we drain the shell output, 1789 * and finally go through the normal error mechanism, which 1790 * gets a chance to make the shell go away. 1791 */ 1792 int just_signaled; /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */ 1793 1794 #ifdef SIGVOID 1795 /*ARGSUSED*/ 1796 #endif 1797 sigret_t 1798 pintr(snum) 1799 int snum; 1800 { 1801 #ifdef UNRELSIGS 1802 if (snum) 1803 (void) sigset(snum, pintr); 1804 #endif /* UNRELSIGS */ 1805 just_signaled = 1; 1806 pintr1(1); 1807 #ifndef SIGVOID 1808 return (snum); 1809 #endif 1810 } 1811 1812 void 1813 pintr1(wantnl) 1814 bool wantnl; 1815 { 1816 register Char **v; 1817 #ifdef BSDSIGS 1818 sigmask_t omask; 1819 #endif 1820 1821 #ifdef BSDSIGS 1822 omask = sigblock((sigmask_t) 0); 1823 #endif 1824 if (setintr) { 1825 #ifdef BSDSIGS 1826 (void) sigsetmask(omask & ~sigmask(SIGINT)); 1827 #else 1828 (void) sigrelse(SIGINT); 1829 #endif 1830 if (pjobs) { 1831 pjobs = 0; 1832 xputchar('\n'); 1833 dojobs(jobargv, NULL); 1834 stderror(ERR_NAME | ERR_INTR); 1835 } 1836 } 1837 /* MH - handle interrupted completions specially */ 1838 { 1839 extern int InsideCompletion; 1840 1841 if (InsideCompletion) 1842 stderror(ERR_SILENT); 1843 } 1844 /* JV - Make sure we shut off inputl */ 1845 { 1846 extern Char GettingInput; 1847 1848 (void) Cookedmode(); 1849 GettingInput = 0; 1850 } 1851 #ifdef BSDSIGS 1852 (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 1853 #else 1854 if (setintr) 1855 (void) sighold(SIGINT); 1856 (void) sigrelse(SIGCHLD); 1857 #endif 1858 drainoline(); 1859 #if !defined(_VMS_POSIX) && !defined(WINNT_NATIVE) 1860 (void) endpwent(); 1861 #endif /* !_VMS_POSIX && !WINNT_NATIVE */ 1862 1863 /* 1864 * If we have an active "onintr" then we search for the label. Note that if 1865 * one does "onintr -" then we shan't be interruptible so we needn't worry 1866 * about that here. 1867 */ 1868 if (gointr) { 1869 gotolab(gointr); 1870 timflg = 0; 1871 if ((v = pargv) != 0) 1872 pargv = 0, blkfree(v); 1873 if ((v = gargv) != 0) 1874 gargv = 0, blkfree(v); 1875 reset(); 1876 } 1877 else if (intty && wantnl) { 1878 if (editing) { 1879 /* 1880 * If we are editing a multi-line input command, and move to 1881 * the beginning of the line, we don't want to trash it when 1882 * we hit ^C 1883 */ 1884 PastBottom(); 1885 ClearLines(); 1886 ClearDisp(); 1887 } 1888 else { 1889 /* xputchar('\n'); *//* Some like this, others don't */ 1890 (void) putraw('\r'); 1891 (void) putraw('\n'); 1892 } 1893 } 1894 stderror(ERR_SILENT); 1895 } 1896 1897 /* 1898 * Process is the main driving routine for the shell. 1899 * It runs all command processing, except for those within { ... } 1900 * in expressions (which is run by a routine evalav in sh.exp.c which 1901 * is a stripped down process), and `...` evaluation which is run 1902 * also by a subset of this code in sh.glob.c in the routine backeval. 1903 * 1904 * The code here is a little strange because part of it is interruptible 1905 * and hence freeing of structures appears to occur when none is necessary 1906 * if this is ignored. 1907 * 1908 * Note that if catch is not set then we will unwind on any error. 1909 * If an end-of-file occurs, we return. 1910 */ 1911 static struct command *savet = NULL; 1912 void 1913 process(catch) 1914 bool catch; 1915 { 1916 extern char Expand; 1917 jmp_buf_t osetexit; 1918 /* PWP: This might get nuked my longjmp so don't make it a register var */ 1919 struct command *t = savet; 1920 1921 savet = NULL; 1922 getexit(osetexit); 1923 for (;;) { 1924 1925 pendjob(); 1926 1927 /* This was leaking memory badly, particularly when sourcing 1928 * files, etc.. For whatever reason we were arriving here with 1929 * allocated pointers still active, and the code was simply 1930 * overwriting them. I can't say I fully understand the 1931 * control flow here, but according to Purify this particular 1932 * leak has been plugged, and I haven't noticed any ill 1933 * effects.. (sg) 1934 */ 1935 if (paraml.next && paraml.next != ¶ml) 1936 freelex(¶ml); 1937 1938 paraml.next = paraml.prev = ¶ml; 1939 paraml.word = STRNULL; 1940 (void) setexit(); 1941 justpr = enterhist; /* execute if not entering history */ 1942 1943 /* 1944 * Interruptible during interactive reads 1945 */ 1946 if (setintr) 1947 #ifdef BSDSIGS 1948 (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT)); 1949 #else 1950 (void) sigrelse(SIGINT); 1951 #endif 1952 1953 1954 /* 1955 * For the sake of reset() 1956 */ 1957 freelex(¶ml); 1958 if (savet) 1959 freesyn(savet), savet = NULL; 1960 1961 if (haderr) { 1962 if (!catch) { 1963 /* unwind */ 1964 doneinp = 0; 1965 savet = t; 1966 resexit(osetexit); 1967 reset(); 1968 } 1969 haderr = 0; 1970 /* 1971 * Every error is eventually caught here or the shell dies. It is 1972 * at this point that we clean up any left-over open files, by 1973 * closing all but a fixed number of pre-defined files. Thus 1974 * routines don't have to worry about leaving files open due to 1975 * deeper errors... they will get closed here. 1976 */ 1977 closem(); 1978 continue; 1979 } 1980 if (doneinp) { 1981 doneinp = 0; 1982 break; 1983 } 1984 if (chkstop) 1985 chkstop--; 1986 if (neednote) 1987 pnote(); 1988 if (intty && prompt && evalvec == 0) { 1989 just_signaled = 0; 1990 mailchk(); 1991 /* 1992 * Watch for logins/logouts. Next is scheduled commands stored 1993 * previously using "sched." Then execute periodic commands. 1994 * Following that, the prompt precmd is run. 1995 */ 1996 #ifndef HAVENOUTMP 1997 watch_login(0); 1998 #endif /* !HAVENOUTMP */ 1999 sched_run(0); 2000 period_cmd(); 2001 precmd(); 2002 /* 2003 * If we are at the end of the input buffer then we are going to 2004 * read fresh stuff. Otherwise, we are rereading input and don't 2005 * need or want to prompt. 2006 */ 2007 if (fseekp == feobp && aret == TCSH_F_SEEK) 2008 printprompt(0, NULL); 2009 flush(); 2010 setalarm(1); 2011 } 2012 if (seterr) { 2013 xfree((ptr_t) seterr); 2014 seterr = NULL; 2015 } 2016 2017 /* 2018 * Echo not only on VERBOSE, but also with history expansion. If there 2019 * is a lexical error then we forego history echo. 2020 */ 2021 if ((lex(¶ml) && !seterr && intty && !tellwhat && !Expand && 2022 !whyles) || adrof(STRverbose)) { 2023 haderr = 1; 2024 prlex(¶ml); 2025 haderr = 0; 2026 } 2027 (void) alarm(0); /* Autologout OFF */ 2028 2029 /* 2030 * The parser may lose space if interrupted. 2031 */ 2032 if (setintr) 2033 #ifdef BSDSIGS 2034 (void) sigblock(sigmask(SIGINT)); 2035 #else 2036 (void) sighold(SIGINT); 2037 #endif 2038 2039 /* 2040 * Save input text on the history list if reading in old history, or it 2041 * is from the terminal at the top level and not in a loop. 2042 * 2043 * PWP: entry of items in the history list while in a while loop is done 2044 * elsewhere... 2045 */ 2046 if (enterhist || (catch && intty && !whyles && !tellwhat && !arun)) 2047 savehist(¶ml, enterhist > 1); 2048 2049 if (Expand && seterr) 2050 Expand = 0; 2051 2052 /* 2053 * Print lexical error messages, except when sourcing history lists. 2054 */ 2055 if (!enterhist && seterr) 2056 stderror(ERR_OLD); 2057 2058 /* 2059 * If had a history command :p modifier then this is as far as we 2060 * should go 2061 */ 2062 if (justpr) 2063 reset(); 2064 2065 /* 2066 * If had a tellwhat from twenex() then do 2067 */ 2068 if (tellwhat) { 2069 (void) tellmewhat(¶ml, NULL); 2070 reset(); 2071 } 2072 2073 alias(¶ml); 2074 2075 #ifdef BSDJOBS 2076 /* 2077 * If we are interactive, try to continue jobs that we have stopped 2078 */ 2079 if (prompt) 2080 continue_jobs(¶ml); 2081 #endif /* BSDJOBS */ 2082 2083 /* 2084 * Check to see if the user typed "rm * .o" or something 2085 */ 2086 if (prompt) 2087 rmstar(¶ml); 2088 /* 2089 * Parse the words of the input into a parse tree. 2090 */ 2091 savet = syntax(paraml.next, ¶ml, 0); 2092 if (seterr) 2093 stderror(ERR_OLD); 2094 2095 postcmd(); 2096 /* 2097 * Execute the parse tree From: Michael Schroeder 2098 * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp); 2099 */ 2100 execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); 2101 2102 /* 2103 * Made it! 2104 */ 2105 freelex(¶ml); 2106 freesyn(savet), savet = NULL; 2107 #ifdef SIG_WINDOW 2108 if (catch && intty && !whyles && !tellwhat) 2109 (void) window_change(0); /* for window systems */ 2110 #endif /* SIG_WINDOW */ 2111 set(STR_, Strsave(InputBuf), VAR_READWRITE | VAR_NOGLOB); 2112 } 2113 savet = t; 2114 resexit(osetexit); 2115 } 2116 2117 /*ARGSUSED*/ 2118 void 2119 dosource(t, c) 2120 register Char **t; 2121 struct command *c; 2122 { 2123 register Char *f; 2124 bool hflg = 0; 2125 extern int bequiet; 2126 char buf[BUFSIZE]; 2127 2128 USE(c); 2129 t++; 2130 if (*t && eq(*t, STRmh)) { 2131 if (*++t == NULL) 2132 stderror(ERR_NAME | ERR_HFLAG); 2133 hflg++; 2134 } 2135 else if (*t && eq(*t, STRmm)) { 2136 if (*++t == NULL) 2137 stderror(ERR_NAME | ERR_MFLAG); 2138 hflg = 2; 2139 } 2140 2141 f = globone(*t++, G_ERROR); 2142 (void) strcpy(buf, short2str(f)); 2143 xfree((ptr_t) f); 2144 if ((!srcfile(buf, 0, hflg, t)) && (!hflg) && (!bequiet)) 2145 stderror(ERR_SYSTEM, buf, strerror(errno)); 2146 } 2147 2148 /* 2149 * Check for mail. 2150 * If we are a login shell, then we don't want to tell 2151 * about any mail file unless its been modified 2152 * after the time we started. 2153 * This prevents us from telling the user things he already 2154 * knows, since the login program insists on saying 2155 * "You have mail." 2156 */ 2157 2158 /* 2159 * The AMS version. 2160 * This version checks if the file is a directory, and if so, 2161 * tells you the number of files in it, otherwise do the old thang. 2162 * The magic "+1" in the time calculation is to compensate for 2163 * an AFS bug where directory mtimes are set to 1 second in 2164 * the future. 2165 */ 2166 2167 static void 2168 mailchk() 2169 { 2170 register struct varent *v; 2171 register Char **vp; 2172 time_t t; 2173 int intvl, cnt; 2174 struct stat stb; 2175 bool new; 2176 2177 v = adrof(STRmail); 2178 if (v == 0) 2179 return; 2180 (void) time(&t); 2181 vp = v->vec; 2182 cnt = blklen(vp); 2183 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 2184 if (intvl < 1) 2185 intvl = 1; 2186 if (chktim + intvl > t) 2187 return; 2188 for (; *vp; vp++) { 2189 char *filename = short2str(*vp); 2190 char *mboxdir = filename; 2191 2192 if (stat(filename, &stb) < 0) 2193 continue; 2194 #if defined(BSDTIMES) || defined(_SEQUENT_) 2195 new = stb.st_mtime > time0.tv_sec; 2196 #else 2197 new = stb.st_mtime > seconds0; 2198 #endif 2199 if (S_ISDIR(stb.st_mode)) { 2200 DIR *mailbox; 2201 int mailcount = 0; 2202 char tempfilename[MAXPATHLEN]; 2203 struct stat stc; 2204 2205 xsnprintf(tempfilename, MAXPATHLEN, "%s/new", filename); 2206 2207 if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) { 2208 /* 2209 * "filename/new" exists and is a directory; you are 2210 * using Qmail. 2211 */ 2212 stb = stc; 2213 #if defined(BSDTIMES) || defined(_SEQUENT_) 2214 new = stb.st_mtime > time0.tv_sec; 2215 #else 2216 new = stb.st_mtime > seconds0; 2217 #endif 2218 mboxdir = tempfilename; 2219 } 2220 2221 if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) 2222 continue; 2223 2224 if ((mailbox = opendir(mboxdir)) == NULL) 2225 continue; 2226 2227 /* skip . and .. */ 2228 if (!readdir(mailbox) || !readdir(mailbox)) 2229 continue; 2230 2231 while (readdir(mailbox)) 2232 mailcount++; 2233 2234 if (mailcount == 0) 2235 continue; 2236 2237 if (cnt == 1) 2238 xprintf(CGETS(11, 3, "You have %d mail messages.\n"), 2239 mailcount); 2240 else 2241 xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"), 2242 mailcount, filename); 2243 } 2244 else { 2245 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 2246 (stb.st_atime <= chktim && stb.st_mtime <= chktim) || 2247 (loginsh && !new)) 2248 continue; 2249 if (cnt == 1) 2250 xprintf(CGETS(11, 5, "You have %smail.\n"), 2251 new ? CGETS(11, 6, "new ") : ""); 2252 else 2253 xprintf(CGETS(11, 7, "You have %smail in %s.\n"), 2254 new ? CGETS(11, 6, "new ") : "", filename); 2255 } 2256 } 2257 chktim = t; 2258 } 2259 2260 /* 2261 * Extract a home directory from the password file 2262 * The argument points to a buffer where the name of the 2263 * user whose home directory is sought is currently. 2264 * We write the home directory of the user back there. 2265 */ 2266 int 2267 gethdir(home) 2268 Char *home; 2269 { 2270 Char *h; 2271 2272 /* 2273 * Is it us? 2274 */ 2275 if (*home == '\0') { 2276 if ((h = varval(STRhome)) != STRNULL) { 2277 (void) Strcpy(home, h); 2278 return 0; 2279 } 2280 else 2281 return 1; 2282 } 2283 2284 /* 2285 * Look in the cache 2286 */ 2287 if ((h = gettilde(home)) == NULL) 2288 return 1; 2289 else { 2290 (void) Strcpy(home, h); 2291 return 0; 2292 } 2293 } 2294 2295 /* 2296 * Move the initial descriptors to their eventual 2297 * resting places, closing all other units. 2298 */ 2299 void 2300 initdesc() 2301 { 2302 #ifdef NLS_BUGS 2303 #ifdef NLS_CATALOGS 2304 (void)catclose(catd); 2305 #endif /* NLS_CATALOGS */ 2306 #endif /* NLS_BUGS */ 2307 2308 2309 didfds = 0; /* 0, 1, 2 aren't set up */ 2310 (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1); 2311 (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1); 2312 (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1); 2313 (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1); 2314 #ifndef CLOSE_ON_EXEC 2315 didcch = 0; /* Havent closed for child */ 2316 #endif /* CLOSE_ON_EXEC */ 2317 isdiagatty = isatty(SHDIAG); 2318 isoutatty = isatty(SHOUT); 2319 closem(); 2320 #ifdef NLS_BUGS 2321 #ifdef NLS_CATALOGS 2322 nlsinit(); 2323 #endif /* NLS_CATALOGS */ 2324 #endif /* NLS_BUGS */ 2325 } 2326 2327 2328 void 2329 #ifdef PROF 2330 done(i) 2331 #else 2332 xexit(i) 2333 #endif 2334 int i; 2335 { 2336 #ifdef TESLA 2337 if (loginsh && do_logout) { 2338 /* this is to send hangup signal to the develcon */ 2339 /* we toggle DTR. clear dtr - sleep 1 - set dtr */ 2340 /* ioctl will return ENOTTY for pty's but we ignore it */ 2341 /* exitstat will run after disconnect */ 2342 /* we sleep for 2 seconds to let things happen in */ 2343 /* .logout and rechist() */ 2344 #ifdef TIOCCDTR 2345 (void) sleep(2); 2346 (void) ioctl(FSHTTY, TIOCCDTR, NULL); 2347 (void) sleep(1); 2348 (void) ioctl(FSHTTY, TIOCSDTR, NULL); 2349 #endif /* TIOCCDTR */ 2350 } 2351 #endif /* TESLA */ 2352 2353 { 2354 struct process *pp, *np; 2355 2356 /* Kill all processes marked for hup'ing */ 2357 for (pp = proclist.p_next; pp; pp = pp->p_next) { 2358 np = pp; 2359 do 2360 if ((np->p_flags & PHUP) && np->p_jobid != shpgrp) { 2361 if (killpg(np->p_jobid, SIGHUP) != -1) { 2362 /* In case the job was suspended... */ 2363 #ifdef SIGCONT 2364 (void) killpg(np->p_jobid, SIGCONT); 2365 #endif 2366 break; 2367 } 2368 } 2369 while ((np = np->p_friends) != pp); 2370 } 2371 } 2372 untty(); 2373 #ifdef NLS_CATALOGS 2374 /* 2375 * We need to call catclose, because SVR4 leaves symlinks behind otherwise 2376 * in the catalog directories. We cannot close on a vforked() child, 2377 * because messages will stop working on the parent too. 2378 */ 2379 if (child == 0) 2380 (void) catclose(catd); 2381 #endif /* NLS_CATALOGS */ 2382 #ifdef WINNT_NATIVE 2383 nt_cleanup(); 2384 #endif /* WINNT_NATIVE */ 2385 _exit(i); 2386 } 2387 2388 #ifndef _PATH_DEFPATH 2389 static Char ** 2390 defaultpath() 2391 { 2392 char *ptr; 2393 Char **blk, **blkp; 2394 struct stat stb; 2395 2396 blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); 2397 2398 #ifndef NODOT 2399 # ifndef DOTLAST 2400 *blkp++ = Strsave(STRdot); 2401 # endif 2402 #endif 2403 2404 #define DIRAPPEND(a) \ 2405 if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \ 2406 *blkp++ = SAVE(ptr) 2407 2408 #ifdef _PATH_LOCAL 2409 DIRAPPEND(_PATH_LOCAL); 2410 #endif 2411 2412 #ifdef _PATH_USRUCB 2413 DIRAPPEND(_PATH_USRUCB); 2414 #endif 2415 2416 #ifdef _PATH_USRBSD 2417 DIRAPPEND(_PATH_USRBSD); 2418 #endif 2419 2420 #ifdef _PATH_BIN 2421 DIRAPPEND(_PATH_BIN); 2422 #endif 2423 2424 #ifdef _PATH_USRBIN 2425 DIRAPPEND(_PATH_USRBIN); 2426 #endif 2427 2428 #undef DIRAPPEND 2429 2430 #ifndef NODOT 2431 # ifdef DOTLAST 2432 *blkp++ = Strsave(STRdot); 2433 # endif 2434 #endif 2435 *blkp = NULL; 2436 return (blk); 2437 } 2438 #endif 2439 2440 static void 2441 record() 2442 { 2443 if (!fast) { 2444 recdirs(NULL, adrof(STRsavedirs) != NULL); 2445 rechist(NULL, adrof(STRsavehist) != NULL); 2446 } 2447 } 2448