sh.c revision 267654
1/* $Header: /p/tcsh/cvsroot/tcsh/sh.c,v 3.174 2011/11/29 18:38:54 christos Exp $ */
2/*
3 * sh.c: Main shell routines
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33#define EXTERN	/* Intern */
34#include "sh.h"
35
36#ifndef lint
37char    copyright[] =
38"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
39 All rights reserved.\n";
40#endif /* not lint */
41
42RCSID("$tcsh: sh.c,v 3.174 2011/11/29 18:38:54 christos Exp $")
43
44#include "tc.h"
45#include "ed.h"
46#include "tw.h"
47
48extern int MapsAreInited;
49extern int NLSMapsAreInited;
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 * Filename recognition added:
61 * Ken Greer, Ind. Consultant, Palo Alto CA
62 * October 1983.
63 *
64 * Karl Kleinpaste, Computer Consoles, Inc.
65 * Added precmd, periodic/tperiod, prompt changes,
66 * directory stack hack, and login watch.
67 * Sometime March 1983 - Feb 1984.
68 *
69 * Added scheduled commands, including the "sched" command,
70 * plus the call to sched_run near the precmd et al
71 * routines.
72 * Upgraded scheduled events for running events while
73 * sitting idle at command input.
74 *
75 * Paul Placeway, Ohio State
76 * added stuff for running with twenex/inputl  9 Oct 1984.
77 *
78 * ported to Apple Unix (TM) (OREO)  26 -- 29 Jun 1987
79 */
80
81jmp_buf_t reslab;
82
83static const char tcshstr[] = "tcsh";
84
85struct sigaction parintr;	/* Parents interrupt catch */
86struct sigaction parterm;	/* Parents terminate catch */
87
88#ifdef TESLA
89int do_logout = 0;
90#endif /* TESLA */
91
92
93int    use_fork = 0;		/* use fork() instead of vfork()? */
94
95/*
96 * Magic pointer values. Used to specify other invalid conditions aside
97 * from null.
98 */
99static Char	INVCHAR;
100Char    *INVPTR = &INVCHAR;
101Char    **INVPPTR = &INVPTR;
102
103static int    fast = 0;
104static int    mflag = 0;
105static int    prompt = 1;
106int     enterhist = 0;
107int    tellwhat = 0;
108time_t  t_period;
109Char  *ffile = NULL;
110int	dolzero = 0;
111int	insource = 0;
112int	exitset = 0;
113static time_t  chktim;		/* Time mail last checked */
114char *progname;
115int tcsh;
116
117/*
118 * This preserves the input state of the shell. It is used by
119 * st_save and st_restore to manupulate shell state.
120 */
121struct saved_state {
122    int		  insource;
123    int		  OLDSTD;
124    int		  SHIN;
125    int		  SHOUT;
126    int		  SHDIAG;
127    int		  intty;
128    struct whyle *whyles;
129    Char 	 *gointr;
130    Char 	 *arginp;
131    Char	 *evalp;
132    Char	**evalvec;
133    Char	 *alvecp;
134    Char	**alvec;
135    int		  onelflg;
136    int	  enterhist;
137    Char	**argv;
138    Char	**av;
139    Char	  HIST;
140    int	  cantell;
141    struct Bin	  B;
142    int		  justpr;
143};
144
145static	int		  srccat	(Char *, Char *);
146#ifndef WINNT_NATIVE
147static	int		  srcfile	(const char *, int, int, Char **);
148#else
149int		  srcfile	(const char *, int, int, Char **);
150#endif /*WINNT_NATIVE*/
151static	void		  srcunit	(int, int, int, Char **);
152static	void		  mailchk	(void);
153#ifndef _PATH_DEFPATH
154static	Char	 	**defaultpath	(void);
155#endif
156static	void		  record	(void);
157static	void		  st_save	(struct saved_state *, int, int,
158					 Char **, Char **);
159static	void		  st_restore	(void *);
160
161	int		  main		(int, char **);
162
163#ifndef LOCALEDIR
164#define LOCALEDIR "/usr/share/locale"
165#endif
166
167#ifdef NLS_CATALOGS
168static void
169add_localedir_to_nlspath(const char *path)
170{
171    static const char msgs_LOC[] = "/%L/LC_MESSAGES/%N.cat";
172    static const char msgs_lang[] = "/%l/LC_MESSAGES/%N.cat";
173    char *old;
174    char *new, *new_p;
175    size_t len;
176    int add_LOC = 1;
177    int add_lang = 1;
178    char trypath[MAXPATHLEN];
179    struct stat st;
180
181    if (path == NULL)
182        return;
183
184    (void) xsnprintf(trypath, sizeof(trypath), "%s/en/LC_MESSAGES/tcsh.cat",
185	path);
186    if (stat(trypath, &st) == -1)
187	return;
188
189    if ((old = getenv("NLSPATH")) != NULL)
190        len = strlen(old) + 1;	/* don't forget the colon. */
191    else
192	len = 0;
193
194    len += 2 * strlen(path) +
195	   sizeof(msgs_LOC) + sizeof(msgs_lang); /* includes the extra colon */
196
197    new = new_p = xcalloc(len, 1);
198
199    if (old != NULL) {
200	size_t pathlen = strlen(path);
201	char *old_p;
202
203	(void) xsnprintf(new_p, len, "%s", old);
204	new_p += strlen(new_p);
205	len -= new_p - new;
206
207	/* Check if the paths we try to add are already present in NLSPATH.
208	   If so, note it by setting the appropriate flag to 0. */
209	for (old_p = old; old_p; old_p = strchr(old_p, ':'),
210				 old_p = old_p ? old_p + 1 : NULL) {
211	    if (strncmp(old_p, path, pathlen) != 0)
212	    	continue;
213	    if (strncmp(old_p + pathlen, msgs_LOC, sizeof(msgs_LOC) - 1) == 0)
214		add_LOC = 0;
215	    else if (strncmp(old_p + pathlen, msgs_lang,
216			      sizeof(msgs_lang) - 1) == 0)
217		add_lang = 0;
218	}
219    }
220
221    /* Add the message catalog paths not already present to NLSPATH. */
222    if (add_LOC || add_lang)
223	(void) xsnprintf(new_p, len, "%s%s%s%s%s%s",
224			 old ? ":" : "",
225			 add_LOC ? path : "", add_LOC ? msgs_LOC : "",
226			 add_LOC && add_lang ? ":" : "",
227			 add_lang ? path : "", add_lang ? msgs_lang : "");
228
229    tsetenv(STRNLSPATH, str2short(new));
230    free(new);
231}
232#endif
233
234int
235main(int argc, char **argv)
236{
237    int batch = 0;
238    volatile int nexececho = 0;
239    int nofile = 0;
240    volatile int nverbose = 0;
241    volatile int rdirs = 0;
242    int quitit = 0;
243    Char *cp;
244#ifdef AUTOLOGOUT
245    Char *cp2;
246#endif
247    char *tcp, *ttyn;
248    int f, reenter;
249    char **tempv;
250    int osetintr;
251    struct sigaction oparintr;
252
253    (void)memset(&reslab, 0, sizeof(reslab));
254#ifdef WINNT_NATIVE
255    nt_init();
256#endif /* WINNT_NATIVE */
257#if defined(NLS_CATALOGS) && defined(LC_MESSAGES)
258    (void) setlocale(LC_MESSAGES, "");
259#endif /* NLS_CATALOGS && LC_MESSAGES */
260
261#ifdef NLS
262# ifdef LC_CTYPE
263    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
264# endif /* LC_CTYPE */
265#endif /* NLS */
266
267    STR_environ = blk2short(environ);
268    environ = short2blk(STR_environ);	/* So that we can free it */
269
270#ifdef NLS_CATALOGS
271    add_localedir_to_nlspath(LOCALEDIR);
272#endif
273
274    nlsinit();
275
276#ifdef MALLOC_TRACE
277    mal_setstatsfile(fdopen(dmove(xopen("/tmp/tcsh.trace",
278	O_WRONLY|O_CREAT|O_LARGEFILE, 0666), 25), "w"));
279    mal_trace(1);
280#endif /* MALLOC_TRACE */
281
282#if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX)
283# ifdef _SC_CLK_TCK
284    clk_tck = (clock_t) sysconf(_SC_CLK_TCK);
285# else /* ! _SC_CLK_TCK */
286#  ifdef CLK_TCK
287    clk_tck = CLK_TCK;
288#  else /* !CLK_TCK */
289    clk_tck = HZ;
290#  endif /* CLK_TCK */
291# endif /* _SC_CLK_TCK */
292#endif /* !BSDTIMES && POSIX */
293
294    settimes();			/* Immed. estab. timing base */
295#ifdef TESLA
296    do_logout = 0;
297#endif /* TESLA */
298
299    /*
300     * Make sure we have 0, 1, 2 open
301     * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr)
302     */
303    {
304	do
305	    if ((f = xopen(_PATH_DEVNULL, O_RDONLY|O_LARGEFILE)) == -1 &&
306		(f = xopen("/", O_RDONLY|O_LARGEFILE)) == -1)
307		exit(1);
308	while (f < 3);
309	xclose(f);
310    }
311
312    osinit();			/* Os dependent initialization */
313
314
315    {
316	char *t;
317
318	t = strrchr(argv[0], '/');
319#ifdef WINNT_NATIVE
320	{
321	    char *s = strrchr(argv[0], '\\');
322	    if (s)
323		t = s;
324	}
325#endif /* WINNT_NATIVE */
326	t = t ? t + 1 : argv[0];
327	if (*t == '-') t++;
328	progname = strsave((t && *t) ? t : tcshstr);    /* never want a null */
329	tcsh = strncmp(progname, tcshstr, sizeof(tcshstr) - 1) == 0;
330    }
331
332    /*
333     * Initialize non constant strings
334     */
335#ifdef _PATH_BSHELL
336    STR_BSHELL = SAVE(_PATH_BSHELL);
337#endif
338#ifdef _PATH_TCSHELL
339    STR_SHELLPATH = SAVE(_PATH_TCSHELL);
340#else
341# ifdef _PATH_CSHELL
342    STR_SHELLPATH = SAVE(_PATH_CSHELL);
343# endif
344#endif
345    STR_WORD_CHARS = SAVE(WORD_CHARS);
346
347    HIST = '!';
348    HISTSUB = '^';
349    PRCH = tcsh ? '>' : '%';	/* to replace %# in $prompt for normal users */
350    PRCHROOT = '#';		/* likewise for root */
351    word_chars = STR_WORD_CHARS;
352    bslash_quote = 0;		/* PWP: do tcsh-style backslash quoting? */
353    anyerror = 1;		/* for compatibility */
354    setcopy(STRanyerror, STRNULL, VAR_READWRITE);
355
356    /* Default history size to 100 */
357    setcopy(STRhistory, str2short("100"), VAR_READWRITE);
358
359    tempv = argv;
360    ffile = SAVE(tempv[0]);
361    dolzero = 0;
362    if (eq(ffile, STRaout))	/* A.out's are quittable */
363	quitit = 1;
364    uid = getuid();
365    gid = getgid();
366    euid = geteuid();
367    egid = getegid();
368    /*
369     * We are a login shell if: 1. we were invoked as -<something> with
370     * optional arguments 2. or we were invoked only with the -l flag
371     */
372    loginsh = (**tempv == '-') || (argc == 2 &&
373				   tempv[1][0] == '-' && tempv[1][1] == 'l' &&
374						tempv[1][2] == '\0');
375#ifdef _VMS_POSIX
376    /* No better way to find if we are a login shell */
377    if (!loginsh) {
378	loginsh = (argc == 1 && getppid() == 1);
379	**tempv = '-';	/* Avoid giving VMS an acidic stomach */
380    }
381#endif /* _VMS_POSIX */
382
383    if (loginsh && **tempv != '-') {
384	char *argv0;
385
386	/*
387	 * Mangle the argv space
388	 */
389	tempv[1][0] = '\0';
390	tempv[1][1] = '\0';
391	tempv[1] = NULL;
392	argv0 = strspl("-", *tempv);
393	*tempv = argv0;
394	argc--;
395    }
396    if (loginsh) {
397	(void) time(&chktim);
398	setNS(STRloginsh);
399    }
400
401    NoNLSRebind = getenv("NOREBIND") != NULL;
402#ifdef NLS
403# ifdef SETLOCALEBUG
404    dont_free = 1;
405# endif /* SETLOCALEBUG */
406    (void) setlocale(LC_ALL, "");
407# ifdef LC_COLLATE
408    (void) setlocale(LC_COLLATE, "");
409# endif
410# ifdef SETLOCALEBUG
411    dont_free = 0;
412# endif /* SETLOCALEBUG */
413# ifdef STRCOLLBUG
414    fix_strcoll_bug();
415# endif /* STRCOLLBUG */
416
417    /*
418     * On solaris ISO8859-1 contains no printable characters in the upper half
419     * so we need to test only for MB_CUR_MAX == 1, otherwise for multi-byte
420     * locales we are always AsciiOnly == 0.
421     */
422    if (MB_CUR_MAX == 1) {
423	int     k;
424
425	for (k = 0200; k <= 0377 && !isprint(CTL_ESC(k)); k++)
426	    continue;
427	AsciiOnly = k > 0377;
428    } else
429	AsciiOnly = 0;
430#else
431    AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
432#endif				/* NLS */
433    if (MapsAreInited && !NLSMapsAreInited)
434	ed_InitNLSMaps();
435    ResetArrowKeys();
436
437    /*
438     * Initialize for periodic command intervals. Also, initialize the dummy
439     * tty list for login-watch.
440     */
441    (void) time(&t_period);
442#ifndef HAVENOUTMP
443    initwatch();
444#endif /* !HAVENOUTMP */
445
446#if defined(alliant)
447    /*
448     * From:  Jim Pace <jdp@research.att.com>
449     * tcsh does not work properly on the alliants through an rlogin session.
450     * The shell generally hangs.  Also, reference to the controlling terminal
451     * does not work ( ie: echo foo > /dev/tty ).
452     *
453     * A security feature was added to rlogind affecting FX/80's Concentrix
454     * from revision 5.5.xx upwards (through 5.7 where this fix was implemented)
455     * This security change also affects the FX/2800 series.
456     * The security change to rlogind requires the process group of an rlogin
457     * session become disassociated with the tty in rlogind.
458     *
459     * The changes needed are:
460     * 1. set the process group
461     * 2. reenable the control terminal
462     */
463     if (loginsh && isatty(SHIN)) {
464	 ttyn = ttyname(SHIN);
465	 xclose(SHIN);
466	 SHIN = xopen(ttyn, O_RDWR|O_LARGEFILE);
467	 shpgrp = getpid();
468	 (void) ioctl (SHIN, TIOCSPGRP, (ioctl_t) &shpgrp);
469	 (void) setpgid(0, shpgrp);
470     }
471#endif /* alliant */
472
473    /*
474     * Move the descriptors to safe places. The variable didfds is 0 while we
475     * have only FSH* to work with. When didfds is true, we have 0,1,2 and
476     * prefer to use these.
477     */
478    initdesc();
479
480    /*
481     * Get and set the tty now
482     */
483    if ((ttyn = ttyname(SHIN)) != NULL) {
484	/*
485	 * Could use rindex to get rid of other possible path components, but
486	 * hpux preserves the subdirectory /pty/ when storing the tty name in
487	 * utmp, so we keep it too.
488	 */
489	if (strncmp(ttyn, "/dev/", 5) == 0)
490	    setv(STRtty, cp = SAVE(ttyn + 5), VAR_READWRITE);
491	else
492	    setv(STRtty, cp = SAVE(ttyn), VAR_READWRITE);
493    }
494    else
495	setv(STRtty, cp = SAVE(""), VAR_READWRITE);
496    /*
497     * Initialize the shell variables. ARGV and PROMPT are initialized later.
498     * STATUS is also munged in several places. CHILD is munged when
499     * forking/waiting
500     */
501
502    /*
503     * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and
504     * on shells running as root.  Out of these, autologout should NOT be set
505     * for any psudo-terminals (this catches most window systems) and not for
506     * any terminal running X windows.
507     *
508     * At Ohio State, we have had problems with a user having his X session
509     * drop out from under him (on a Sun) because the shell in his master
510     * xterm timed out and exited.
511     *
512     * Really, this should be done with a program external to the shell, that
513     * watches for no activity (and NO running programs, such as dump) on a
514     * terminal for a long peroid of time, and then SIGHUPS the shell on that
515     * terminal.
516     *
517     * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things
518     * allways first check to see if loginsh or really root, then do things
519     * with ttyname()
520     *
521     * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the
522     * value of cp before using it! ("root can rsh too")
523     *
524     * PWP: keep the nested ifs; the order of the tests matters and a good
525     * (smart) C compiler might re-arange things wrong.
526     */
527#ifdef AUTOLOGOUT
528# ifdef convex
529    if (uid == 0)
530	/*  root always has a 15 minute autologout  */
531	setcopy(STRautologout, STRrootdefautologout, VAR_READWRITE);
532    else
533	if (loginsh)
534	    /*  users get autologout set to 0  */
535	    setcopy(STRautologout, STR0, VAR_READWRITE);
536# else /* convex */
537    if (loginsh || (uid == 0)) {
538	if (*cp) {
539	    /* only for login shells or root and we must have a tty */
540	    if (((cp2 = Strrchr(cp, (Char) '/')) != NULL) &&
541		(Strncmp(cp, STRptssl, 3) != 0)) {
542		cp2 = cp2 + 1;
543	    }
544	    else
545		cp2 = cp;
546	    if (!(((Strncmp(cp2, STRtty, 3) == 0) && Isalpha(cp2[3])) ||
547	          Strstr(cp, STRptssl) != NULL)) {
548		if (getenv("DISPLAY") == NULL) {
549		    /* NOT on X window shells */
550		    setcopy(STRautologout, STRdefautologout, VAR_READWRITE);
551		}
552	    }
553	}
554    }
555# endif /* convex */
556#endif /* AUTOLOGOUT */
557
558    sigset_interrupting(SIGALRM, queue_alrmcatch);
559
560    setcopy(STRstatus, STR0, VAR_READWRITE);
561
562    /*
563     * get and set machine specific environment variables
564     */
565    getmachine();
566
567
568    /*
569     * Publish the selected echo style
570     */
571#if ECHO_STYLE != BSD_ECHO
572    if (tcsh) {
573# if ECHO_STYLE == NONE_ECHO
574	setcopy(STRecho_style, STRnone, VAR_READWRITE);
575# endif /* ECHO_STYLE == NONE_ECHO */
576# if ECHO_STYLE == SYSV_ECHO
577	setcopy(STRecho_style, STRsysv, VAR_READWRITE);
578# endif /* ECHO_STYLE == SYSV_ECHO */
579# if ECHO_STYLE == BOTH_ECHO
580	setcopy(STRecho_style, STRboth, VAR_READWRITE);
581# endif /* ECHO_STYLE == BOTH_ECHO */
582    } else
583#endif /* ECHO_STYLE != BSD_ECHO */
584	setcopy(STRecho_style, STRbsd, VAR_READWRITE);
585
586    /*
587     * increment the shell level.
588     */
589    shlvl(1);
590
591    if ((tcp = getenv("HOME")) != NULL)
592	cp = quote(SAVE(tcp));
593    else
594#ifdef __ANDROID__
595	/* On Android, $HOME usually isn't set, so we can't load user RC files.
596	   Check for the environment variable EXTERNAL_STORAGE, which contains
597	   the mount point of the external storage (SD card, mostly).  If
598	   EXTERNAL_STORAGE isn't set fall back to "/sdcard". */
599    if ((tcp = getenv("EXTERNAL_STORAGE")) != NULL)
600	cp = quote(SAVE(tcp));
601    else
602	cp = quote(SAVE("/sdcard"));
603#else
604	cp = NULL;
605#endif
606
607    if (cp == NULL)
608	fast = 1;		/* No home -> can't read scripts */
609    else
610	setv(STRhome, cp, VAR_READWRITE);
611
612    dinit(cp);			/* dinit thinks that HOME == cwd in a login
613				 * shell */
614    /*
615     * Grab other useful things from the environment. Should we grab
616     * everything??
617     */
618    {
619	char *cln, *cus, *cgr;
620	struct passwd *pw;
621	struct group *gr;
622
623
624#ifdef apollo
625	int     oid = getoid();
626
627	setv(STRoid, Itoa(oid, 0, 0), VAR_READWRITE);
628#endif /* apollo */
629
630	setv(STReuid, Itoa(euid, 0, 0), VAR_READWRITE);
631	if ((pw = xgetpwuid(euid)) == NULL)
632	    setcopy(STReuser, STRunknown, VAR_READWRITE);
633	else
634	    setcopy(STReuser, str2short(pw->pw_name), VAR_READWRITE);
635
636	setv(STRuid, Itoa(uid, 0, 0), VAR_READWRITE);
637
638	setv(STRgid, Itoa(gid, 0, 0), VAR_READWRITE);
639
640	cln = getenv("LOGNAME");
641	cus = getenv("USER");
642	if (cus != NULL)
643	    setv(STRuser, quote(SAVE(cus)), VAR_READWRITE);
644	else if (cln != NULL)
645	    setv(STRuser, quote(SAVE(cln)), VAR_READWRITE);
646	else if ((pw = xgetpwuid(uid)) == NULL)
647	    setcopy(STRuser, STRunknown, VAR_READWRITE);
648	else
649	    setcopy(STRuser, str2short(pw->pw_name), VAR_READWRITE);
650	if (cln == NULL)
651	    tsetenv(STRLOGNAME, varval(STRuser));
652	if (cus == NULL)
653	    tsetenv(STRKUSER, varval(STRuser));
654
655	cgr = getenv("GROUP");
656	if (cgr != NULL)
657	    setv(STRgroup, quote(SAVE(cgr)), VAR_READWRITE);
658	else if ((gr = xgetgrgid(gid)) == NULL)
659	    setcopy(STRgroup, STRunknown, VAR_READWRITE);
660	else
661	    setcopy(STRgroup, str2short(gr->gr_name), VAR_READWRITE);
662	if (cgr == NULL)
663	    tsetenv(STRKGROUP, varval(STRgroup));
664    }
665
666    /*
667     * HOST may be wrong, since rexd transports the entire environment on sun
668     * 3.x Just set it again
669     */
670    {
671	char    cbuff[MAXHOSTNAMELEN];
672
673	if (gethostname(cbuff, sizeof(cbuff)) >= 0) {
674	    cbuff[sizeof(cbuff) - 1] = '\0';	/* just in case */
675	    tsetenv(STRHOST, str2short(cbuff));
676	}
677	else
678	    tsetenv(STRHOST, STRunknown);
679    }
680
681
682#ifdef REMOTEHOST
683    /*
684     * Try to determine the remote host we were logged in from.
685     */
686    remotehost();
687#endif /* REMOTEHOST */
688
689#ifdef apollo
690    if ((tcp = getenv("SYSTYPE")) == NULL)
691	tcp = "bsd4.3";
692    tsetenv(STRSYSTYPE, quote(str2short(tcp)));
693#endif /* apollo */
694
695    /*
696     * set editing on by default, unless running under Emacs as an inferior
697     * shell.
698     * We try to do this intelligently. If $TERM is available, then it
699     * should determine if we should edit or not. $TERM is preserved
700     * across rlogin sessions, so we will not get confused if we rlogin
701     * under an emacs shell. Another advantage is that if we run an
702     * xterm under an emacs shell, then the $TERM will be set to
703     * xterm, so we are going to want to edit. Unfortunately emacs
704     * does not restore all the tty modes, so xterm is not very well
705     * set up. But this is not the shell's fault.
706     * Also don't edit if $TERM == wm, for when we're running under an ATK app.
707     * Finally, emacs compiled under terminfo, sets the terminal to dumb,
708     * so disable editing for that too.
709     *
710     * Unfortunately, in some cases the initial $TERM setting is "unknown",
711     * "dumb", or "network" which is then changed in the user's startup files.
712     * We fix this by setting noediting here if $TERM is unknown/dumb and
713     * if noediting is set, we switch on editing if $TERM is changed.
714     */
715    if ((tcp = getenv("TERM")) != NULL) {
716	setv(STRterm, quote(SAVE(tcp)), VAR_READWRITE);
717	noediting = strcmp(tcp, "unknown") == 0 || strcmp(tcp, "dumb") == 0 ||
718		    strcmp(tcp, "network") == 0;
719	editing = strcmp(tcp, "emacs") != 0 && strcmp(tcp, "wm") != 0 &&
720		  !noediting;
721    }
722    else {
723	noediting = 0;
724	editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0);
725    }
726
727    /*
728     * The 'edit' variable is either set or unset.  It doesn't
729     * need a value.  Making it 'emacs' might be confusing.
730     */
731    if (editing)
732	setNS(STRedit);
733
734
735    /*
736     * still more mutability: make the complete routine automatically add the
737     * suffix of file names...
738     */
739    setNS(STRaddsuffix);
740
741    /*
742     * Compatibility with tcsh >= 6.12 by default
743     */
744    setNS(STRcsubstnonl);
745
746    /*
747     * Random default kill ring size
748     */
749    setcopy(STRkillring, str2short("30"), VAR_READWRITE);
750
751    /*
752     * Re-initialize path if set in environment
753     */
754    if ((tcp = getenv("PATH")) == NULL)
755#ifdef _PATH_DEFPATH
756	importpath(str2short(_PATH_DEFPATH));
757#else /* !_PATH_DEFPATH */
758	setq(STRpath, defaultpath(), &shvhed, VAR_READWRITE);
759#endif /* _PATH_DEFPATH */
760    else
761	/* Importpath() allocates memory for the path, and the
762	 * returned pointer from SAVE() was discarded, so
763	 * this was a memory leak.. (sg)
764	 *
765	 * importpath(SAVE(tcp));
766	 */
767	importpath(str2short(tcp));
768
769
770    {
771	/* If the SHELL environment variable ends with "tcsh", set
772	 * STRshell to the same path.  This is to facilitate using
773	 * the executable in environments where the compiled-in
774	 * default isn't appropriate (sg).
775	 */
776
777	size_t sh_len = 0;
778
779	if ((tcp = getenv("SHELL")) != NULL) {
780	    sh_len = strlen(tcp);
781	    if ((sh_len >= 5 && strcmp(tcp + (sh_len - 5), "/tcsh") == 0) ||
782	        (!tcsh && sh_len >= 4 && strcmp(tcp + (sh_len - 4), "/csh") == 0))
783		setv(STRshell, quote(SAVE(tcp)), VAR_READWRITE);
784	    else
785		sh_len = 0;
786	}
787	if (sh_len == 0)
788	    setcopy(STRshell, STR_SHELLPATH, VAR_READWRITE);
789    }
790
791#ifdef _OSD_POSIX  /* BS2000 needs this variable set to "SHELL" */
792    if ((tcp = getenv("PROGRAM_ENVIRONMENT")) == NULL)
793	tcp = "SHELL";
794    tsetenv(STRPROGRAM_ENVIRONMENT, quote(str2short(tcp)));
795#endif /* _OSD_POSIX */
796
797#ifdef COLOR_LS_F
798    if ((tcp = getenv("LS_COLORS")) != NULL)
799	parseLS_COLORS(str2short(tcp));
800#endif /* COLOR_LS_F */
801
802    doldol = putn((tcsh_number_t)getpid());	/* For $$ */
803#ifdef WINNT_NATIVE
804    {
805	char *tmp;
806	Char *tmp2;
807	if ((tmp = getenv("TMP")) != NULL) {
808	    tmp = xasprintf("%s/%s", tmp, "sh");
809	    tmp2 = SAVE(tmp);
810	    xfree(tmp);
811	}
812	else {
813	    tmp2 = SAVE("");
814	}
815	shtemp = Strspl(tmp2, doldol);	/* For << */
816	xfree(tmp2);
817    }
818#else /* !WINNT_NATIVE */
819#ifdef HAVE_MKSTEMP
820    {
821	char *tmpdir = getenv ("TMPDIR");
822	if (!tmpdir)
823	    tmpdir = "/tmp";
824	shtemp = Strspl(SAVE(tmpdir), SAVE("/sh" TMP_TEMPLATE)); /* For << */
825    }
826#else /* !HAVE_MKSTEMP */
827    shtemp = Strspl(STRtmpsh, doldol);	/* For << */
828#endif /* HAVE_MKSTEMP */
829#endif /* WINNT_NATIVE */
830
831    /*
832     * Record the interrupt states from the parent process. If the parent is
833     * non-interruptible our hand must be forced or we (and our children) won't
834     * be either. Our children inherit termination from our parent. We catch it
835     * only if we are the login shell.
836     */
837    sigaction(SIGINT, NULL, &parintr);
838    sigaction(SIGTERM, NULL, &parterm);
839
840
841#ifdef TCF
842    /* Enable process migration on ourselves and our progeny */
843    (void) signal(SIGMIGRATE, SIG_DFL);
844#endif /* TCF */
845
846    /*
847     * dspkanji/dspmbyte autosetting
848     */
849    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
850#if defined(DSPMBYTE)
851#if defined(NLS) && defined(LC_CTYPE)
852    if (((tcp = setlocale(LC_CTYPE, NULL)) != NULL || (tcp = getenv("LANG")) != NULL) && !adrof(CHECK_MBYTEVAR))
853#else
854    if ((tcp = getenv("LANG")) != NULL && !adrof(CHECK_MBYTEVAR))
855#endif
856    {
857	autoset_dspmbyte(str2short(tcp));
858    }
859#if defined(WINNT_NATIVE)
860    else if (!adrof(CHECK_MBYTEVAR))
861      nt_autoset_dspmbyte();
862#endif /* WINNT_NATIVE */
863#endif
864#if defined(AUTOSET_KANJI)
865# if defined(NLS) && defined(LC_CTYPE)
866    if (setlocale(LC_CTYPE, NULL) != NULL || getenv("LANG") != NULL)
867# else
868    if (getenv("LANG") != NULL)
869# endif
870	autoset_kanji();
871#endif /* AUTOSET_KANJI */
872    fix_version();		/* publish the shell version */
873
874    if (argc > 1 && strcmp(argv[1], "--version") == 0) {
875	xprintf("%S\n", varval(STRversion));
876	xexit(0);
877    }
878    if (argc > 1 && strcmp(argv[1], "--help") == 0) {
879	xprintf("%S\n\n", varval(STRversion));
880	xprintf("%s", CGETS(11, 8, HELP_STRING));
881	xexit(0);
882    }
883    /*
884     * Process the arguments.
885     *
886     * Note that processing of -v/-x is actually delayed till after script
887     * processing.
888     *
889     * We set the first character of our name to be '-' if we are a shell
890     * running interruptible commands.  Many programs which examine ps'es
891     * use this to filter such shells out.
892     */
893    argc--, tempv++;
894    while (argc > 0 && (tcp = tempv[0])[0] == '-' &&
895	   *++tcp != '\0' && !batch) {
896	do
897	    switch (*tcp++) {
898
899	    case 0:		/* -	Interruptible, no prompt */
900		prompt = 0;
901		setintr = 1;
902		nofile = 1;
903		break;
904
905	    case 'b':		/* -b	Next arg is input file */
906		batch = 1;
907		break;
908
909	    case 'c':		/* -c	Command input from arg */
910		if (argc == 1)
911		    xexit(0);
912		argc--, tempv++;
913#ifdef M_XENIX
914		/* Xenix Vi bug:
915		   it relies on a 7 bit environment (/bin/sh), so it
916		   pass ascii arguments with the 8th bit set */
917		if (!strcmp(argv[0], "sh"))
918		  {
919		    char *p;
920
921		    for (p = tempv[0]; *p; ++p)
922		      *p &= ASCII;
923		  }
924#endif
925		arginp = SAVE(tempv[0]);
926
927		/*
928		 * we put the command into a variable
929		 */
930		if (arginp != NULL)
931		    setv(STRcommand, quote(Strsave(arginp)), VAR_READWRITE);
932
933		/*
934		 * * Give an error on -c arguments that end in * backslash to
935		 * ensure that you don't make * nonportable csh scripts.
936		 */
937		{
938		    int count;
939
940		    cp = Strend(arginp);
941		    count = 0;
942		    while (cp > arginp && *--cp == '\\')
943			++count;
944		    if ((count & 1) != 0) {
945			exiterr = 1;
946			stderror(ERR_ARGC);
947		    }
948		}
949		prompt = 0;
950		nofile = 1;
951		break;
952	    case 'd':		/* -d	Load directory stack from file */
953		rdirs = 1;
954		break;
955
956#ifdef apollo
957	    case 'D':		/* -D	Define environment variable */
958		{
959		    Char *dp;
960
961		    cp = str2short(tcp);
962		    if (dp = Strchr(cp, '=')) {
963			*dp++ = '\0';
964			tsetenv(cp, dp);
965		    }
966		    else
967			tsetenv(cp, STRNULL);
968		}
969		*tcp = '\0'; 	/* done with this argument */
970		break;
971#endif /* apollo */
972
973	    case 'e':		/* -e	Exit on any error */
974		exiterr = 1;
975		break;
976
977	    case 'f':		/* -f	Fast start */
978		fast = 1;
979		break;
980
981	    case 'i':		/* -i	Interactive, even if !intty */
982		intact = 1;
983		nofile = 1;
984		break;
985
986	    case 'm':		/* -m	read .cshrc (from su) */
987		mflag = 1;
988		break;
989
990	    case 'n':		/* -n	Don't execute */
991		noexec = 1;
992		break;
993
994	    case 'q':		/* -q	(Undoc'd) ... die on quit */
995		quitit = 1;
996		break;
997
998	    case 's':		/* -s	Read from std input */
999		nofile = 1;
1000		break;
1001
1002	    case 't':		/* -t	Read one line from input */
1003		onelflg = 2;
1004		prompt = 0;
1005		nofile = 1;
1006		break;
1007
1008	    case 'v':		/* -v	Echo hist expanded input */
1009		nverbose = 1;	/* ... later */
1010		break;
1011
1012	    case 'x':		/* -x	Echo just before execution */
1013		nexececho = 1;	/* ... later */
1014		break;
1015
1016	    case 'V':		/* -V	Echo hist expanded input */
1017		setNS(STRverbose);	/* NOW! */
1018		break;
1019
1020	    case 'X':		/* -X	Echo just before execution */
1021		setNS(STRecho);	/* NOW! */
1022		break;
1023
1024	    case 'F':
1025		/*
1026		 * This will cause children to be created using fork instead of
1027		 * vfork.
1028		 */
1029		use_fork = 1;
1030		break;
1031
1032	    case ' ':
1033	    case '\t':
1034	    case '\r':
1035	    case '\n':
1036		/*
1037		 * for O/S's that don't do the argument parsing right in
1038		 * "#!/foo -f " scripts
1039		 */
1040		break;
1041
1042	    default:		/* Unknown command option */
1043		exiterr = 1;
1044		stderror(ERR_TCSHUSAGE, tcp-1, progname);
1045		break;
1046
1047	} while (*tcp);
1048	tempv++, argc--;
1049    }
1050
1051    if (quitit)			/* With all due haste, for debugging */
1052	(void) signal(SIGQUIT, SIG_DFL);
1053
1054    /*
1055     * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
1056     * arguments the first of them is the name of a shell file from which to
1057     * read commands.
1058     */
1059    if (nofile == 0 && argc > 0) {
1060	nofile = xopen(tempv[0], O_RDONLY|O_LARGEFILE);
1061	if (nofile < 0) {
1062	    child = 1;		/* So this ... */
1063	    /* ... doesn't return */
1064	    stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1065	}
1066	xfree(ffile);
1067	dolzero = 1;
1068	ffile = SAVE(tempv[0]);
1069	/*
1070	 * Replace FSHIN. Handle /dev/std{in,out,err} specially
1071	 * since once they are closed we cannot open them again.
1072	 * In that case we use our own saved descriptors
1073	 */
1074	if ((SHIN = dmove(nofile, FSHIN)) < 0)
1075	    switch(nofile) {
1076	    case 0:
1077		SHIN = FSHIN;
1078		break;
1079	    case 1:
1080		SHIN = FSHOUT;
1081		break;
1082	    case 2:
1083		SHIN = FSHDIAG;
1084		break;
1085	    default:
1086		stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1087		break;
1088	    }
1089	(void) close_on_exec(SHIN, 1);
1090	prompt = 0;
1091	 /* argc not used any more */ tempv++;
1092    }
1093
1094    /*
1095     * Call to closem() used to be part of initdesc(). Now called below where
1096     * the script name argument has become stdin. Kernel may have used a file
1097     * descriptor to hold the name of the script (setuid case) and this name
1098     * mustn't be lost by closing the fd too soon.
1099     */
1100    closem();
1101
1102    /*
1103     * Consider input a tty if it really is or we are interactive. but not for
1104     * editing (christos)
1105     */
1106    if (!(intty = isatty(SHIN))) {
1107	if (adrof(STRedit))
1108	    unsetv(STRedit);
1109	editing = 0;
1110    }
1111    intty |= intact;
1112#ifndef convex
1113    if (intty || (intact && isatty(SHOUT))) {
1114	if (!batch && (uid != euid || gid != egid)) {
1115	    errno = EACCES;
1116	    child = 1;		/* So this ... */
1117	    /* ... doesn't return */
1118	    stderror(ERR_SYSTEM, progname, strerror(errno));
1119	}
1120    }
1121#endif /* convex */
1122    isoutatty = isatty(SHOUT);
1123    isdiagatty = isatty(SHDIAG);
1124    /*
1125     * Decide whether we should play with signals or not. If we are explicitly
1126     * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
1127     * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
1128     * Note that in only the login shell is it likely that parent may have set
1129     * signals to be ignored
1130     */
1131    if (loginsh || intact || (intty && isatty(SHOUT)))
1132	setintr = 1;
1133    settell();
1134    /*
1135     * Save the remaining arguments in argv.
1136     */
1137    setq(STRargv, blk2short(tempv), &shvhed, VAR_READWRITE);
1138
1139    /*
1140     * Set up the prompt.
1141     */
1142    if (prompt) {
1143	setcopy(STRprompt, STRdefprompt, VAR_READWRITE);
1144	/* that's a meta-questionmark */
1145	setcopy(STRprompt2, STRmquestion, VAR_READWRITE);
1146	setcopy(STRprompt3, STRKCORRECT, VAR_READWRITE);
1147    }
1148
1149    /*
1150     * If we are an interactive shell, then start fiddling with the signals;
1151     * this is a tricky game.
1152     */
1153    shpgrp = mygetpgrp();
1154    opgrp = tpgrp = -1;
1155    if (setintr) {
1156	struct sigaction osig;
1157
1158	**argv = '-';
1159	if (!quitit)		/* Wary! */
1160	    (void) signal(SIGQUIT, SIG_IGN);
1161	pintr_disabled = 1;
1162	sigset_interrupting(SIGINT, queue_pintr);
1163	(void) signal(SIGTERM, SIG_IGN);
1164
1165	/*
1166	 * No reason I can see not to save history on all these events..
1167	 * Most usual occurrence is in a window system, where we're not a login
1168	 * shell, but might as well be... (sg)
1169	 * But there might be races when lots of shells exit together...
1170	 * [this is also incompatible].
1171	 * We have to be mre careful here. If the parent wants to
1172	 * ignore the signals then we leave them untouched...
1173	 * We also only setup the handlers for shells that are trully
1174	 * interactive.
1175	 */
1176	sigaction(SIGHUP, NULL, &osig);
1177	if (loginsh || osig.sa_handler != SIG_IGN)
1178	    /* exit processing on HUP */
1179	    sigset_interrupting(SIGHUP, queue_phup);
1180#ifdef SIGXCPU
1181	sigaction(SIGXCPU, NULL, &osig);
1182	if (loginsh || osig.sa_handler != SIG_IGN)
1183	    /* exit processing on XCPU */
1184	    sigset_interrupting(SIGXCPU, queue_phup);
1185#endif
1186#ifdef SIGXFSZ
1187	sigaction(SIGXFSZ, NULL, &osig);
1188	if (loginsh || osig.sa_handler != SIG_IGN)
1189	    /* exit processing on XFSZ */
1190	    sigset_interrupting(SIGXFSZ, queue_phup);
1191#endif
1192
1193	if (quitit == 0 && arginp == 0) {
1194#ifdef SIGTSTP
1195	    (void) signal(SIGTSTP, SIG_IGN);
1196#endif
1197#ifdef SIGTTIN
1198	    (void) signal(SIGTTIN, SIG_IGN);
1199#endif
1200#ifdef SIGTTOU
1201	    (void) signal(SIGTTOU, SIG_IGN);
1202#endif
1203	    /*
1204	     * Wait till in foreground, in case someone stupidly runs csh &
1205	     * dont want to try to grab away the tty.
1206	     */
1207	    if (isatty(FSHDIAG))
1208		f = FSHDIAG;
1209	    else if (isatty(FSHOUT))
1210		f = FSHOUT;
1211	    else if (isatty(OLDSTD))
1212		f = OLDSTD;
1213	    else
1214		f = -1;
1215
1216#ifdef NeXT
1217	    /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */
1218	    if (shpgrp == 0) {
1219	        shpgrp = getpid();
1220		(void) setpgid(0, shpgrp);
1221	        (void) tcsetpgrp(f, shpgrp);
1222	    }
1223#endif /* NeXT */
1224#ifdef BSDJOBS			/* if we have tty job control */
1225	    if (grabpgrp(f, shpgrp) != -1) {
1226		/*
1227		 * Thanks to Matt Day for the POSIX references, and to
1228		 * Paul Close for the SGI clarification.
1229		 */
1230		if (setdisc(f) != -1) {
1231		    opgrp = shpgrp;
1232		    shpgrp = getpid();
1233		    tpgrp = shpgrp;
1234		    if (tcsetpgrp(f, shpgrp) == -1) {
1235			/*
1236			 * On hpux 7.03 this fails with EPERM. This happens on
1237			 * the 800 when opgrp != shpgrp at this point. (we were
1238			 * forked from a non job control shell)
1239			 * POSIX 7.2.4, says we failed because the process
1240			 * group specified did not belong to a process
1241			 * in the same session with the tty. So we set our
1242			 * process group and try again.
1243			 */
1244			if (setpgid(0, shpgrp) == -1) {
1245			    xprintf("setpgid:");
1246			    goto notty;
1247			}
1248			if (tcsetpgrp(f, shpgrp) == -1) {
1249			    xprintf("tcsetpgrp:");
1250			    goto notty;
1251			}
1252		    }
1253		    /*
1254		     * We check the process group now. If it is the same, then
1255		     * we don't need to set it again. On hpux 7.0 on the 300's
1256		     * if we set it again it fails with EPERM. This is the
1257		     * correct behavior according to POSIX 4.3.3 if the process
1258		     * was a session leader .
1259		     */
1260		    else if (shpgrp != mygetpgrp()) {
1261			if(setpgid(0, shpgrp) == -1) {
1262			    xprintf("setpgid:");
1263			    goto notty;
1264			}
1265		    }
1266#ifdef IRIS4D
1267		    /*
1268		     * But on irix 3.3 we need to set it again, even if it is
1269		     * the same. We do that to tell the system that we
1270		     * need BSD process group compatibility.
1271		     */
1272		    else
1273			(void) setpgid(0, shpgrp);
1274#endif
1275		    (void) close_on_exec(dcopy(f, FSHTTY), 1);
1276		}
1277		else
1278		    tpgrp = -1;
1279	    }
1280	    if (tpgrp == -1) {
1281	notty:
1282	        xprintf(CGETS(11, 1, "Warning: no access to tty (%s).\n"),
1283		    strerror(errno));
1284		xprintf("%s",
1285		    CGETS(11, 2, "Thus no job control in this shell.\n"));
1286		/*
1287		 * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't
1288		 * have access to tty, disable editing too
1289		 */
1290		if (adrof(STRedit))
1291		    unsetv(STRedit);
1292		editing = 0;
1293	    }
1294#else	/* BSDJOBS */		/* don't have job control, so frotz it */
1295	    tpgrp = -1;
1296#endif				/* BSDJOBS */
1297	}
1298    }
1299    if (setintr == 0 && parintr.sa_handler == SIG_DFL)
1300	setintr = 1;
1301
1302/*
1303 * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the
1304 * handler is installed with signal(2) or sigset(2).  sigaction(2) must
1305 * be used instead.
1306 *
1307 * David Dawes (dawes@physics.su.oz.au) Sept 1991
1308 */
1309    sigset_interrupting(SIGCHLD, queue_pchild);
1310
1311    if (intty && !arginp)
1312	(void) ed_Setup(editing);/* Get the tty state, and set defaults */
1313				 /* Only alter the tty state if editing */
1314
1315    /*
1316     * Set an exit here in case of an interrupt or error reading the shell
1317     * start-up scripts.
1318     */
1319    osetintr = setintr;
1320    oparintr = parintr;
1321    (void)cleanup_push_mark(); /* There is no outer handler */
1322    if (setexit() != 0) /* PWP */
1323	reenter = 1;
1324    else
1325	reenter = 0;
1326    exitset++;
1327    haderr = 0;			/* In case second time through */
1328    if (!fast && reenter == 0) {
1329	/* Will have varval(STRhome) here because set fast if don't */
1330	{
1331	    pintr_disabled++;
1332	    cleanup_push(&pintr_disabled, disabled_cleanup);
1333	    setintr = 0;/*FIXRESET:cleanup*/
1334	    /* onintr in /etc/ files has no effect */
1335	    parintr.sa_handler = SIG_IGN;/*FIXRESET: cleanup*/
1336#ifdef LOGINFIRST
1337#ifdef _PATH_DOTLOGIN
1338	    if (loginsh)
1339		(void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1340#endif
1341#endif
1342
1343#ifdef _PATH_DOTCSHRC
1344	    (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL);
1345#endif
1346	    if (!arginp && !onelflg && !havhash)
1347		dohash(NULL,NULL);
1348#ifndef LOGINFIRST
1349#ifdef _PATH_DOTLOGIN
1350	    if (loginsh)
1351		(void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1352#endif
1353#endif
1354	    cleanup_until(&pintr_disabled);
1355	    setintr = osetintr;
1356	    parintr = oparintr;
1357	}
1358#ifdef LOGINFIRST
1359	if (loginsh)
1360	    (void) srccat(varval(STRhome), STRsldotlogin);
1361#endif
1362	/* upward compat. */
1363	if (!srccat(varval(STRhome), STRsldottcshrc))
1364	    (void) srccat(varval(STRhome), STRsldotcshrc);
1365
1366	if (!arginp && !onelflg && !havhash)
1367	    dohash(NULL,NULL);
1368
1369	/*
1370	 * Source history before .login so that it is available in .login
1371	 */
1372	loadhist(NULL, 0);
1373#ifndef LOGINFIRST
1374	if (loginsh)
1375	    (void) srccat(varval(STRhome), STRsldotlogin);
1376#endif
1377	if (loginsh || rdirs)
1378	    loaddirs(NULL);
1379    }
1380    /* Reset interrupt flag */
1381    setintr = osetintr;
1382    parintr = oparintr;
1383    exitset--;
1384
1385    /* Initing AFTER .cshrc is the Right Way */
1386    if (intty && !arginp) {	/* PWP setup stuff */
1387	ed_Init();		/* init the new line editor */
1388#ifdef SIG_WINDOW
1389	check_window_size(1);	/* mung environment */
1390#endif				/* SIG_WINDOW */
1391    }
1392
1393    /*
1394     * Now are ready for the -v and -x flags
1395     */
1396    if (nverbose)
1397	setNS(STRverbose);
1398    if (nexececho)
1399	setNS(STRecho);
1400
1401    /*
1402     * All the rest of the world is inside this call. The argument to process
1403     * indicates whether it should catch "error unwinds".  Thus if we are a
1404     * interactive shell our call here will never return by being blown past on
1405     * an error.
1406     */
1407    process(setintr);
1408
1409    /*
1410     * Mop-up.
1411     */
1412    /* Take care of these (especially HUP) here instead of inside flush. */
1413    handle_pending_signals();
1414    if (intty) {
1415	if (loginsh) {
1416	    xprintf("logout\n");
1417	    xclose(SHIN);
1418	    child = 1;
1419#ifdef TESLA
1420	    do_logout = 1;
1421#endif				/* TESLA */
1422	    goodbye(NULL, NULL);
1423	}
1424	else {
1425	    xprintf("exit\n");
1426	}
1427    }
1428    record();
1429    exitstat();
1430    return (0);
1431}
1432
1433void
1434untty(void)
1435{
1436#ifdef BSDJOBS
1437    if (tpgrp > 0 && opgrp != shpgrp) {
1438	(void) setpgid(0, opgrp);
1439	(void) tcsetpgrp(FSHTTY, opgrp);
1440	(void) resetdisc(FSHTTY);
1441    }
1442#endif /* BSDJOBS */
1443}
1444
1445void
1446importpath(Char *cp)
1447{
1448    size_t i = 0;
1449    Char *dp;
1450    Char **pv;
1451    int     c;
1452
1453    for (dp = cp; *dp; dp++)
1454	if (*dp == PATHSEP)
1455	    i++;
1456    /*
1457     * i+2 where i is the number of colons in the path. There are i+1
1458     * directories in the path plus we need room for a zero terminator.
1459     */
1460    pv = xcalloc(i + 2, sizeof(Char *));
1461    dp = cp;
1462    i = 0;
1463    if (*dp)
1464	for (;;) {
1465	    if ((c = *dp) == PATHSEP || c == 0) {
1466		*dp = 0;
1467		pv[i++] = Strsave(*cp ? cp : STRdot);
1468		if (c) {
1469		    cp = dp + 1;
1470		    *dp = PATHSEP;
1471		}
1472		else
1473		    break;
1474	    }
1475#ifdef WINNT_NATIVE
1476	    else if (*dp == '\\')
1477		*dp = '/';
1478#endif /* WINNT_NATIVE */
1479	    dp++;
1480	}
1481    pv[i] = 0;
1482    cleanup_push(pv, blk_cleanup);
1483    setq(STRpath, pv, &shvhed, VAR_READWRITE);
1484    cleanup_ignore(pv);
1485    cleanup_until(pv);
1486}
1487
1488/*
1489 * Source to the file which is the catenation of the argument names.
1490 */
1491static int
1492srccat(Char *cp, Char *dp)
1493{
1494    if (cp[0] == '/' && cp[1] == '\0')
1495	return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
1496    else {
1497	Char *ep;
1498	char   *ptr;
1499	int rv;
1500
1501#ifdef WINNT_NATIVE
1502	ep = Strend(cp);
1503	if (ep != cp && ep[-1] == '/' && dp[0] == '/') /* silly win95 */
1504	    dp++;
1505#endif /* WINNT_NATIVE */
1506
1507	ep = Strspl(cp, dp);
1508	cleanup_push(ep, xfree);
1509	ptr = short2str(ep);
1510
1511	rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
1512	cleanup_until(ep);
1513	return rv;
1514    }
1515}
1516
1517/*
1518 * Source to a file putting the file descriptor in a safe place (> 2).
1519 */
1520#ifndef WINNT_NATIVE
1521static int
1522#else
1523int
1524#endif /*WINNT_NATIVE*/
1525srcfile(const char *f, int onlyown, int flag, Char **av)
1526{
1527    int unit;
1528
1529    if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1)
1530	return 0;
1531    cleanup_push(&unit, open_cleanup);
1532    unit = dmove(unit, -1);
1533    cleanup_ignore(&unit);
1534    cleanup_until(&unit);
1535
1536    (void) close_on_exec(unit, 1);
1537    srcunit(unit, onlyown, flag, av);
1538    return 1;
1539}
1540
1541
1542/*
1543 * Save the shell state, and establish new argument vector, and new input
1544 * fd.
1545 */
1546static void
1547st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
1548{
1549    st->insource	= insource;
1550    st->SHIN		= SHIN;
1551    /* Want to preserve the meaning of "source file >output".
1552     * Save old descriptors, move new 0,1,2 to safe places and assign
1553     * them to SH* and let process() redo 0,1,2 from them.
1554     *
1555     * The macro returns true if d1 and d2 are good and they point to
1556     * different things.  If you don't avoid saving duplicate
1557     * descriptors, you really limit the depth of "source" recursion
1558     * you can do because of all the open file descriptors.  -IAN!
1559     */
1560#define NEED_SAVE_FD(d1,d2) \
1561    (fstat(d1, &s1) != -1 && fstat(d2, &s2) != -1 \
1562	&& (s1.st_ino != s2.st_ino || s1.st_dev != s2.st_dev) )
1563
1564    st->OLDSTD = st->SHOUT = st->SHDIAG = -1;/* test later to restore these */
1565    if (didfds) {
1566	    struct stat s1, s2;
1567	    if (NEED_SAVE_FD(0,OLDSTD)) {
1568		    st->OLDSTD = OLDSTD;
1569		    OLDSTD = dmove(0, -1);
1570		    (void)close_on_exec(OLDSTD, 1);
1571	    }
1572	    if (NEED_SAVE_FD(1,SHOUT)) {
1573		    st->SHOUT = SHOUT;
1574		    SHOUT = dmove(1, -1);
1575		    (void)close_on_exec(SHOUT, 1);
1576	    }
1577	    if (NEED_SAVE_FD(2,SHDIAG)) {
1578		    st->SHDIAG = SHDIAG;
1579		    SHDIAG = dmove(2, -1);
1580		    (void)close_on_exec(SHDIAG, 1);
1581	    }
1582	    donefds();
1583    }
1584
1585    st->intty		= intty;
1586    st->whyles		= whyles;
1587    st->gointr		= gointr;
1588    st->arginp		= arginp;
1589    st->evalp		= evalp;
1590    st->evalvec		= evalvec;
1591    st->alvecp		= alvecp;
1592    st->alvec		= alvec;
1593    st->onelflg		= onelflg;
1594    st->enterhist	= enterhist;
1595    st->justpr		= justpr;
1596    if (hflg)
1597	st->HIST	= HIST;
1598    else
1599	st->HIST	= '\0';
1600    st->cantell		= cantell;
1601    cpybin(st->B, B);
1602
1603    /*
1604     * we can now pass arguments to source.
1605     * For compatibility we do that only if arguments were really
1606     * passed, otherwise we keep the old, global $argv like before.
1607     */
1608    if (av != NULL && *av != NULL) {
1609	struct varent *vp;
1610	if ((vp = adrof(STRargv)) != NULL && vp->vec != NULL)
1611	    st->argv = saveblk(vp->vec);
1612	else
1613	    st->argv = NULL;
1614	setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE);
1615    }
1616    else
1617	st->argv = NULL;
1618    st->av = av;
1619
1620    SHIN	= unit;	/* Do this first */
1621
1622    /* Establish new input arena */
1623    {
1624	fbuf = NULL;
1625	fseekp = feobp = fblocks = 0;
1626	settell();
1627    }
1628
1629    arginp	= 0;
1630    onelflg	= 0;
1631    intty	= isatty(SHIN);
1632    whyles	= 0;
1633    gointr	= 0;
1634    evalvec	= 0;
1635    evalp	= 0;
1636    alvec	= al;
1637    alvecp	= 0;
1638    enterhist	= hflg;
1639    if (enterhist)
1640	HIST	= '\0';
1641    insource	= 1;
1642}
1643
1644
1645/*
1646 * Restore the shell to a saved state
1647 */
1648static void
1649st_restore(void *xst)
1650{
1651    struct saved_state *st;
1652
1653    st = xst;
1654    if (st->SHIN == -1)
1655	return;
1656
1657    /* Reset input arena */
1658    {
1659	int i;
1660	Char** nfbuf = fbuf;
1661	int nfblocks = fblocks;
1662
1663	fblocks = 0;
1664	fbuf = NULL;
1665	for (i = 0; i < nfblocks; i++)
1666	    xfree(nfbuf[i]);
1667	xfree(nfbuf);
1668    }
1669    cpybin(B, st->B);
1670
1671    xclose(SHIN);
1672
1673    insource	= st->insource;
1674    SHIN	= st->SHIN;
1675    if (st->OLDSTD != -1)
1676	xclose(OLDSTD), OLDSTD = st->OLDSTD;
1677    if (st->SHOUT != -1)
1678	xclose(SHOUT),  SHOUT = st->SHOUT;
1679    if (st->SHDIAG != -1)
1680	xclose(SHDIAG), SHDIAG = st->SHDIAG;
1681    arginp	= st->arginp;
1682    onelflg	= st->onelflg;
1683    evalp	= st->evalp;
1684    evalvec	= st->evalvec;
1685    alvecp	= st->alvecp;
1686    alvec	= st->alvec;
1687    intty	= st->intty;
1688    whyles	= st->whyles;
1689    gointr	= st->gointr;
1690    if (st->HIST != '\0')
1691	HIST	= st->HIST;
1692    enterhist	= st->enterhist;
1693    cantell	= st->cantell;
1694    justpr	= st->justpr;
1695
1696    if (st->argv != NULL)
1697	setq(STRargv, st->argv, &shvhed, VAR_READWRITE);
1698    else if (st->av != NULL  && *st->av != NULL && adrof(STRargv) != NULL)
1699	unsetv(STRargv);
1700}
1701
1702/*
1703 * Source to a unit.  If onlyown it must be our file or our group or
1704 * we don't chance it.	This occurs on ".cshrc"s and the like.
1705 */
1706static void
1707srcunit(int unit, int onlyown, int hflg, Char **av)
1708{
1709    struct saved_state st;
1710
1711    st.SHIN = -1;	/* st_restore checks this */
1712
1713    if (unit < 0)
1714	return;
1715
1716    if (onlyown) {
1717	struct stat stb;
1718
1719	if (fstat(unit, &stb) < 0) {
1720	    xclose(unit);
1721	    return;
1722	}
1723    }
1724
1725    /* Does nothing before st_save() because st.SHIN == -1 */
1726    cleanup_push(&st, st_restore);
1727    if (setintr) {
1728	pintr_disabled++;
1729	cleanup_push(&pintr_disabled, disabled_cleanup);
1730    }
1731
1732    /* Save the current state and move us to a new state */
1733    st_save(&st, unit, hflg, NULL, av);
1734
1735    /*
1736     * Now if we are allowing commands to be interrupted, we let ourselves be
1737     * interrupted.
1738     */
1739    if (setintr) {
1740	cleanup_until(&pintr_disabled);
1741	pintr_disabled++;
1742	cleanup_push(&pintr_disabled, disabled_cleanup);
1743    }
1744
1745    process(0);		/* 0 -> blow away on errors */
1746
1747    /* Restore the old state */
1748    cleanup_until(&st);
1749}
1750
1751
1752/*ARGSUSED*/
1753void
1754goodbye(Char **v, struct command *c)
1755{
1756    USE(v);
1757    USE(c);
1758    record();
1759
1760    if (loginsh) {
1761	size_t omark;
1762	sigset_t set;
1763
1764	sigemptyset(&set);
1765	signal(SIGQUIT, SIG_IGN);
1766	sigaddset(&set, SIGQUIT);
1767	sigprocmask(SIG_UNBLOCK, &set, NULL);
1768	signal(SIGINT, SIG_IGN);
1769	sigaddset(&set, SIGINT);
1770	signal(SIGTERM, SIG_IGN);
1771	sigaddset(&set, SIGTERM);
1772	signal(SIGHUP, SIG_IGN);
1773	sigaddset(&set, SIGHUP);
1774	sigprocmask(SIG_UNBLOCK, &set, NULL);
1775	phup_disabled = 1;
1776	setintr = 0;		/* No interrupts after "logout" */
1777	/* Trap errors inside .logout */
1778	omark = cleanup_push_mark();
1779	if (setexit() == 0) {
1780	    if (!(adrof(STRlogout)))
1781		setcopy(STRlogout, STRnormal, VAR_READWRITE);
1782#ifdef _PATH_DOTLOGOUT
1783	    (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1784#endif
1785	    if (adrof(STRhome))
1786		(void) srccat(varval(STRhome), STRsldtlogout);
1787#ifdef TESLA
1788	    do_logout = 1;
1789#endif /* TESLA */
1790	}
1791	cleanup_pop_mark(omark);
1792    }
1793    exitstat();
1794}
1795
1796void
1797exitstat(void)
1798{
1799#ifdef PROF
1800    _mcleanup();
1801#endif
1802    /*
1803     * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1804     * directly because we poke child here. Otherwise we might continue
1805     * unwarrantedly (sic).
1806     */
1807    child = 1;
1808
1809    xexit(getn(varval(STRstatus)));
1810}
1811
1812/*
1813 * in the event of a HUP we want to save the history
1814 */
1815void
1816phup(void)
1817{
1818    if (loginsh) {
1819	setcopy(STRlogout, STRhangup, VAR_READWRITE);
1820#ifdef _PATH_DOTLOGOUT
1821	(void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1822#endif
1823	if (adrof(STRhome))
1824	    (void) srccat(varval(STRhome), STRsldtlogout);
1825    }
1826
1827    record();
1828
1829#ifdef POSIXJOBS
1830    /*
1831     * We kill the last foreground process group. It then becomes
1832     * responsible to propagate the SIGHUP to its progeny.
1833     */
1834    {
1835	struct process *pp, *np;
1836
1837	for (pp = proclist.p_next; pp; pp = pp->p_next) {
1838	    np = pp;
1839	    /*
1840	     * Find if this job is in the foreground. It could be that
1841	     * the process leader has exited and the foreground flag
1842	     * is cleared for it.
1843	     */
1844	    do
1845		/*
1846		 * If a process is in the foreground we try to kill
1847		 * it's process group. If we succeed, then the
1848		 * whole job is gone. Otherwise we keep going...
1849		 * But avoid sending HUP to the shell again.
1850		 */
1851		if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) {
1852		    np->p_flags &= ~PHUP;
1853		    if (killpg(np->p_jobid, SIGHUP) != -1) {
1854			/* In case the job was suspended... */
1855#ifdef SIGCONT
1856			(void) killpg(np->p_jobid, SIGCONT);
1857#endif
1858			break;
1859		    }
1860		}
1861	    while ((np = np->p_friends) != pp);
1862	}
1863    }
1864#endif /* POSIXJOBS */
1865
1866    xexit(SIGHUP);
1867}
1868
1869static Char   *jobargv[2] = {STRjobs, 0};
1870
1871/*
1872 * Catch an interrupt, e.g. during lexical input.
1873 * If we are an interactive shell, we reset the interrupt catch
1874 * immediately.  In any case we drain the shell output,
1875 * and finally go through the normal error mechanism, which
1876 * gets a chance to make the shell go away.
1877 */
1878int just_signaled;		/* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1879
1880void
1881pintr(void)
1882{
1883    just_signaled = 1;
1884    pintr1(1);
1885}
1886
1887void
1888pintr1(int wantnl)
1889{
1890    if (setintr) {
1891	if (pjobs) {
1892	    pjobs = 0;
1893	    xputchar('\n');
1894	    dojobs(jobargv, NULL);
1895	    stderror(ERR_NAME | ERR_INTR);
1896	}
1897    }
1898    /* MH - handle interrupted completions specially */
1899    {
1900	if (InsideCompletion)
1901	    stderror(ERR_SILENT);
1902    }
1903    /* JV - Make sure we shut off inputl */
1904    {
1905	(void) Cookedmode();
1906	GettingInput = 0;
1907    }
1908    drainoline();
1909#ifdef HAVE_GETPWENT
1910    (void) endpwent();
1911#endif
1912
1913    /*
1914     * If we have an active "onintr" then we search for the label. Note that if
1915     * one does "onintr -" then we shan't be interruptible so we needn't worry
1916     * about that here.
1917     */
1918    if (gointr) {
1919	gotolab(gointr);
1920	reset();
1921    }
1922    else if (intty && wantnl) {
1923	if (editing) {
1924	    /*
1925	     * If we are editing a multi-line input command, and move to
1926	     * the beginning of the line, we don't want to trash it when
1927	     * we hit ^C
1928	     */
1929	    PastBottom();
1930	    ClearLines();
1931	    ClearDisp();
1932	}
1933	else {
1934	    /* xputchar('\n'); *//* Some like this, others don't */
1935	    (void) putraw('\r');
1936	    (void) putraw('\n');
1937	}
1938    }
1939    stderror(ERR_SILENT);
1940}
1941
1942/*
1943 * Process is the main driving routine for the shell.
1944 * It runs all command processing, except for those within { ... }
1945 * in expressions (which is run by a routine evalav in sh.exp.c which
1946 * is a stripped down process), and `...` evaluation which is run
1947 * also by a subset of this code in sh.glob.c in the routine backeval.
1948 *
1949 * The code here is a little strange because part of it is interruptible
1950 * and hence freeing of structures appears to occur when none is necessary
1951 * if this is ignored.
1952 *
1953 * Note that if catch is not set then we will unwind on any error.
1954 * If an end-of-file occurs, we return.
1955 */
1956void
1957process(int catch)
1958{
1959    jmp_buf_t osetexit;
1960    /* PWP: This might get nuked by longjmp so don't make it a register var */
1961    size_t omark;
1962    volatile int didexitset = 0;
1963
1964    getexit(osetexit);
1965    omark = cleanup_push_mark();
1966    for (;;) {
1967	struct command *t;
1968	int hadhist, old_pintr_disabled;
1969
1970	(void)setexit();
1971	if (didexitset == 0) {
1972	    exitset++;
1973	    didexitset++;
1974	}
1975	pendjob();
1976
1977	justpr = enterhist;	/* execute if not entering history */
1978
1979	if (haderr) {
1980	    if (!catch) {
1981		/* unwind */
1982		doneinp = 0;
1983		cleanup_pop_mark(omark);
1984		resexit(osetexit);
1985		reset();
1986	    }
1987	    haderr = 0;
1988	    /*
1989	     * Every error is eventually caught here or the shell dies.  It is
1990	     * at this point that we clean up any left-over open files, by
1991	     * closing all but a fixed number of pre-defined files.  Thus
1992	     * routines don't have to worry about leaving files open due to
1993	     * deeper errors... they will get closed here.
1994	     */
1995	    closem();
1996	    continue;
1997	}
1998	if (doneinp) {
1999	    doneinp = 0;
2000	    break;
2001	}
2002	if (chkstop)
2003	    chkstop--;
2004	if (neednote)
2005	    pnote();
2006	if (intty && prompt && evalvec == 0) {
2007	    just_signaled = 0;
2008	    mailchk();
2009	    /*
2010	     * Watch for logins/logouts. Next is scheduled commands stored
2011	     * previously using "sched." Then execute periodic commands.
2012	     * Following that, the prompt precmd is run.
2013	     */
2014#ifndef HAVENOUTMP
2015	    watch_login(0);
2016#endif /* !HAVENOUTMP */
2017	    sched_run();
2018	    period_cmd();
2019	    precmd();
2020	    /*
2021	     * If we are at the end of the input buffer then we are going to
2022	     * read fresh stuff. Otherwise, we are rereading input and don't
2023	     * need or want to prompt.
2024	     */
2025	    if (fseekp == feobp && aret == TCSH_F_SEEK)
2026		printprompt(0, NULL);
2027	    flush();
2028	    setalarm(1);
2029	}
2030	if (seterr) {
2031	    xfree(seterr);
2032	    seterr = NULL;
2033	}
2034
2035	/*
2036	 * Interruptible during interactive reads
2037	 */
2038	if (setintr)
2039	    pintr_push_enable(&old_pintr_disabled);
2040	hadhist = lex(&paraml);
2041	if (setintr)
2042	    cleanup_until(&old_pintr_disabled);
2043	cleanup_push(&paraml, lex_cleanup);
2044
2045	/*
2046	 * Echo not only on VERBOSE, but also with history expansion. If there
2047	 * is a lexical error then we forego history echo.
2048	 * Do not echo if we're only entering history (source -h).
2049	 */
2050	if ((hadhist && !seterr && intty && !tellwhat && !Expand && !whyles) ||
2051	    (!enterhist && adrof(STRverbose)))
2052	{
2053	    int odidfds = didfds;
2054	    haderr = 1;
2055	    didfds = 0;
2056	    prlex(&paraml);
2057	    flush();
2058	    haderr = 0;
2059	    didfds = odidfds;
2060	}
2061	(void) alarm(0);	/* Autologout OFF */
2062	alrmcatch_disabled = 1;
2063
2064	/*
2065	 * Save input text on the history list if reading in old history, or it
2066	 * is from the terminal at the top level and not in a loop.
2067	 *
2068	 * PWP: entry of items in the history list while in a while loop is done
2069	 * elsewhere...
2070	 */
2071	if (enterhist || (catch && intty && !whyles && !tellwhat && !arun))
2072	    savehist(&paraml, enterhist > 1);
2073
2074	if (Expand && seterr)
2075	    Expand = 0;
2076
2077	/*
2078	 * Print lexical error messages, except when sourcing history lists.
2079	 */
2080	if (!enterhist && seterr)
2081	    stderror(ERR_OLD);
2082
2083	/*
2084	 * If had a history command :p modifier then this is as far as we
2085	 * should go
2086	 */
2087	if (justpr)
2088	    goto cmd_done;
2089
2090	/*
2091	 * If had a tellwhat from twenex() then do
2092	 */
2093	if (tellwhat) {
2094	    (void) tellmewhat(&paraml, NULL);
2095	    goto cmd_done;
2096	}
2097
2098	alias(&paraml);
2099
2100#ifdef BSDJOBS
2101	/*
2102	 * If we are interactive, try to continue jobs that we have stopped
2103	 */
2104	if (prompt)
2105	    continue_jobs(&paraml);
2106#endif				/* BSDJOBS */
2107
2108	/*
2109	 * Check to see if the user typed "rm * .o" or something
2110	 */
2111	if (prompt)
2112	    rmstar(&paraml);
2113	/*
2114	 * Parse the words of the input into a parse tree.
2115	 */
2116	t = syntax(paraml.next, &paraml, 0);
2117	/*
2118	 * We cannot cleanup push here, because cd /blah; echo foo
2119	 * would rewind t on the chdir error, and free the rest of the command
2120	 */
2121	if (seterr) {
2122	    freesyn(t);
2123	    stderror(ERR_OLD);
2124	}
2125
2126	postcmd();
2127	/*
2128	 * Execute the parse tree From: Michael Schroeder
2129	 * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
2130	 */
2131	execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
2132	freesyn(t);
2133
2134	/*
2135	 * Made it!
2136	 */
2137#ifdef SIG_WINDOW
2138	if (windowchg || (catch && intty && !whyles && !tellwhat)) {
2139	    (void) check_window_size(0);	/* for window systems */
2140	}
2141#endif /* SIG_WINDOW */
2142	setcopy(STR_, InputBuf, VAR_READWRITE | VAR_NOGLOB);
2143    cmd_done:
2144	if (cleanup_reset())
2145	    cleanup_until(&paraml);
2146	else
2147	    haderr = 1;
2148    }
2149    cleanup_pop_mark(omark);
2150    resexit(osetexit);
2151    exitset--;
2152    handle_pending_signals();
2153}
2154
2155/*ARGSUSED*/
2156void
2157dosource(Char **t, struct command *c)
2158{
2159    Char *f;
2160    int    hflg = 0;
2161    char *file;
2162
2163    USE(c);
2164    t++;
2165    if (*t && eq(*t, STRmh)) {
2166	if (*++t == NULL)
2167	    stderror(ERR_NAME | ERR_HFLAG);
2168	hflg++;
2169    }
2170    else if (*t && eq(*t, STRmm)) {
2171    	if (*++t == NULL)
2172	    stderror(ERR_NAME | ERR_MFLAG);
2173	hflg = 2;
2174    }
2175
2176    f = globone(*t++, G_ERROR);
2177    file = strsave(short2str(f));
2178    cleanup_push(file, xfree);
2179    xfree(f);
2180    t = glob_all_or_error(t);
2181    if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
2182	stderror(ERR_SYSTEM, file, strerror(errno));
2183    cleanup_until(file);
2184}
2185
2186/*
2187 * Check for mail.
2188 * If we are a login shell, then we don't want to tell
2189 * about any mail file unless its been modified
2190 * after the time we started.
2191 * This prevents us from telling the user things he already
2192 * knows, since the login program insists on saying
2193 * "You have mail."
2194 */
2195
2196/*
2197 * The AMS version.
2198 * This version checks if the file is a directory, and if so,
2199 * tells you the number of files in it, otherwise do the old thang.
2200 * The magic "+1" in the time calculation is to compensate for
2201 * an AFS bug where directory mtimes are set to 1 second in
2202 * the future.
2203 */
2204
2205static void
2206mailchk(void)
2207{
2208    struct varent *v;
2209    Char **vp;
2210    time_t  t;
2211    int     intvl, cnt;
2212    struct stat stb;
2213    int    new;
2214
2215    v = adrof(STRmail);
2216    if (v == NULL || v->vec == NULL)
2217	return;
2218    (void) time(&t);
2219    vp = v->vec;
2220    cnt = blklen(vp);
2221    intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
2222    if (intvl < 1)
2223	intvl = 1;
2224    if (chktim + intvl > t)
2225	return;
2226    for (; *vp; vp++) {
2227	char *filename = short2str(*vp);
2228	char *mboxdir = filename;
2229
2230	if (stat(filename, &stb) < 0)
2231	    continue;
2232#if defined(BSDTIMES) || defined(_SEQUENT_)
2233	new = stb.st_mtime > time0.tv_sec;
2234#else
2235	new = stb.st_mtime > seconds0;
2236#endif
2237	if (S_ISDIR(stb.st_mode)) {
2238	    DIR *mailbox;
2239	    int mailcount = 0;
2240	    char *tempfilename;
2241	    struct stat stc;
2242
2243	    tempfilename = xasprintf("%s/new", filename);
2244
2245	    if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) {
2246		/*
2247		 * "filename/new" exists and is a directory; you are
2248		 * using Qmail.
2249		 */
2250		stb = stc;
2251#if defined(BSDTIMES) || defined(_SEQUENT_)
2252		new = stb.st_mtime > time0.tv_sec;
2253#else
2254		new = stb.st_mtime > seconds0;
2255#endif
2256		mboxdir = tempfilename;
2257	    }
2258
2259	    if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) {
2260		xfree(tempfilename);
2261		continue;
2262	    }
2263
2264	    mailbox = opendir(mboxdir);
2265	    xfree(tempfilename);
2266	    if (mailbox == NULL)
2267		continue;
2268
2269	    /* skip . and .. */
2270	    if (!readdir(mailbox) || !readdir(mailbox)) {
2271		(void)closedir(mailbox);
2272		continue;
2273	    }
2274
2275	    while (readdir(mailbox))
2276		mailcount++;
2277
2278	    (void)closedir(mailbox);
2279	    if (mailcount == 0)
2280		continue;
2281
2282	    if (cnt == 1)
2283		xprintf(CGETS(11, 3, "You have %d mail messages.\n"),
2284			mailcount);
2285	    else
2286		xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"),
2287			mailcount, filename);
2288	}
2289	else {
2290	    char *type;
2291
2292	    if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime ||
2293		(stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
2294		(loginsh && !new))
2295		continue;
2296	    type = strsave(new ? CGETS(11, 6, "new ") : "");
2297	    cleanup_push(type, xfree);
2298	    if (cnt == 1)
2299		xprintf(CGETS(11, 5, "You have %smail.\n"), type);
2300	    else
2301	        xprintf(CGETS(11, 7, "You have %smail in %s.\n"), type, filename);
2302	    cleanup_until(type);
2303	}
2304    }
2305    chktim = t;
2306}
2307
2308/*
2309 * Extract a home directory from the password file
2310 * The argument points to a buffer where the name of the
2311 * user whose home directory is sought is currently.
2312 * We return home directory of the user, or NULL.
2313 */
2314Char *
2315gethdir(const Char *home)
2316{
2317    Char   *h;
2318
2319    /*
2320     * Is it us?
2321     */
2322    if (*home == '\0') {
2323	if ((h = varval(STRhome)) != STRNULL)
2324	    return Strsave(h);
2325	else
2326	    return NULL;
2327    }
2328
2329    /*
2330     * Look in the cache
2331     */
2332    if ((h = gettilde(home)) == NULL)
2333	return NULL;
2334    else
2335	return Strsave(h);
2336}
2337
2338/*
2339 * Move the initial descriptors to their eventual
2340 * resting places, closing all other units.
2341 */
2342void
2343initdesc(void)
2344{
2345#ifdef NLS_BUGS
2346#ifdef NLS_CATALOGS
2347    nlsclose();
2348#endif /* NLS_CATALOGS */
2349#endif /* NLS_BUGS */
2350
2351
2352    didfds = 0;			/* 0, 1, 2 aren't set up */
2353    (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1);
2354    (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1);
2355    (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1);
2356    (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1);
2357#ifndef CLOSE_ON_EXEC
2358    didcch = 0;			/* Havent closed for child */
2359#endif /* CLOSE_ON_EXEC */
2360    if (SHDIAG >= 0)
2361	isdiagatty = isatty(SHDIAG);
2362    else
2363    	isdiagatty = 0;
2364    if (SHDIAG >= 0)
2365	isoutatty = isatty(SHOUT);
2366    else
2367    	isoutatty = 0;
2368#ifdef NLS_BUGS
2369#ifdef NLS_CATALOGS
2370    nlsinit();
2371#endif /* NLS_CATALOGS */
2372#endif /* NLS_BUGS */
2373}
2374
2375
2376void
2377#ifdef PROF
2378done(int i)
2379#else
2380xexit(int i)
2381#endif
2382{
2383#ifdef TESLA
2384    if (loginsh && do_logout) {
2385	/* this is to send hangup signal to the develcon */
2386	/* we toggle DTR. clear dtr - sleep 1 - set dtr */
2387	/* ioctl will return ENOTTY for pty's but we ignore it 	 */
2388	/* exitstat will run after disconnect */
2389	/* we sleep for 2 seconds to let things happen in */
2390	/* .logout and rechist() */
2391#ifdef TIOCCDTR
2392	(void) sleep(2);
2393	(void) ioctl(FSHTTY, TIOCCDTR, NULL);
2394	(void) sleep(1);
2395	(void) ioctl(FSHTTY, TIOCSDTR, NULL);
2396#endif /* TIOCCDTR */
2397    }
2398#endif /* TESLA */
2399
2400    {
2401	struct process *pp, *np;
2402	pid_t mypid = getpid();
2403	/* Kill all processes marked for hup'ing */
2404	for (pp = proclist.p_next; pp; pp = pp->p_next) {
2405	    np = pp;
2406	    do
2407		if ((np->p_flags & PHUP) && np->p_jobid != shpgrp &&
2408		    np->p_parentid == mypid) {
2409		    if (killpg(np->p_jobid, SIGHUP) != -1) {
2410			/* In case the job was suspended... */
2411#ifdef SIGCONT
2412			(void) killpg(np->p_jobid, SIGCONT);
2413#endif
2414			break;
2415		    }
2416		}
2417	    while ((np = np->p_friends) != pp);
2418	}
2419    }
2420    untty();
2421#ifdef NLS_CATALOGS
2422    /*
2423     * We need to call catclose, because SVR4 leaves symlinks behind otherwise
2424     * in the catalog directories. We cannot close on a vforked() child,
2425     * because messages will stop working on the parent too.
2426     */
2427    if (child == 0)
2428	nlsclose();
2429#endif /* NLS_CATALOGS */
2430#ifdef WINNT_NATIVE
2431    nt_cleanup();
2432#endif /* WINNT_NATIVE */
2433    _exit(i);
2434}
2435
2436#ifndef _PATH_DEFPATH
2437static Char **
2438defaultpath(void)
2439{
2440    char   *ptr;
2441    Char  **blk, **blkp;
2442    struct stat stb;
2443
2444    blkp = blk = xmalloc(sizeof(Char *) * 10);
2445
2446#ifndef NODOT
2447# ifndef DOTLAST
2448    *blkp++ = Strsave(STRdot);
2449# endif
2450#endif
2451
2452#define DIRAPPEND(a)  \
2453	if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
2454		*blkp++ = SAVE(ptr)
2455
2456#ifdef _PATH_LOCAL
2457    DIRAPPEND(_PATH_LOCAL);
2458#endif
2459
2460#ifdef _PATH_USRUCB
2461    DIRAPPEND(_PATH_USRUCB);
2462#endif
2463
2464#ifdef _PATH_USRBSD
2465    DIRAPPEND(_PATH_USRBSD);
2466#endif
2467
2468#ifdef _PATH_BIN
2469    DIRAPPEND(_PATH_BIN);
2470#endif
2471
2472#ifdef _PATH_USRBIN
2473    DIRAPPEND(_PATH_USRBIN);
2474#endif
2475
2476#undef DIRAPPEND
2477
2478#ifndef NODOT
2479# ifdef DOTLAST
2480    *blkp++ = Strsave(STRdot);
2481# endif
2482#endif
2483    *blkp = NULL;
2484    return (blk);
2485}
2486#endif
2487
2488static void
2489record(void)
2490{
2491    if (!fast) {
2492	recdirs(NULL, adrof(STRsavedirs) != NULL);
2493	rechist(NULL, adrof(STRsavehist) != NULL);
2494    }
2495    displayHistStats("Exiting");	/* no-op unless DEBUG_HIST */
2496}
2497
2498/*
2499 * Grab the tty repeatedly, and give up if we are not in the correct
2500 * tty process group.
2501 */
2502int
2503grabpgrp(int fd, pid_t desired)
2504{
2505    struct sigaction old;
2506    pid_t pgrp;
2507    size_t i;
2508
2509    for (i = 0; i < 100; i++) {
2510	if ((pgrp = tcgetpgrp(fd)) == -1)
2511	    return -1;
2512	if (pgrp == desired)
2513	    return 0;
2514	(void)sigaction(SIGTTIN, NULL, &old);
2515	(void)signal(SIGTTIN, SIG_DFL);
2516	(void)kill(0, SIGTTIN);
2517	(void)sigaction(SIGTTIN, &old, NULL);
2518    }
2519    errno = EPERM;
2520    return -1;
2521}
2522