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