xref: /netbsd/bin/csh/csh.c (revision ee9e50ea)
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 = &paraml;
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(&paraml);
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(&paraml) && !seterr && intty) || adrof(STRverbose)) {
1089cee2bad8Smycroft 	    prlex(csherr, &paraml);
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(&paraml);
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(&paraml);
112261f28255Scgd 
112361f28255Scgd 	/*
112461f28255Scgd 	 * Parse the words of the input into a parse tree.
112561f28255Scgd 	 */
1126cee2bad8Smycroft 	savet = syntax(paraml.next, &paraml, 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(&paraml);
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