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