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