1*ee9e50eaSmycroft /* $NetBSD: csh.c,v 1.24 1998/07/28 11:41:41 mycroft Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 4cee2bad8Smycroft * Copyright (c) 1980, 1991, 1993 5cee2bad8Smycroft * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * Redistribution and use in source and binary forms, with or without 861f28255Scgd * modification, are permitted provided that the following conditions 961f28255Scgd * are met: 1061f28255Scgd * 1. Redistributions of source code must retain the above copyright 1161f28255Scgd * notice, this list of conditions and the following disclaimer. 1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1361f28255Scgd * notice, this list of conditions and the following disclaimer in the 1461f28255Scgd * documentation and/or other materials provided with the distribution. 1561f28255Scgd * 3. All advertising materials mentioning features or use of this software 1661f28255Scgd * must display the following acknowledgement: 1761f28255Scgd * This product includes software developed by the University of 1861f28255Scgd * California, Berkeley and its contributors. 1961f28255Scgd * 4. Neither the name of the University nor the names of its contributors 2061f28255Scgd * may be used to endorse or promote products derived from this software 2161f28255Scgd * without specific prior written permission. 2261f28255Scgd * 2361f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2461f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2561f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2661f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2761f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2861f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2961f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3061f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3161f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3261f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3361f28255Scgd * SUCH DAMAGE. 3461f28255Scgd */ 3561f28255Scgd 368ea378c6Schristos #include <sys/cdefs.h> 3761f28255Scgd #ifndef lint 388ea378c6Schristos __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993\n\ 398ea378c6Schristos The Regents of the University of California. All rights reserved.\n"); 4061f28255Scgd #endif /* not lint */ 4161f28255Scgd 4261f28255Scgd #ifndef lint 4349f0ad86Scgd #if 0 4449f0ad86Scgd static char sccsid[] = "@(#)csh.c 8.2 (Berkeley) 10/12/93"; 4549f0ad86Scgd #else 46*ee9e50eaSmycroft __RCSID("$NetBSD: csh.c,v 1.24 1998/07/28 11:41:41 mycroft Exp $"); 4749f0ad86Scgd #endif 4861f28255Scgd #endif /* not lint */ 4961f28255Scgd 5061f28255Scgd #include <sys/types.h> 5161f28255Scgd #include <sys/ioctl.h> 5261f28255Scgd #include <sys/stat.h> 5361f28255Scgd #include <fcntl.h> 5461f28255Scgd #include <errno.h> 5561f28255Scgd #include <pwd.h> 5661f28255Scgd #include <stdlib.h> 5761f28255Scgd #include <string.h> 5867e49b4bSkleink #include <time.h> 5961f28255Scgd #include <locale.h> 6061f28255Scgd #include <unistd.h> 61cee2bad8Smycroft #include <vis.h> 620f668275Sfair #include <paths.h> /* should this be included in pathnames.h instead? */ 6361f28255Scgd #if __STDC__ 6461f28255Scgd # include <stdarg.h> 6561f28255Scgd #else 6661f28255Scgd # include <varargs.h> 6761f28255Scgd #endif 6861f28255Scgd 6961f28255Scgd #include "csh.h" 70cee2bad8Smycroft #include "proc.h" 7161f28255Scgd #include "extern.h" 7261f28255Scgd #include "pathnames.h" 7361f28255Scgd 7461f28255Scgd /* 7561f28255Scgd * C Shell 7661f28255Scgd * 7761f28255Scgd * Bill Joy, UC Berkeley, California, USA 7861f28255Scgd * October 1978, May 1980 7961f28255Scgd * 8061f28255Scgd * Jim Kulp, IIASA, Laxenburg, Austria 8161f28255Scgd * April 1980 8261f28255Scgd * 8361f28255Scgd * Christos Zoulas, Cornell University 8461f28255Scgd * June, 1991 8561f28255Scgd */ 8661f28255Scgd 8761f28255Scgd Char *dumphist[] = {STRhistory, STRmh, 0, 0}; 88cee2bad8Smycroft Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0}; 8961f28255Scgd 9061f28255Scgd int nofile = 0; 9161f28255Scgd bool reenter = 0; 9261f28255Scgd bool nverbose = 0; 9361f28255Scgd bool nexececho = 0; 9461f28255Scgd bool quitit = 0; 9561f28255Scgd bool fast = 0; 9661f28255Scgd bool batch = 0; 9761f28255Scgd bool mflag = 0; 9861f28255Scgd bool prompt = 1; 9961f28255Scgd bool enterhist = 0; 10061f28255Scgd 10161f28255Scgd extern char **environ; 10261f28255Scgd 103cee2bad8Smycroft static int readf __P((void *, char *, int)); 104cee2bad8Smycroft static fpos_t seekf __P((void *, fpos_t, int)); 105cee2bad8Smycroft static int writef __P((void *, const char *, int)); 106cee2bad8Smycroft static int closef __P((void *)); 10761f28255Scgd static int srccat __P((Char *, Char *)); 10861f28255Scgd static int srcfile __P((char *, bool, bool)); 10961f28255Scgd static void phup __P((int)); 11061f28255Scgd static void srcunit __P((int, bool, bool)); 11161f28255Scgd static void mailchk __P((void)); 1120f668275Sfair #ifndef _PATH_DEFPATH 11361f28255Scgd static Char **defaultpath __P((void)); 1140f668275Sfair #endif 11561f28255Scgd 1168ea378c6Schristos int main __P((int, char **)); 1178ea378c6Schristos 11861f28255Scgd int 11961f28255Scgd main(argc, argv) 12061f28255Scgd int argc; 12161f28255Scgd char **argv; 12261f28255Scgd { 12376adbe2bStls Char *cp; 12476adbe2bStls char *tcp; 1254d669802Smycroft const char *ecp; 12676adbe2bStls int f; 12776adbe2bStls char **tempv; 1287b38403cSmycroft struct sigaction oact; 1297b38403cSmycroft sigset_t sigset; 13061f28255Scgd 131cee2bad8Smycroft cshin = stdin; 132cee2bad8Smycroft cshout = stdout; 133cee2bad8Smycroft csherr = stderr; 13461f28255Scgd 13561f28255Scgd settimes(); /* Immed. estab. timing base */ 13661f28255Scgd 13761f28255Scgd /* 13861f28255Scgd * Initialize non constant strings 13961f28255Scgd */ 14061f28255Scgd #ifdef _PATH_BSHELL 14161f28255Scgd STR_BSHELL = SAVE(_PATH_BSHELL); 14261f28255Scgd #endif 14361f28255Scgd #ifdef _PATH_CSHELL 14461f28255Scgd STR_SHELLPATH = SAVE(_PATH_CSHELL); 14561f28255Scgd #endif 14661f28255Scgd STR_environ = blk2short(environ); 14761f28255Scgd environ = short2blk(STR_environ); /* So that we can free it */ 14861f28255Scgd STR_WORD_CHARS = SAVE(WORD_CHARS); 14961f28255Scgd 15061f28255Scgd HIST = '!'; 15161f28255Scgd HISTSUB = '^'; 15261f28255Scgd word_chars = STR_WORD_CHARS; 15361f28255Scgd 15461f28255Scgd tempv = argv; 15561f28255Scgd if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ 15661f28255Scgd quitit = 1; 15761f28255Scgd uid = getuid(); 15861f28255Scgd gid = getgid(); 159cee2bad8Smycroft euid = geteuid(); 160cee2bad8Smycroft egid = getegid(); 16161f28255Scgd /* 16261f28255Scgd * We are a login shell if: 1. we were invoked as -<something> and we had 16361f28255Scgd * no arguments 2. or we were invoked only with the -l flag 16461f28255Scgd */ 16561f28255Scgd loginsh = (**tempv == '-' && argc == 1) || 16661f28255Scgd (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && 16761f28255Scgd tempv[1][2] == '\0'); 16861f28255Scgd 16961f28255Scgd if (loginsh && **tempv != '-') { 17061f28255Scgd /* 17161f28255Scgd * Mangle the argv space 17261f28255Scgd */ 17361f28255Scgd tempv[1][0] = '\0'; 17461f28255Scgd tempv[1][1] = '\0'; 17561f28255Scgd tempv[1] = NULL; 176cee2bad8Smycroft for (tcp = *tempv; *tcp++;) 177cee2bad8Smycroft continue; 17861f28255Scgd for (tcp--; tcp >= *tempv; tcp--) 17961f28255Scgd tcp[1] = tcp[0]; 18061f28255Scgd *++tcp = '-'; 18161f28255Scgd argc--; 18261f28255Scgd } 18361f28255Scgd if (loginsh) 18461f28255Scgd (void) time(&chktim); 18561f28255Scgd 18661f28255Scgd AsciiOnly = 1; 18761f28255Scgd #ifdef NLS 18861f28255Scgd (void) setlocale(LC_ALL, ""); 18961f28255Scgd { 19061f28255Scgd int k; 19161f28255Scgd 192cee2bad8Smycroft for (k = 0200; k <= 0377 && !Isprint(k); k++) 193cee2bad8Smycroft continue; 19461f28255Scgd AsciiOnly = k > 0377; 19561f28255Scgd } 19661f28255Scgd #else 19761f28255Scgd AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; 19861f28255Scgd #endif /* NLS */ 19961f28255Scgd 20061f28255Scgd /* 20161f28255Scgd * Move the descriptors to safe places. The variable didfds is 0 while we 20261f28255Scgd * have only FSH* to work with. When didfds is true, we have 0,1,2 and 20361f28255Scgd * prefer to use these. 20461f28255Scgd */ 20561f28255Scgd initdesc(); 206cee2bad8Smycroft /* 207cee2bad8Smycroft * XXX: This is to keep programs that use stdio happy. 208cee2bad8Smycroft * what we really want is freunopen() .... 209cee2bad8Smycroft * Closing cshin cshout and csherr (which are really stdin stdout 210cee2bad8Smycroft * and stderr at this point and then reopening them in the same order 211cee2bad8Smycroft * gives us again stdin == cshin stdout == cshout and stderr == csherr. 212cee2bad8Smycroft * If that was not the case builtins like printf that use stdio 213cee2bad8Smycroft * would break. But in any case we could fix that with memcpy and 214cee2bad8Smycroft * a bit of pointer manipulation... 215cee2bad8Smycroft * Fortunately this is not needed under the current implementation 216cee2bad8Smycroft * of stdio. 217cee2bad8Smycroft */ 218cee2bad8Smycroft (void) fclose(cshin); 219cee2bad8Smycroft (void) fclose(cshout); 220cee2bad8Smycroft (void) fclose(csherr); 221*ee9e50eaSmycroft if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef))) 222cee2bad8Smycroft exit(1); 223*ee9e50eaSmycroft if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef))) 224cee2bad8Smycroft exit(1); 225*ee9e50eaSmycroft if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef))) 226cee2bad8Smycroft exit(1); 227cee2bad8Smycroft (void) setvbuf(cshin, NULL, _IOLBF, 0); 228cee2bad8Smycroft (void) setvbuf(cshout, NULL, _IOLBF, 0); 229cee2bad8Smycroft (void) setvbuf(csherr, NULL, _IOLBF, 0); 23061f28255Scgd 23161f28255Scgd /* 23261f28255Scgd * Initialize the shell variables. ARGV and PROMPT are initialized later. 23361f28255Scgd * STATUS is also munged in several places. CHILD is munged when 23461f28255Scgd * forking/waiting 23561f28255Scgd */ 23661f28255Scgd set(STRstatus, Strsave(STR0)); 23761f28255Scgd 2384d669802Smycroft if ((ecp = getenv("HOME")) != NULL) 2394d669802Smycroft cp = quote(SAVE(ecp)); 24061f28255Scgd else 24161f28255Scgd cp = NULL; 24261f28255Scgd 24361f28255Scgd if (cp == NULL) 24461f28255Scgd fast = 1; /* No home -> can't read scripts */ 24561f28255Scgd else 24661f28255Scgd set(STRhome, cp); 24761f28255Scgd dinit(cp); /* dinit thinks that HOME == cwd in a login 24861f28255Scgd * shell */ 24961f28255Scgd /* 25061f28255Scgd * Grab other useful things from the environment. Should we grab 25161f28255Scgd * everything?? 25261f28255Scgd */ 2534d669802Smycroft if ((ecp = getenv("LOGNAME")) != NULL || 2544d669802Smycroft (ecp = getenv("USER")) != NULL) 2554d669802Smycroft set(STRuser, quote(SAVE(ecp))); 2564d669802Smycroft if ((ecp = getenv("TERM")) != NULL) 2574d669802Smycroft set(STRterm, quote(SAVE(ecp))); 25861f28255Scgd 25961f28255Scgd /* 26061f28255Scgd * Re-initialize path if set in environment 26161f28255Scgd */ 2624d669802Smycroft if ((ecp = getenv("PATH")) == NULL) { 2630f668275Sfair #ifdef _PATH_DEFPATH 2640f668275Sfair importpath(SAVE(_PATH_DEFPATH)); 2650f668275Sfair #else 2668af89705Schristos setq(STRpath, defaultpath(), &shvhed); 2670f668275Sfair #endif 2680f668275Sfair } else { 2694d669802Smycroft importpath(SAVE(ecp)); 2700f668275Sfair } 27161f28255Scgd 27261f28255Scgd set(STRshell, Strsave(STR_SHELLPATH)); 27361f28255Scgd 27461f28255Scgd doldol = putn((int) getpid()); /* For $$ */ 27561f28255Scgd shtemp = Strspl(STRtmpsh, doldol); /* For << */ 27661f28255Scgd 27761f28255Scgd /* 27861f28255Scgd * Record the interrupt states from the parent process. If the parent is 27961f28255Scgd * non-interruptible our hand must be forced or we (and our children) won't 28061f28255Scgd * be either. Our children inherit termination from our parent. We catch it 28161f28255Scgd * only if we are the login shell. 28261f28255Scgd */ 28361f28255Scgd /* parents interruptibility */ 2847b38403cSmycroft (void) sigaction(SIGINT, NULL, &oact); 2857b38403cSmycroft parintr = oact.sa_handler; 2867b38403cSmycroft (void) sigaction(SIGTERM, NULL, &oact); 2877b38403cSmycroft parterm = oact.sa_handler; 28861f28255Scgd 2892ee028a2Scgd /* catch these all, login shell or not */ 29061f28255Scgd (void) signal(SIGHUP, phup); /* exit processing on HUP */ 29161f28255Scgd (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 29261f28255Scgd (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 29361f28255Scgd 29461f28255Scgd /* 29561f28255Scgd * Process the arguments. 29661f28255Scgd * 29761f28255Scgd * Note that processing of -v/-x is actually delayed till after script 29861f28255Scgd * processing. 29961f28255Scgd * 300cee2bad8Smycroft * We set the first character of our name to be '-' if we are a shell 301cee2bad8Smycroft * running interruptible commands. Many programs which examine ps'es 302cee2bad8Smycroft * use this to filter such shells out. 30361f28255Scgd */ 30461f28255Scgd argc--, tempv++; 30561f28255Scgd while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { 30661f28255Scgd do 30761f28255Scgd switch (*tcp++) { 30861f28255Scgd 30961f28255Scgd case 0: /* - Interruptible, no prompt */ 31061f28255Scgd prompt = 0; 31161f28255Scgd setintr = 1; 31261f28255Scgd nofile = 1; 31361f28255Scgd break; 31461f28255Scgd 31561f28255Scgd case 'b': /* -b Next arg is input file */ 31661f28255Scgd batch = 1; 31761f28255Scgd break; 31861f28255Scgd 31961f28255Scgd case 'c': /* -c Command input from arg */ 320*ee9e50eaSmycroft if (argc == 1) 32161f28255Scgd xexit(0); 32261f28255Scgd argc--, tempv++; 32361f28255Scgd arginp = SAVE(tempv[0]); 32461f28255Scgd prompt = 0; 32561f28255Scgd nofile = 1; 32661f28255Scgd break; 32761f28255Scgd 32861f28255Scgd case 'e': /* -e Exit on any error */ 32961f28255Scgd exiterr = 1; 33061f28255Scgd break; 33161f28255Scgd 33261f28255Scgd case 'f': /* -f Fast start */ 33361f28255Scgd fast = 1; 33461f28255Scgd break; 33561f28255Scgd 33661f28255Scgd case 'i': /* -i Interactive, even if !intty */ 33761f28255Scgd intact = 1; 33861f28255Scgd nofile = 1; 33961f28255Scgd break; 34061f28255Scgd 34161f28255Scgd case 'm': /* -m read .cshrc (from su) */ 34261f28255Scgd mflag = 1; 34361f28255Scgd break; 34461f28255Scgd 34561f28255Scgd case 'n': /* -n Don't execute */ 34661f28255Scgd noexec = 1; 34761f28255Scgd break; 34861f28255Scgd 34961f28255Scgd case 'q': /* -q (Undoc'd) ... die on quit */ 35061f28255Scgd quitit = 1; 35161f28255Scgd break; 35261f28255Scgd 35361f28255Scgd case 's': /* -s Read from std input */ 35461f28255Scgd nofile = 1; 35561f28255Scgd break; 35661f28255Scgd 35761f28255Scgd case 't': /* -t Read one line from input */ 35861f28255Scgd onelflg = 2; 35961f28255Scgd prompt = 0; 36061f28255Scgd nofile = 1; 36161f28255Scgd break; 36261f28255Scgd 36361f28255Scgd case 'v': /* -v Echo hist expanded input */ 36461f28255Scgd nverbose = 1; /* ... later */ 36561f28255Scgd break; 36661f28255Scgd 36761f28255Scgd case 'x': /* -x Echo just before execution */ 36861f28255Scgd nexececho = 1; /* ... later */ 36961f28255Scgd break; 37061f28255Scgd 37161f28255Scgd case 'V': /* -V Echo hist expanded input */ 37261f28255Scgd setNS(STRverbose); /* NOW! */ 37361f28255Scgd break; 37461f28255Scgd 37561f28255Scgd case 'X': /* -X Echo just before execution */ 37661f28255Scgd setNS(STRecho); /* NOW! */ 37761f28255Scgd break; 37861f28255Scgd 37961f28255Scgd } while (*tcp); 38061f28255Scgd tempv++, argc--; 38161f28255Scgd } 38261f28255Scgd 38361f28255Scgd if (quitit) /* With all due haste, for debugging */ 38461f28255Scgd (void) signal(SIGQUIT, SIG_DFL); 38561f28255Scgd 38661f28255Scgd /* 38761f28255Scgd * Unless prevented by -, -c, -i, -s, or -t, if there are remaining 38861f28255Scgd * arguments the first of them is the name of a shell file from which to 38961f28255Scgd * read commands. 39061f28255Scgd */ 39161f28255Scgd if (nofile == 0 && argc > 0) { 39261f28255Scgd nofile = open(tempv[0], O_RDONLY); 39361f28255Scgd if (nofile < 0) { 39461f28255Scgd child = 1; /* So this doesn't return */ 39561f28255Scgd stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 39661f28255Scgd } 39761f28255Scgd ffile = SAVE(tempv[0]); 39861f28255Scgd /* 39961f28255Scgd * Replace FSHIN. Handle /dev/std{in,out,err} specially 40061f28255Scgd * since once they are closed we cannot open them again. 40161f28255Scgd * In that case we use our own saved descriptors 40261f28255Scgd */ 40361f28255Scgd if ((SHIN = dmove(nofile, FSHIN)) < 0) 40461f28255Scgd switch(nofile) { 40561f28255Scgd case 0: 40661f28255Scgd SHIN = FSHIN; 40761f28255Scgd break; 40861f28255Scgd case 1: 40961f28255Scgd SHIN = FSHOUT; 41061f28255Scgd break; 41161f28255Scgd case 2: 412cee2bad8Smycroft SHIN = FSHERR; 41361f28255Scgd break; 41461f28255Scgd default: 41561f28255Scgd stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 416cdbd74daSmycroft /* NOTREACHED */ 41761f28255Scgd } 41861f28255Scgd (void) ioctl(SHIN, FIOCLEX, NULL); 41961f28255Scgd prompt = 0; 42061f28255Scgd /* argc not used any more */ tempv++; 42161f28255Scgd } 422cee2bad8Smycroft 42361f28255Scgd intty = isatty(SHIN); 42461f28255Scgd intty |= intact; 42561f28255Scgd if (intty || (intact && isatty(SHOUT))) { 426cee2bad8Smycroft if (!batch && (uid != euid || gid != egid)) { 42761f28255Scgd errno = EACCES; 42861f28255Scgd child = 1; /* So this doesn't return */ 42961f28255Scgd stderror(ERR_SYSTEM, "csh", strerror(errno)); 43061f28255Scgd } 43161f28255Scgd } 43261f28255Scgd /* 43361f28255Scgd * Decide whether we should play with signals or not. If we are explicitly 43461f28255Scgd * told (via -i, or -) or we are a login shell (arg0 starts with -) or the 43561f28255Scgd * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 43661f28255Scgd * Note that in only the login shell is it likely that parent may have set 43761f28255Scgd * signals to be ignored 43861f28255Scgd */ 439cee2bad8Smycroft if (loginsh || intact || (intty && isatty(SHOUT))) 44061f28255Scgd setintr = 1; 44161f28255Scgd settell(); 44261f28255Scgd /* 44361f28255Scgd * Save the remaining arguments in argv. 44461f28255Scgd */ 44561f28255Scgd setq(STRargv, blk2short(tempv), &shvhed); 44661f28255Scgd 44761f28255Scgd /* 44861f28255Scgd * Set up the prompt. 44961f28255Scgd */ 45061f28255Scgd if (prompt) { 45161f28255Scgd set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); 45261f28255Scgd /* that's a meta-questionmark */ 45361f28255Scgd set(STRprompt2, Strsave(STRmquestion)); 45461f28255Scgd } 45561f28255Scgd 45661f28255Scgd /* 45761f28255Scgd * If we are an interactive shell, then start fiddling with the signals; 45861f28255Scgd * this is a tricky game. 45961f28255Scgd */ 46061f28255Scgd shpgrp = getpgrp(); 46161f28255Scgd opgrp = tpgrp = -1; 46261f28255Scgd if (setintr) { 46361f28255Scgd **argv = '-'; 46461f28255Scgd if (!quitit) /* Wary! */ 46561f28255Scgd (void) signal(SIGQUIT, SIG_IGN); 46661f28255Scgd (void) signal(SIGINT, pintr); 4677b38403cSmycroft sigemptyset(&sigset); 4685924694dSmycroft (void) sigaddset(&sigset, SIGINT); 4695924694dSmycroft (void) sigprocmask(SIG_BLOCK, &sigset, NULL); 47061f28255Scgd (void) signal(SIGTERM, SIG_IGN); 47161f28255Scgd if (quitit == 0 && arginp == 0) { 47261f28255Scgd (void) signal(SIGTSTP, SIG_IGN); 47361f28255Scgd (void) signal(SIGTTIN, SIG_IGN); 47461f28255Scgd (void) signal(SIGTTOU, SIG_IGN); 47561f28255Scgd /* 47661f28255Scgd * Wait till in foreground, in case someone stupidly runs csh & 47761f28255Scgd * dont want to try to grab away the tty. 47861f28255Scgd */ 479cee2bad8Smycroft if (isatty(FSHERR)) 480cee2bad8Smycroft f = FSHERR; 48161f28255Scgd else if (isatty(FSHOUT)) 48261f28255Scgd f = FSHOUT; 48361f28255Scgd else if (isatty(OLDSTD)) 48461f28255Scgd f = OLDSTD; 48561f28255Scgd else 48661f28255Scgd f = -1; 48761f28255Scgd retry: 48861f28255Scgd if ((tpgrp = tcgetpgrp(f)) != -1) { 48961f28255Scgd if (tpgrp != shpgrp) { 49061f28255Scgd sig_t old = signal(SIGTTIN, SIG_DFL); 49161f28255Scgd (void) kill(0, SIGTTIN); 49261f28255Scgd (void) signal(SIGTTIN, old); 49361f28255Scgd goto retry; 49461f28255Scgd } 49561f28255Scgd opgrp = shpgrp; 49661f28255Scgd shpgrp = getpid(); 49761f28255Scgd tpgrp = shpgrp; 49861f28255Scgd /* 49961f28255Scgd * Setpgid will fail if we are a session leader and 50061f28255Scgd * mypid == mypgrp (POSIX 4.3.3) 50161f28255Scgd */ 50261f28255Scgd if (opgrp != shpgrp) 50361f28255Scgd if (setpgid(0, shpgrp) == -1) 50461f28255Scgd goto notty; 50561f28255Scgd /* 50661f28255Scgd * We do that after we set our process group, to make sure 50761f28255Scgd * that the process group belongs to a process in the same 50861f28255Scgd * session as the tty (our process and our group) (POSIX 7.2.4) 50961f28255Scgd */ 51061f28255Scgd if (tcsetpgrp(f, shpgrp) == -1) 51161f28255Scgd goto notty; 51261f28255Scgd (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); 51361f28255Scgd } 51461f28255Scgd if (tpgrp == -1) { 51561f28255Scgd notty: 516cee2bad8Smycroft (void) fprintf(csherr, "Warning: no access to tty (%s).\n", 517cee2bad8Smycroft strerror(errno)); 518cee2bad8Smycroft (void) fprintf(csherr, "Thus no job control in this shell.\n"); 51961f28255Scgd } 52061f28255Scgd } 52161f28255Scgd } 52261f28255Scgd if ((setintr == 0) && (parintr == SIG_DFL)) 52361f28255Scgd setintr = 1; 52461f28255Scgd (void) signal(SIGCHLD, pchild); /* while signals not ready */ 52561f28255Scgd 52661f28255Scgd /* 52761f28255Scgd * Set an exit here in case of an interrupt or error reading the shell 52861f28255Scgd * start-up scripts. 52961f28255Scgd */ 53061f28255Scgd reenter = setexit(); /* PWP */ 53161f28255Scgd haderr = 0; /* In case second time through */ 53261f28255Scgd if (!fast && reenter == 0) { 53361f28255Scgd /* Will have value(STRhome) here because set fast if don't */ 53461f28255Scgd { 53561f28255Scgd int osetintr = setintr; 536cee2bad8Smycroft sig_t oparintr = parintr; 5377b38403cSmycroft sigset_t osigset; 5387b38403cSmycroft 5397b38403cSmycroft sigemptyset(&sigset); 5405924694dSmycroft (void) sigaddset(&sigset, SIGINT); 5415924694dSmycroft (void) sigprocmask(SIG_BLOCK, &sigset, &osigset); 54261f28255Scgd 54361f28255Scgd setintr = 0; 544cee2bad8Smycroft parintr = SIG_IGN; /* Disable onintr */ 54561f28255Scgd #ifdef _PATH_DOTCSHRC 54661f28255Scgd (void) srcfile(_PATH_DOTCSHRC, 0, 0); 54761f28255Scgd #endif 54861f28255Scgd if (!fast && !arginp && !onelflg) 549cee2bad8Smycroft dohash(NULL, NULL); 55061f28255Scgd #ifdef _PATH_DOTLOGIN 55161f28255Scgd if (loginsh) 55261f28255Scgd (void) srcfile(_PATH_DOTLOGIN, 0, 0); 55361f28255Scgd #endif 5545924694dSmycroft (void) sigprocmask(SIG_SETMASK, &osigset, NULL); 55561f28255Scgd setintr = osetintr; 556cee2bad8Smycroft parintr = oparintr; 55761f28255Scgd } 55861f28255Scgd (void) srccat(value(STRhome), STRsldotcshrc); 55961f28255Scgd 56061f28255Scgd if (!fast && !arginp && !onelflg && !havhash) 561cee2bad8Smycroft dohash(NULL, NULL); 562cee2bad8Smycroft /* 563cee2bad8Smycroft * Source history before .login so that it is available in .login 564cee2bad8Smycroft */ 565cee2bad8Smycroft if ((cp = value(STRhistfile)) != STRNULL) 566cee2bad8Smycroft loadhist[2] = cp; 567cee2bad8Smycroft dosource(loadhist, NULL); 56861f28255Scgd if (loginsh) 56961f28255Scgd (void) srccat(value(STRhome), STRsldotlogin); 57061f28255Scgd } 57161f28255Scgd 57261f28255Scgd /* 57361f28255Scgd * Now are ready for the -v and -x flags 57461f28255Scgd */ 57561f28255Scgd if (nverbose) 57661f28255Scgd setNS(STRverbose); 57761f28255Scgd if (nexececho) 57861f28255Scgd setNS(STRecho); 57961f28255Scgd 58061f28255Scgd /* 58161f28255Scgd * All the rest of the world is inside this call. The argument to process 58261f28255Scgd * indicates whether it should catch "error unwinds". Thus if we are a 58361f28255Scgd * interactive shell our call here will never return by being blown past on 58461f28255Scgd * an error. 58561f28255Scgd */ 58661f28255Scgd process(setintr); 58761f28255Scgd 58861f28255Scgd /* 58961f28255Scgd * Mop-up. 59061f28255Scgd */ 59161f28255Scgd if (intty) { 59261f28255Scgd if (loginsh) { 593cee2bad8Smycroft (void) fprintf(cshout, "logout\n"); 59461f28255Scgd (void) close(SHIN); 59561f28255Scgd child = 1; 59661f28255Scgd goodbye(); 59761f28255Scgd } 59861f28255Scgd else { 599cee2bad8Smycroft (void) fprintf(cshout, "exit\n"); 60061f28255Scgd } 60161f28255Scgd } 60261f28255Scgd rechist(); 60361f28255Scgd exitstat(); 604cdbd74daSmycroft /* NOTREACHED */ 60561f28255Scgd } 60661f28255Scgd 60761f28255Scgd void 60861f28255Scgd untty() 60961f28255Scgd { 61061f28255Scgd if (tpgrp > 0) { 61161f28255Scgd (void) setpgid(0, opgrp); 61261f28255Scgd (void) tcsetpgrp(FSHTTY, opgrp); 61361f28255Scgd } 61461f28255Scgd } 61561f28255Scgd 61661f28255Scgd void 61761f28255Scgd importpath(cp) 61861f28255Scgd Char *cp; 61961f28255Scgd { 62076adbe2bStls int i = 0; 62176adbe2bStls Char *dp; 62276adbe2bStls Char **pv; 62361f28255Scgd int c; 62461f28255Scgd 62561f28255Scgd for (dp = cp; *dp; dp++) 62661f28255Scgd if (*dp == ':') 62761f28255Scgd i++; 62861f28255Scgd /* 62961f28255Scgd * i+2 where i is the number of colons in the path. There are i+1 63061f28255Scgd * directories in the path plus we need room for a zero terminator. 63161f28255Scgd */ 63261f28255Scgd pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); 63361f28255Scgd dp = cp; 63461f28255Scgd i = 0; 63561f28255Scgd if (*dp) 63661f28255Scgd for (;;) { 63761f28255Scgd if ((c = *dp) == ':' || c == 0) { 63861f28255Scgd *dp = 0; 63961f28255Scgd pv[i++] = Strsave(*cp ? cp : STRdot); 64061f28255Scgd if (c) { 64161f28255Scgd cp = dp + 1; 64261f28255Scgd *dp = ':'; 64361f28255Scgd } 64461f28255Scgd else 64561f28255Scgd break; 64661f28255Scgd } 64761f28255Scgd dp++; 64861f28255Scgd } 64961f28255Scgd pv[i] = 0; 65061f28255Scgd set1(STRpath, pv, &shvhed); 65161f28255Scgd } 65261f28255Scgd 65361f28255Scgd /* 65461f28255Scgd * Source to the file which is the catenation of the argument names. 65561f28255Scgd */ 65661f28255Scgd static int 65761f28255Scgd srccat(cp, dp) 65861f28255Scgd Char *cp, *dp; 65961f28255Scgd { 66076adbe2bStls Char *ep = Strspl(cp, dp); 66161f28255Scgd char *ptr = short2str(ep); 66261f28255Scgd 66361f28255Scgd xfree((ptr_t) ep); 66461f28255Scgd return srcfile(ptr, mflag ? 0 : 1, 0); 66561f28255Scgd } 66661f28255Scgd 66761f28255Scgd /* 66861f28255Scgd * Source to a file putting the file descriptor in a safe place (> 2). 66961f28255Scgd */ 67061f28255Scgd static int 67161f28255Scgd srcfile(f, onlyown, flag) 67261f28255Scgd char *f; 67361f28255Scgd bool onlyown, flag; 67461f28255Scgd { 67576adbe2bStls int unit; 67661f28255Scgd 67761f28255Scgd if ((unit = open(f, O_RDONLY)) == -1) 67861f28255Scgd return 0; 67961f28255Scgd unit = dmove(unit, -1); 68061f28255Scgd 68161f28255Scgd (void) ioctl(unit, FIOCLEX, NULL); 68261f28255Scgd srcunit(unit, onlyown, flag); 68361f28255Scgd return 1; 68461f28255Scgd } 68561f28255Scgd 68661f28255Scgd /* 68761f28255Scgd * Source to a unit. If onlyown it must be our file or our group or 68861f28255Scgd * we don't chance it. This occurs on ".cshrc"s and the like. 68961f28255Scgd */ 69061f28255Scgd int insource; 69161f28255Scgd static void 69261f28255Scgd srcunit(unit, onlyown, hflg) 69376adbe2bStls int unit; 69461f28255Scgd bool onlyown, hflg; 69561f28255Scgd { 69661f28255Scgd /* We have to push down a lot of state here */ 69761f28255Scgd /* All this could go into a structure */ 69861f28255Scgd int oSHIN = -1, oldintty = intty, oinsource = insource; 69961f28255Scgd struct whyle *oldwhyl = whyles; 70061f28255Scgd Char *ogointr = gointr, *oarginp = arginp; 70161f28255Scgd Char *oevalp = evalp, **oevalvec = evalvec; 70261f28255Scgd int oonelflg = onelflg; 70361f28255Scgd bool oenterhist = enterhist; 70461f28255Scgd char OHIST = HIST; 70561f28255Scgd bool otell = cantell; 70661f28255Scgd 70761f28255Scgd struct Bin saveB; 7087b38403cSmycroft sigset_t sigset, osigset; 70961f28255Scgd jmp_buf oldexit; 71061f28255Scgd 71161f28255Scgd /* The (few) real local variables */ 71261f28255Scgd int my_reenter; 71361f28255Scgd 71461f28255Scgd if (unit < 0) 71561f28255Scgd return; 71661f28255Scgd if (didfds) 71761f28255Scgd donefds(); 71861f28255Scgd if (onlyown) { 71961f28255Scgd struct stat stb; 72061f28255Scgd 72161f28255Scgd if (fstat(unit, &stb) < 0) { 72261f28255Scgd (void) close(unit); 72361f28255Scgd return; 72461f28255Scgd } 72561f28255Scgd } 72661f28255Scgd 72761f28255Scgd /* 72861f28255Scgd * There is a critical section here while we are pushing down the input 72961f28255Scgd * stream since we have stuff in different structures. If we weren't 73061f28255Scgd * careful an interrupt could corrupt SHIN's Bin structure and kill the 73161f28255Scgd * shell. 73261f28255Scgd * 73361f28255Scgd * We could avoid the critical region by grouping all the stuff in a single 73461f28255Scgd * structure and pointing at it to move it all at once. This is less 73561f28255Scgd * efficient globally on many variable references however. 73661f28255Scgd */ 73761f28255Scgd insource = 1; 73861f28255Scgd getexit(oldexit); 73961f28255Scgd 7407b38403cSmycroft if (setintr) { 7417b38403cSmycroft sigemptyset(&sigset); 7425924694dSmycroft (void) sigaddset(&sigset, SIGINT); 7435924694dSmycroft (void) sigprocmask(SIG_BLOCK, &sigset, &osigset); 7447b38403cSmycroft } 74561f28255Scgd /* Setup the new values of the state stuff saved above */ 7465924694dSmycroft (void) memcpy(&saveB, &B, sizeof(B)); 74761f28255Scgd fbuf = NULL; 74861f28255Scgd fseekp = feobp = fblocks = 0; 74961f28255Scgd oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 75061f28255Scgd intty = isatty(SHIN), whyles = 0, gointr = 0; 75161f28255Scgd evalvec = 0; 75261f28255Scgd evalp = 0; 75361f28255Scgd enterhist = hflg; 75461f28255Scgd if (enterhist) 75561f28255Scgd HIST = '\0'; 75661f28255Scgd 75761f28255Scgd /* 75861f28255Scgd * Now if we are allowing commands to be interrupted, we let ourselves be 75961f28255Scgd * interrupted. 76061f28255Scgd */ 76161f28255Scgd if (setintr) 7625924694dSmycroft (void) sigprocmask(SIG_SETMASK, &osigset, NULL); 76361f28255Scgd settell(); 76461f28255Scgd 76561f28255Scgd if ((my_reenter = setexit()) == 0) 76661f28255Scgd process(0); /* 0 -> blow away on errors */ 76761f28255Scgd 76861f28255Scgd if (setintr) 7695924694dSmycroft (void) sigprocmask(SIG_SETMASK, &osigset, NULL); 77061f28255Scgd if (oSHIN >= 0) { 77176adbe2bStls int i; 77261f28255Scgd 77361f28255Scgd /* We made it to the new state... free up its storage */ 77461f28255Scgd /* This code could get run twice but xfree doesn't care */ 77561f28255Scgd for (i = 0; i < fblocks; i++) 77661f28255Scgd xfree((ptr_t) fbuf[i]); 77761f28255Scgd xfree((ptr_t) fbuf); 77861f28255Scgd 77961f28255Scgd /* Reset input arena */ 7805924694dSmycroft (void) memcpy(&B, &saveB, sizeof(B)); 78161f28255Scgd 78261f28255Scgd (void) close(SHIN), SHIN = oSHIN; 78361f28255Scgd arginp = oarginp, onelflg = oonelflg; 78461f28255Scgd evalp = oevalp, evalvec = oevalvec; 78561f28255Scgd intty = oldintty, whyles = oldwhyl, gointr = ogointr; 78661f28255Scgd if (enterhist) 78761f28255Scgd HIST = OHIST; 78861f28255Scgd enterhist = oenterhist; 78961f28255Scgd cantell = otell; 79061f28255Scgd } 79161f28255Scgd 79261f28255Scgd resexit(oldexit); 79361f28255Scgd /* 79461f28255Scgd * If process reset() (effectively an unwind) then we must also unwind. 79561f28255Scgd */ 796*ee9e50eaSmycroft if (my_reenter) 79761f28255Scgd stderror(ERR_SILENT); 79861f28255Scgd insource = oinsource; 79961f28255Scgd } 80061f28255Scgd 80161f28255Scgd void 80261f28255Scgd rechist() 80361f28255Scgd { 804cee2bad8Smycroft Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile; 80561f28255Scgd int fp, ftmp, oldidfds; 806cee2bad8Smycroft struct varent *shist; 80761f28255Scgd 80861f28255Scgd if (!fast) { 809cee2bad8Smycroft /* 810cee2bad8Smycroft * If $savehist is just set, we use the value of $history 811cee2bad8Smycroft * else we use the value in $savehist 812cee2bad8Smycroft */ 813cee2bad8Smycroft if ((shist = adrof(STRsavehist)) != NULL) { 814cee2bad8Smycroft if (shist->vec[0][0] != '\0') 815cee2bad8Smycroft (void) Strcpy(hbuf, shist->vec[0]); 816cee2bad8Smycroft else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0') 817cee2bad8Smycroft (void) Strcpy(hbuf, shist->vec[0]); 818cee2bad8Smycroft else 81961f28255Scgd return; 820cee2bad8Smycroft } 821cee2bad8Smycroft else 822cee2bad8Smycroft return; 823cee2bad8Smycroft 824cee2bad8Smycroft if ((hfile = value(STRhistfile)) == STRNULL) { 825cee2bad8Smycroft hfile = Strcpy(buf, value(STRhome)); 82661f28255Scgd (void) Strcat(buf, STRsldthist); 827cee2bad8Smycroft } 828cee2bad8Smycroft 829baccf0fbSmycroft if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC, 830baccf0fbSmycroft 0600)) == -1) 83161f28255Scgd return; 832cee2bad8Smycroft 83361f28255Scgd oldidfds = didfds; 83461f28255Scgd didfds = 0; 83561f28255Scgd ftmp = SHOUT; 83661f28255Scgd SHOUT = fp; 837cee2bad8Smycroft dumphist[2] = hbuf; 838cee2bad8Smycroft dohist(dumphist, NULL); 83961f28255Scgd SHOUT = ftmp; 840cee2bad8Smycroft (void) close(fp); 84161f28255Scgd didfds = oldidfds; 84261f28255Scgd } 84361f28255Scgd } 84461f28255Scgd 84561f28255Scgd void 84661f28255Scgd goodbye() 84761f28255Scgd { 84861f28255Scgd rechist(); 84961f28255Scgd 85061f28255Scgd if (loginsh) { 85161f28255Scgd (void) signal(SIGQUIT, SIG_IGN); 85261f28255Scgd (void) signal(SIGINT, SIG_IGN); 85361f28255Scgd (void) signal(SIGTERM, SIG_IGN); 85461f28255Scgd setintr = 0; /* No interrupts after "logout" */ 85561f28255Scgd if (!(adrof(STRlogout))) 85661f28255Scgd set(STRlogout, STRnormal); 85761f28255Scgd #ifdef _PATH_DOTLOGOUT 85861f28255Scgd (void) srcfile(_PATH_DOTLOGOUT, 0, 0); 85961f28255Scgd #endif 86061f28255Scgd if (adrof(STRhome)) 86161f28255Scgd (void) srccat(value(STRhome), STRsldtlogout); 86261f28255Scgd } 86361f28255Scgd exitstat(); 864cdbd74daSmycroft /* NOTREACHED */ 86561f28255Scgd } 86661f28255Scgd 867cdbd74daSmycroft __dead void 86861f28255Scgd exitstat() 86961f28255Scgd { 870cee2bad8Smycroft Char *s; 87161f28255Scgd #ifdef PROF 87261f28255Scgd monitor(0); 87361f28255Scgd #endif 87461f28255Scgd /* 87561f28255Scgd * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit 87661f28255Scgd * directly because we poke child here. Otherwise we might continue 87761f28255Scgd * unwarrantedly (sic). 87861f28255Scgd */ 87961f28255Scgd child = 1; 880cee2bad8Smycroft s = value(STRstatus); 881cee2bad8Smycroft xexit(s ? getn(s) : 0); 882cdbd74daSmycroft /* NOTREACHED */ 88361f28255Scgd } 88461f28255Scgd 88561f28255Scgd /* 88661f28255Scgd * in the event of a HUP we want to save the history 88761f28255Scgd */ 88861f28255Scgd static void 88961f28255Scgd phup(sig) 89061f28255Scgd int sig; 89161f28255Scgd { 89261f28255Scgd rechist(); 8932ee028a2Scgd 8942ee028a2Scgd /* 8952ee028a2Scgd * We kill the last foreground process group. It then becomes 8962ee028a2Scgd * responsible to propagate the SIGHUP to its progeny. 8972ee028a2Scgd */ 898cee2bad8Smycroft { 899cee2bad8Smycroft struct process *pp, *np; 900cee2bad8Smycroft 9012ee028a2Scgd for (pp = proclist.p_next; pp; pp = pp->p_next) { 9022ee028a2Scgd np = pp; 9032ee028a2Scgd /* 9042ee028a2Scgd * Find if this job is in the foreground. It could be that 9052ee028a2Scgd * the process leader has exited and the foreground flag 9062ee028a2Scgd * is cleared for it. 9072ee028a2Scgd */ 908cee2bad8Smycroft do 9092ee028a2Scgd /* 9102ee028a2Scgd * If a process is in the foreground; we try to kill 9112ee028a2Scgd * it's process group. If we succeed, then the 9122ee028a2Scgd * whole job is gone. Otherwise we keep going... 9132ee028a2Scgd * But avoid sending HUP to the shell again. 9142ee028a2Scgd */ 915cee2bad8Smycroft if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp && 916556d212cSmycroft kill(-np->p_jobid, SIGHUP) != -1) { 9172ee028a2Scgd /* In case the job was suspended... */ 918556d212cSmycroft (void) kill(-np->p_jobid, SIGCONT); 9192ee028a2Scgd break; 9202ee028a2Scgd } 921cee2bad8Smycroft while ((np = np->p_friends) != pp); 9222ee028a2Scgd } 923cee2bad8Smycroft } 92461f28255Scgd xexit(sig); 925cdbd74daSmycroft /* NOTREACHED */ 92661f28255Scgd } 92761f28255Scgd 92861f28255Scgd Char *jobargv[2] = {STRjobs, 0}; 92961f28255Scgd 93061f28255Scgd /* 93161f28255Scgd * Catch an interrupt, e.g. during lexical input. 93261f28255Scgd * If we are an interactive shell, we reset the interrupt catch 93361f28255Scgd * immediately. In any case we drain the shell output, 93461f28255Scgd * and finally go through the normal error mechanism, which 93561f28255Scgd * gets a chance to make the shell go away. 93661f28255Scgd */ 93761f28255Scgd /* ARGSUSED */ 93861f28255Scgd void 93961f28255Scgd pintr(notused) 94061f28255Scgd int notused; 94161f28255Scgd { 94261f28255Scgd pintr1(1); 943*ee9e50eaSmycroft /* NOTREACHED */ 94461f28255Scgd } 94561f28255Scgd 94661f28255Scgd void 94761f28255Scgd pintr1(wantnl) 94861f28255Scgd bool wantnl; 94961f28255Scgd { 950cee2bad8Smycroft Char **v; 9517b38403cSmycroft sigset_t sigset, osigset; 95261f28255Scgd 9537b38403cSmycroft sigemptyset(&sigset); 9545924694dSmycroft (void) sigprocmask(SIG_BLOCK, &sigset, &osigset); 95561f28255Scgd if (setintr) { 9567b38403cSmycroft sigset = osigset; 9575924694dSmycroft (void) sigdelset(&sigset, SIGINT); 9585924694dSmycroft (void) sigprocmask(SIG_SETMASK, &sigset, NULL); 95961f28255Scgd if (pjobs) { 96061f28255Scgd pjobs = 0; 961cee2bad8Smycroft (void) fprintf(cshout, "\n"); 962cee2bad8Smycroft dojobs(jobargv, NULL); 96361f28255Scgd stderror(ERR_NAME | ERR_INTR); 96461f28255Scgd } 96561f28255Scgd } 9665924694dSmycroft (void) sigdelset(&osigset, SIGCHLD); 9675924694dSmycroft (void) sigprocmask(SIG_SETMASK, &osigset, NULL); 968cee2bad8Smycroft (void) fpurge(cshout); 96961f28255Scgd (void) endpwent(); 97061f28255Scgd 97161f28255Scgd /* 97261f28255Scgd * If we have an active "onintr" then we search for the label. Note that if 97361f28255Scgd * one does "onintr -" then we shan't be interruptible so we needn't worry 97461f28255Scgd * about that here. 97561f28255Scgd */ 97661f28255Scgd if (gointr) { 977cee2bad8Smycroft gotolab(gointr); 97861f28255Scgd timflg = 0; 979cee2bad8Smycroft if ((v = pargv) != NULL) 98061f28255Scgd pargv = 0, blkfree(v); 981cee2bad8Smycroft if ((v = gargv) != NULL) 98261f28255Scgd gargv = 0, blkfree(v); 98361f28255Scgd reset(); 98461f28255Scgd } 98561f28255Scgd else if (intty && wantnl) { 986cee2bad8Smycroft (void) fputc('\r', cshout); 987cee2bad8Smycroft (void) fputc('\n', cshout); 98861f28255Scgd } 98961f28255Scgd stderror(ERR_SILENT); 990cdbd74daSmycroft /* NOTREACHED */ 99161f28255Scgd } 99261f28255Scgd 99361f28255Scgd /* 99461f28255Scgd * Process is the main driving routine for the shell. 99561f28255Scgd * It runs all command processing, except for those within { ... } 99661f28255Scgd * in expressions (which is run by a routine evalav in sh.exp.c which 99761f28255Scgd * is a stripped down process), and `...` evaluation which is run 99861f28255Scgd * also by a subset of this code in sh.glob.c in the routine backeval. 99961f28255Scgd * 100061f28255Scgd * The code here is a little strange because part of it is interruptible 100161f28255Scgd * and hence freeing of structures appears to occur when none is necessary 100261f28255Scgd * if this is ignored. 100361f28255Scgd * 100461f28255Scgd * Note that if catch is not set then we will unwind on any error. 100561f28255Scgd * If an end-of-file occurs, we return. 100661f28255Scgd */ 1007cee2bad8Smycroft static struct command *savet = NULL; 100861f28255Scgd void 100961f28255Scgd process(catch) 101061f28255Scgd bool catch; 101161f28255Scgd { 101261f28255Scgd jmp_buf osetexit; 1013cee2bad8Smycroft struct command *t = savet; 10147b38403cSmycroft sigset_t sigset; 101561f28255Scgd 1016cee2bad8Smycroft savet = NULL; 101761f28255Scgd getexit(osetexit); 101861f28255Scgd for (;;) { 101961f28255Scgd pendjob(); 102061f28255Scgd paraml.next = paraml.prev = ¶ml; 102161f28255Scgd paraml.word = STRNULL; 102261f28255Scgd (void) setexit(); 102361f28255Scgd justpr = enterhist; /* execute if not entering history */ 102461f28255Scgd 102561f28255Scgd /* 102661f28255Scgd * Interruptible during interactive reads 102761f28255Scgd */ 10287b38403cSmycroft if (setintr) { 10297b38403cSmycroft sigemptyset(&sigset); 10305924694dSmycroft (void) sigaddset(&sigset, SIGINT); 10315924694dSmycroft (void) sigprocmask(SIG_UNBLOCK, &sigset, NULL); 10327b38403cSmycroft } 103361f28255Scgd 103461f28255Scgd /* 103561f28255Scgd * For the sake of reset() 103661f28255Scgd */ 103761f28255Scgd freelex(¶ml); 1038cee2bad8Smycroft if (savet) 1039cee2bad8Smycroft freesyn(savet), savet = NULL; 104061f28255Scgd 104161f28255Scgd if (haderr) { 104261f28255Scgd if (!catch) { 104361f28255Scgd /* unwind */ 104461f28255Scgd doneinp = 0; 104561f28255Scgd resexit(osetexit); 1046cee2bad8Smycroft savet = t; 104761f28255Scgd reset(); 104861f28255Scgd } 104961f28255Scgd haderr = 0; 105061f28255Scgd /* 105161f28255Scgd * Every error is eventually caught here or the shell dies. It is 105261f28255Scgd * at this point that we clean up any left-over open files, by 105361f28255Scgd * closing all but a fixed number of pre-defined files. Thus 105461f28255Scgd * routines don't have to worry about leaving files open due to 105561f28255Scgd * deeper errors... they will get closed here. 105661f28255Scgd */ 105761f28255Scgd closem(); 105861f28255Scgd continue; 105961f28255Scgd } 106061f28255Scgd if (doneinp) { 106161f28255Scgd doneinp = 0; 106261f28255Scgd break; 106361f28255Scgd } 106461f28255Scgd if (chkstop) 106561f28255Scgd chkstop--; 106661f28255Scgd if (neednote) 106761f28255Scgd pnote(); 106861f28255Scgd if (intty && prompt && evalvec == 0) { 106961f28255Scgd mailchk(); 107061f28255Scgd /* 107161f28255Scgd * If we are at the end of the input buffer then we are going to 107261f28255Scgd * read fresh stuff. Otherwise, we are rereading input and don't 107361f28255Scgd * need or want to prompt. 107461f28255Scgd */ 1075cee2bad8Smycroft if (aret == F_SEEK && fseekp == feobp) 107661f28255Scgd printprompt(); 1077cee2bad8Smycroft (void) fflush(cshout); 107861f28255Scgd } 107961f28255Scgd if (seterr) { 108061f28255Scgd xfree((ptr_t) seterr); 108161f28255Scgd seterr = NULL; 108261f28255Scgd } 108361f28255Scgd 108461f28255Scgd /* 108561f28255Scgd * Echo not only on VERBOSE, but also with history expansion. If there 108661f28255Scgd * is a lexical error then we forego history echo. 108761f28255Scgd */ 1088cee2bad8Smycroft if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) { 1089cee2bad8Smycroft prlex(csherr, ¶ml); 109061f28255Scgd } 109161f28255Scgd 109261f28255Scgd /* 109361f28255Scgd * The parser may lose space if interrupted. 109461f28255Scgd */ 109561f28255Scgd if (setintr) 10965924694dSmycroft (void) sigprocmask(SIG_BLOCK, &sigset, NULL); 109761f28255Scgd 109861f28255Scgd /* 109961f28255Scgd * Save input text on the history list if reading in old history, or it 110061f28255Scgd * is from the terminal at the top level and not in a loop. 110161f28255Scgd * 110261f28255Scgd * PWP: entry of items in the history list while in a while loop is done 110361f28255Scgd * elsewhere... 110461f28255Scgd */ 1105cee2bad8Smycroft if (enterhist || (catch && intty && !whyles)) 110661f28255Scgd savehist(¶ml); 110761f28255Scgd 110861f28255Scgd /* 110961f28255Scgd * Print lexical error messages, except when sourcing history lists. 111061f28255Scgd */ 1111*ee9e50eaSmycroft if (!enterhist && seterr) 111261f28255Scgd stderror(ERR_OLD); 111361f28255Scgd 111461f28255Scgd /* 111561f28255Scgd * If had a history command :p modifier then this is as far as we 111661f28255Scgd * should go 111761f28255Scgd */ 111861f28255Scgd if (justpr) 111961f28255Scgd reset(); 112061f28255Scgd 112161f28255Scgd alias(¶ml); 112261f28255Scgd 112361f28255Scgd /* 112461f28255Scgd * Parse the words of the input into a parse tree. 112561f28255Scgd */ 1126cee2bad8Smycroft savet = syntax(paraml.next, ¶ml, 0); 1127*ee9e50eaSmycroft if (seterr) 112861f28255Scgd stderror(ERR_OLD); 112961f28255Scgd 1130cee2bad8Smycroft execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); 113161f28255Scgd 113261f28255Scgd /* 113361f28255Scgd * Made it! 113461f28255Scgd */ 113561f28255Scgd freelex(¶ml); 1136cee2bad8Smycroft freesyn((struct command *) savet), savet = NULL; 113761f28255Scgd } 113861f28255Scgd resexit(osetexit); 1139cee2bad8Smycroft savet = t; 114061f28255Scgd } 114161f28255Scgd 114261f28255Scgd void 1143cee2bad8Smycroft /*ARGSUSED*/ 1144cee2bad8Smycroft dosource(v, t) 1145cee2bad8Smycroft Char **v; 1146cee2bad8Smycroft struct command *t; 1147cee2bad8Smycroft 114861f28255Scgd { 114976adbe2bStls Char *f; 115061f28255Scgd bool hflg = 0; 115161f28255Scgd Char buf[BUFSIZ]; 115261f28255Scgd 1153cee2bad8Smycroft v++; 1154cee2bad8Smycroft if (*v && eq(*v, STRmh)) { 1155*ee9e50eaSmycroft if (*++v == NULL) 115661f28255Scgd stderror(ERR_NAME | ERR_HFLAG); 115761f28255Scgd hflg++; 115861f28255Scgd } 1159cee2bad8Smycroft (void) Strcpy(buf, *v); 116061f28255Scgd f = globone(buf, G_ERROR); 116161f28255Scgd (void) strcpy((char *) buf, short2str(f)); 116261f28255Scgd xfree((ptr_t) f); 1163*ee9e50eaSmycroft if (!srcfile((char *) buf, 0, hflg) && !hflg) 116461f28255Scgd stderror(ERR_SYSTEM, (char *) buf, strerror(errno)); 116561f28255Scgd } 116661f28255Scgd 116761f28255Scgd /* 116861f28255Scgd * Check for mail. 116961f28255Scgd * If we are a login shell, then we don't want to tell 117061f28255Scgd * about any mail file unless its been modified 117161f28255Scgd * after the time we started. 117261f28255Scgd * This prevents us from telling the user things he already 117361f28255Scgd * knows, since the login program insists on saying 117461f28255Scgd * "You have mail." 117561f28255Scgd */ 117661f28255Scgd static void 117761f28255Scgd mailchk() 117861f28255Scgd { 117976adbe2bStls struct varent *v; 118076adbe2bStls Char **vp; 118161f28255Scgd time_t t; 118261f28255Scgd int intvl, cnt; 118361f28255Scgd struct stat stb; 118461f28255Scgd bool new; 118561f28255Scgd 118661f28255Scgd v = adrof(STRmail); 118761f28255Scgd if (v == 0) 118861f28255Scgd return; 118961f28255Scgd (void) time(&t); 119061f28255Scgd vp = v->vec; 119161f28255Scgd cnt = blklen(vp); 119261f28255Scgd intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 119361f28255Scgd if (intvl < 1) 119461f28255Scgd intvl = 1; 119561f28255Scgd if (chktim + intvl > t) 119661f28255Scgd return; 119761f28255Scgd for (; *vp; vp++) { 119861f28255Scgd if (stat(short2str(*vp), &stb) < 0) 119961f28255Scgd continue; 120061f28255Scgd new = stb.st_mtime > time0.tv_sec; 120161f28255Scgd if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 120261f28255Scgd (stb.st_atime < chktim && stb.st_mtime < chktim) || 1203cee2bad8Smycroft (loginsh && !new)) 120461f28255Scgd continue; 120561f28255Scgd if (cnt == 1) 1206cee2bad8Smycroft (void) fprintf(cshout, "You have %smail.\n", new ? "new " : ""); 120761f28255Scgd else 1208cee2bad8Smycroft (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail", 1209cee2bad8Smycroft vis_str(*vp)); 121061f28255Scgd } 121161f28255Scgd chktim = t; 121261f28255Scgd } 121361f28255Scgd 121461f28255Scgd /* 121561f28255Scgd * Extract a home directory from the password file 121661f28255Scgd * The argument points to a buffer where the name of the 121761f28255Scgd * user whose home directory is sought is currently. 121861f28255Scgd * We write the home directory of the user back there. 121961f28255Scgd */ 122061f28255Scgd int 122161f28255Scgd gethdir(home) 122261f28255Scgd Char *home; 122361f28255Scgd { 122461f28255Scgd Char *h; 122561f28255Scgd struct passwd *pw; 122661f28255Scgd 122761f28255Scgd /* 122861f28255Scgd * Is it us? 122961f28255Scgd */ 123061f28255Scgd if (*home == '\0') { 1231cee2bad8Smycroft if ((h = value(STRhome)) != NULL) { 123261f28255Scgd (void) Strcpy(home, h); 123361f28255Scgd return 0; 123461f28255Scgd } 123561f28255Scgd else 123661f28255Scgd return 1; 123761f28255Scgd } 123861f28255Scgd 1239cee2bad8Smycroft if ((pw = getpwnam(short2str(home))) != NULL) { 124061f28255Scgd (void) Strcpy(home, str2short(pw->pw_dir)); 124161f28255Scgd return 0; 124261f28255Scgd } 124361f28255Scgd else 124461f28255Scgd return 1; 124561f28255Scgd } 124661f28255Scgd 124761f28255Scgd /* 1248cee2bad8Smycroft * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17 1249cee2bad8Smycroft * We also check if the shell has already changed the decriptor to point to 1250cee2bad8Smycroft * 0, 1, 2 when didfds is set. 1251cee2bad8Smycroft */ 1252cee2bad8Smycroft #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0)) 1253cee2bad8Smycroft 1254cee2bad8Smycroft static int 1255cee2bad8Smycroft readf(oreo, buf, siz) 1256cee2bad8Smycroft void *oreo; 1257cee2bad8Smycroft char *buf; 1258cee2bad8Smycroft int siz; 1259cee2bad8Smycroft { 1260cee2bad8Smycroft return read(DESC(oreo), buf, siz); 1261cee2bad8Smycroft } 1262cee2bad8Smycroft 1263cee2bad8Smycroft 1264cee2bad8Smycroft static int 1265cee2bad8Smycroft writef(oreo, buf, siz) 1266cee2bad8Smycroft void *oreo; 1267cee2bad8Smycroft const char *buf; 1268cee2bad8Smycroft int siz; 1269cee2bad8Smycroft { 1270cee2bad8Smycroft return write(DESC(oreo), buf, siz); 1271cee2bad8Smycroft } 1272cee2bad8Smycroft 1273cee2bad8Smycroft static fpos_t 1274cee2bad8Smycroft seekf(oreo, off, whence) 1275cee2bad8Smycroft void *oreo; 1276cee2bad8Smycroft fpos_t off; 1277cee2bad8Smycroft int whence; 1278cee2bad8Smycroft { 1279cee2bad8Smycroft return lseek(DESC(oreo), off, whence); 1280cee2bad8Smycroft } 1281cee2bad8Smycroft 1282cee2bad8Smycroft 1283cee2bad8Smycroft static int 1284cee2bad8Smycroft closef(oreo) 1285cee2bad8Smycroft void *oreo; 1286cee2bad8Smycroft { 1287cee2bad8Smycroft return close(DESC(oreo)); 1288cee2bad8Smycroft } 1289cee2bad8Smycroft 1290cee2bad8Smycroft 1291cee2bad8Smycroft /* 1292cee2bad8Smycroft * Print the visible version of a string. 1293cee2bad8Smycroft */ 1294cee2bad8Smycroft int 1295cee2bad8Smycroft vis_fputc(ch, fp) 1296cee2bad8Smycroft int ch; 1297cee2bad8Smycroft FILE *fp; 1298cee2bad8Smycroft { 1299cee2bad8Smycroft char uenc[5]; /* 4 + NULL */ 1300cee2bad8Smycroft 1301cee2bad8Smycroft if (ch & QUOTE) 1302cee2bad8Smycroft return fputc(ch & TRIM, fp); 1303cee2bad8Smycroft /* 1304cee2bad8Smycroft * XXX: When we are in AsciiOnly we want all characters >= 0200 to 1305cee2bad8Smycroft * be encoded, but currently there is no way in vis to do that. 1306cee2bad8Smycroft */ 1307cee2bad8Smycroft (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0); 1308cee2bad8Smycroft return fputs(uenc, fp); 1309cee2bad8Smycroft } 1310cee2bad8Smycroft 1311cee2bad8Smycroft /* 131261f28255Scgd * Move the initial descriptors to their eventual 131361f28255Scgd * resting places, closin all other units. 131461f28255Scgd */ 131561f28255Scgd void 131661f28255Scgd initdesc() 131761f28255Scgd { 131861f28255Scgd 131961f28255Scgd didfds = 0; /* 0, 1, 2 aren't set up */ 132061f28255Scgd (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); 132161f28255Scgd (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); 1322cee2bad8Smycroft (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); 132361f28255Scgd (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); 132461f28255Scgd closem(); 132561f28255Scgd } 132661f28255Scgd 132761f28255Scgd 1328cdbd74daSmycroft __dead void 132961f28255Scgd #ifdef PROF 133061f28255Scgd done(i) 133161f28255Scgd #else 133261f28255Scgd xexit(i) 133361f28255Scgd #endif 133461f28255Scgd int i; 133561f28255Scgd { 133661f28255Scgd untty(); 133761f28255Scgd _exit(i); 1338cdbd74daSmycroft /* NOTREACHED */ 133961f28255Scgd } 134061f28255Scgd 13410f668275Sfair #ifndef _PATH_DEFPATH 134261f28255Scgd static Char ** 134361f28255Scgd defaultpath() 134461f28255Scgd { 134561f28255Scgd char *ptr; 134661f28255Scgd Char **blk, **blkp; 134761f28255Scgd struct stat stb; 134861f28255Scgd 134961f28255Scgd blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); 135061f28255Scgd 135161f28255Scgd #define DIRAPPEND(a) \ 1352f5ad44b6Smycroft if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \ 135361f28255Scgd *blkp++ = SAVE(ptr) 135461f28255Scgd 135561f28255Scgd DIRAPPEND(_PATH_BIN); 135661f28255Scgd DIRAPPEND(_PATH_USRBIN); 135761f28255Scgd 135861f28255Scgd #undef DIRAPPEND 135961f28255Scgd 13604d643bf2Smycroft #if 0 1361cee2bad8Smycroft if (euid != 0 && uid != 0) 136261f28255Scgd *blkp++ = Strsave(STRdot); 13634d643bf2Smycroft #endif 13644d643bf2Smycroft 136561f28255Scgd *blkp = NULL; 136661f28255Scgd return (blk); 136761f28255Scgd } 13680f668275Sfair #endif /* _PATH_DEFPATH */ 136961f28255Scgd 137061f28255Scgd void 137161f28255Scgd printprompt() 137261f28255Scgd { 137376adbe2bStls Char *cp; 137461f28255Scgd 137561f28255Scgd if (!whyles) { 137661f28255Scgd for (cp = value(STRprompt); *cp; cp++) 137761f28255Scgd if (*cp == HIST) 1378cee2bad8Smycroft (void) fprintf(cshout, "%d", eventno + 1); 137961f28255Scgd else { 138061f28255Scgd if (*cp == '\\' && cp[1] == HIST) 138161f28255Scgd cp++; 1382cee2bad8Smycroft (void) vis_fputc(*cp | QUOTE, cshout); 138361f28255Scgd } 138461f28255Scgd } 138561f28255Scgd else 138661f28255Scgd /* 138761f28255Scgd * Prompt for forward reading loop body content. 138861f28255Scgd */ 1389cee2bad8Smycroft (void) fprintf(cshout, "? "); 1390cee2bad8Smycroft (void) fflush(cshout); 139161f28255Scgd } 1392