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