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