sh.c revision 225736
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    /* Take care of these (especially HUP) here instead of inside flush. */
1295    handle_pending_signals();
1296    if (intty) {
1297	if (loginsh) {
1298	    xprintf("logout\n");
1299	    xclose(SHIN);
1300	    child = 1;
1301#ifdef TESLA
1302	    do_logout = 1;
1303#endif				/* TESLA */
1304	    goodbye(NULL, NULL);
1305	}
1306	else {
1307	    xprintf("exit\n");
1308	}
1309    }
1310    record();
1311    exitstat();
1312    return (0);
1313}
1314
1315void
1316untty(void)
1317{
1318#ifdef BSDJOBS
1319    if (tpgrp > 0 && opgrp != shpgrp) {
1320	(void) setpgid(0, opgrp);
1321	(void) tcsetpgrp(FSHTTY, opgrp);
1322	(void) resetdisc(FSHTTY);
1323    }
1324#endif /* BSDJOBS */
1325}
1326
1327void
1328importpath(Char *cp)
1329{
1330    size_t i = 0;
1331    Char *dp;
1332    Char **pv;
1333    int     c;
1334
1335    for (dp = cp; *dp; dp++)
1336	if (*dp == PATHSEP)
1337	    i++;
1338    /*
1339     * i+2 where i is the number of colons in the path. There are i+1
1340     * directories in the path plus we need room for a zero terminator.
1341     */
1342    pv = xcalloc(i + 2, sizeof(Char *));
1343    dp = cp;
1344    i = 0;
1345    if (*dp)
1346	for (;;) {
1347	    if ((c = *dp) == PATHSEP || c == 0) {
1348		*dp = 0;
1349		pv[i++] = Strsave(*cp ? cp : STRdot);
1350		if (c) {
1351		    cp = dp + 1;
1352		    *dp = PATHSEP;
1353		}
1354		else
1355		    break;
1356	    }
1357#ifdef WINNT_NATIVE
1358	    else if (*dp == '\\')
1359		*dp = '/';
1360#endif /* WINNT_NATIVE */
1361	    dp++;
1362	}
1363    pv[i] = 0;
1364    cleanup_push(pv, blk_cleanup);
1365    setq(STRpath, pv, &shvhed, VAR_READWRITE);
1366    cleanup_ignore(pv);
1367    cleanup_until(pv);
1368}
1369
1370/*
1371 * Source to the file which is the catenation of the argument names.
1372 */
1373static int
1374srccat(Char *cp, Char *dp)
1375{
1376    if (cp[0] == '/' && cp[1] == '\0')
1377	return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
1378    else {
1379	Char *ep;
1380	char   *ptr;
1381	int rv;
1382
1383#ifdef WINNT_NATIVE
1384	ep = Strend(cp);
1385	if (ep != cp && ep[-1] == '/' && dp[0] == '/') /* silly win95 */
1386	    dp++;
1387#endif /* WINNT_NATIVE */
1388
1389	ep = Strspl(cp, dp);
1390	cleanup_push(ep, xfree);
1391	ptr = short2str(ep);
1392
1393	rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
1394	cleanup_until(ep);
1395	return rv;
1396    }
1397}
1398
1399/*
1400 * Source to a file putting the file descriptor in a safe place (> 2).
1401 */
1402#ifndef WINNT_NATIVE
1403static int
1404#else
1405int
1406#endif /*WINNT_NATIVE*/
1407srcfile(const char *f, int onlyown, int flag, Char **av)
1408{
1409    int unit;
1410
1411    if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1)
1412	return 0;
1413    cleanup_push(&unit, open_cleanup);
1414    unit = dmove(unit, -1);
1415    cleanup_ignore(&unit);
1416    cleanup_until(&unit);
1417
1418    (void) close_on_exec(unit, 1);
1419    srcunit(unit, onlyown, flag, av);
1420    return 1;
1421}
1422
1423
1424/*
1425 * Save the shell state, and establish new argument vector, and new input
1426 * fd.
1427 */
1428static void
1429st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
1430{
1431    st->insource	= insource;
1432    st->SHIN		= SHIN;
1433    /* Want to preserve the meaning of "source file >output".
1434     * Save old descriptors, move new 0,1,2 to safe places and assign
1435     * them to SH* and let process() redo 0,1,2 from them.
1436     *
1437     * The macro returns true if d1 and d2 are good and they point to
1438     * different things.  If you don't avoid saving duplicate
1439     * descriptors, you really limit the depth of "source" recursion
1440     * you can do because of all the open file descriptors.  -IAN!
1441     */
1442#define NEED_SAVE_FD(d1,d2) \
1443    (fstat(d1, &s1) != -1 && fstat(d2, &s2) != -1 \
1444	&& (s1.st_ino != s2.st_ino || s1.st_dev != s2.st_dev) )
1445
1446    st->OLDSTD = st->SHOUT = st->SHDIAG = -1;/* test later to restore these */
1447    if (didfds) {
1448	    struct stat s1, s2;
1449	    if (NEED_SAVE_FD(0,OLDSTD)) {
1450		    st->OLDSTD = OLDSTD;
1451		    OLDSTD = dmove(0, -1);
1452		    (void)close_on_exec(OLDSTD, 1);
1453	    }
1454	    if (NEED_SAVE_FD(1,SHOUT)) {
1455		    st->SHOUT = SHOUT;
1456		    SHOUT = dmove(1, -1);
1457		    (void)close_on_exec(SHOUT, 1);
1458	    }
1459	    if (NEED_SAVE_FD(2,SHDIAG)) {
1460		    st->SHDIAG = SHDIAG;
1461		    SHDIAG = dmove(2, -1);
1462		    (void)close_on_exec(SHDIAG, 1);
1463	    }
1464	    donefds();
1465    }
1466
1467    st->intty		= intty;
1468    st->whyles		= whyles;
1469    st->gointr		= gointr;
1470    st->arginp		= arginp;
1471    st->evalp		= evalp;
1472    st->evalvec		= evalvec;
1473    st->alvecp		= alvecp;
1474    st->alvec		= alvec;
1475    st->onelflg		= onelflg;
1476    st->enterhist	= enterhist;
1477    st->justpr		= justpr;
1478    if (hflg)
1479	st->HIST	= HIST;
1480    else
1481	st->HIST	= '\0';
1482    st->cantell		= cantell;
1483    cpybin(st->B, B);
1484
1485    /*
1486     * we can now pass arguments to source.
1487     * For compatibility we do that only if arguments were really
1488     * passed, otherwise we keep the old, global $argv like before.
1489     */
1490    if (av != NULL && *av != NULL) {
1491	struct varent *vp;
1492	if ((vp = adrof(STRargv)) != NULL && vp->vec != NULL)
1493	    st->argv = saveblk(vp->vec);
1494	else
1495	    st->argv = NULL;
1496	setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE);
1497    }
1498    else
1499	st->argv = NULL;
1500    st->av = av;
1501
1502    SHIN	= unit;	/* Do this first */
1503
1504    /* Establish new input arena */
1505    {
1506	fbuf = NULL;
1507	fseekp = feobp = fblocks = 0;
1508	settell();
1509    }
1510
1511    arginp	= 0;
1512    onelflg	= 0;
1513    intty	= isatty(SHIN);
1514    whyles	= 0;
1515    gointr	= 0;
1516    evalvec	= 0;
1517    evalp	= 0;
1518    alvec	= al;
1519    alvecp	= 0;
1520    enterhist	= hflg;
1521    if (enterhist)
1522	HIST	= '\0';
1523    insource	= 1;
1524}
1525
1526
1527/*
1528 * Restore the shell to a saved state
1529 */
1530static void
1531st_restore(void *xst)
1532{
1533    struct saved_state *st;
1534
1535    st = xst;
1536    if (st->SHIN == -1)
1537	return;
1538
1539    /* Reset input arena */
1540    {
1541	int i;
1542	Char** nfbuf = fbuf;
1543	int nfblocks = fblocks;
1544
1545	fblocks = 0;
1546	fbuf = NULL;
1547	for (i = 0; i < nfblocks; i++)
1548	    xfree(nfbuf[i]);
1549	xfree(nfbuf);
1550    }
1551    cpybin(B, st->B);
1552
1553    xclose(SHIN);
1554
1555    insource	= st->insource;
1556    SHIN	= st->SHIN;
1557    if (st->OLDSTD != -1)
1558	xclose(OLDSTD), OLDSTD = st->OLDSTD;
1559    if (st->SHOUT != -1)
1560	xclose(SHOUT),  SHOUT = st->SHOUT;
1561    if (st->SHDIAG != -1)
1562	xclose(SHDIAG), SHDIAG = st->SHDIAG;
1563    arginp	= st->arginp;
1564    onelflg	= st->onelflg;
1565    evalp	= st->evalp;
1566    evalvec	= st->evalvec;
1567    alvecp	= st->alvecp;
1568    alvec	= st->alvec;
1569    intty	= st->intty;
1570    whyles	= st->whyles;
1571    gointr	= st->gointr;
1572    if (st->HIST != '\0')
1573	HIST	= st->HIST;
1574    enterhist	= st->enterhist;
1575    cantell	= st->cantell;
1576    justpr	= st->justpr;
1577
1578    if (st->argv != NULL)
1579	setq(STRargv, st->argv, &shvhed, VAR_READWRITE);
1580    else if (st->av != NULL  && *st->av != NULL && adrof(STRargv) != NULL)
1581	unsetv(STRargv);
1582}
1583
1584/*
1585 * Source to a unit.  If onlyown it must be our file or our group or
1586 * we don't chance it.	This occurs on ".cshrc"s and the like.
1587 */
1588static void
1589srcunit(int unit, int onlyown, int hflg, Char **av)
1590{
1591    struct saved_state st;
1592
1593    st.SHIN = -1;	/* st_restore checks this */
1594
1595    if (unit < 0)
1596	return;
1597
1598    if (onlyown) {
1599	struct stat stb;
1600
1601	if (fstat(unit, &stb) < 0) {
1602	    xclose(unit);
1603	    return;
1604	}
1605    }
1606
1607    /* Does nothing before st_save() because st.SHIN == -1 */
1608    cleanup_push(&st, st_restore);
1609    if (setintr) {
1610	pintr_disabled++;
1611	cleanup_push(&pintr_disabled, disabled_cleanup);
1612    }
1613
1614    /* Save the current state and move us to a new state */
1615    st_save(&st, unit, hflg, NULL, av);
1616
1617    /*
1618     * Now if we are allowing commands to be interrupted, we let ourselves be
1619     * interrupted.
1620     */
1621    if (setintr) {
1622	cleanup_until(&pintr_disabled);
1623	pintr_disabled++;
1624	cleanup_push(&pintr_disabled, disabled_cleanup);
1625    }
1626
1627    process(0);		/* 0 -> blow away on errors */
1628
1629    /* Restore the old state */
1630    cleanup_until(&st);
1631}
1632
1633
1634/*ARGSUSED*/
1635void
1636goodbye(Char **v, struct command *c)
1637{
1638    USE(v);
1639    USE(c);
1640    record();
1641
1642    if (loginsh) {
1643	size_t omark;
1644	sigset_t set;
1645
1646	sigemptyset(&set);
1647	signal(SIGQUIT, SIG_IGN);
1648	sigaddset(&set, SIGQUIT);
1649	sigprocmask(SIG_UNBLOCK, &set, NULL);
1650	signal(SIGINT, SIG_IGN);
1651	sigaddset(&set, SIGINT);
1652	signal(SIGTERM, SIG_IGN);
1653	sigaddset(&set, SIGTERM);
1654	signal(SIGHUP, SIG_IGN);
1655	sigaddset(&set, SIGHUP);
1656	sigprocmask(SIG_UNBLOCK, &set, NULL);
1657	phup_disabled = 1;
1658	setintr = 0;		/* No interrupts after "logout" */
1659	/* Trap errors inside .logout */
1660	omark = cleanup_push_mark();
1661	if (setexit() == 0) {
1662	    if (!(adrof(STRlogout)))
1663		setcopy(STRlogout, STRnormal, VAR_READWRITE);
1664#ifdef _PATH_DOTLOGOUT
1665	    (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1666#endif
1667	    if (adrof(STRhome))
1668		(void) srccat(varval(STRhome), STRsldtlogout);
1669#ifdef TESLA
1670	    do_logout = 1;
1671#endif /* TESLA */
1672	}
1673	cleanup_pop_mark(omark);
1674    }
1675    exitstat();
1676}
1677
1678void
1679exitstat(void)
1680{
1681#ifdef PROF
1682    monitor(0);
1683#endif
1684    /*
1685     * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1686     * directly because we poke child here. Otherwise we might continue
1687     * unwarrantedly (sic).
1688     */
1689    child = 1;
1690
1691    xexit(getn(varval(STRstatus)));
1692}
1693
1694/*
1695 * in the event of a HUP we want to save the history
1696 */
1697void
1698phup(void)
1699{
1700    if (loginsh) {
1701	setcopy(STRlogout, STRhangup, VAR_READWRITE);
1702#ifdef _PATH_DOTLOGOUT
1703	(void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1704#endif
1705	if (adrof(STRhome))
1706	    (void) srccat(varval(STRhome), STRsldtlogout);
1707    }
1708
1709    record();
1710
1711#ifdef POSIXJOBS
1712    /*
1713     * We kill the last foreground process group. It then becomes
1714     * responsible to propagate the SIGHUP to its progeny.
1715     */
1716    {
1717	struct process *pp, *np;
1718
1719	for (pp = proclist.p_next; pp; pp = pp->p_next) {
1720	    np = pp;
1721	    /*
1722	     * Find if this job is in the foreground. It could be that
1723	     * the process leader has exited and the foreground flag
1724	     * is cleared for it.
1725	     */
1726	    do
1727		/*
1728		 * If a process is in the foreground we try to kill
1729		 * it's process group. If we succeed, then the
1730		 * whole job is gone. Otherwise we keep going...
1731		 * But avoid sending HUP to the shell again.
1732		 */
1733		if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) {
1734		    np->p_flags &= ~PHUP;
1735		    if (killpg(np->p_jobid, SIGHUP) != -1) {
1736			/* In case the job was suspended... */
1737#ifdef SIGCONT
1738			(void) killpg(np->p_jobid, SIGCONT);
1739#endif
1740			break;
1741		    }
1742		}
1743	    while ((np = np->p_friends) != pp);
1744	}
1745    }
1746#endif /* POSIXJOBS */
1747
1748    xexit(SIGHUP);
1749}
1750
1751static Char   *jobargv[2] = {STRjobs, 0};
1752
1753/*
1754 * Catch an interrupt, e.g. during lexical input.
1755 * If we are an interactive shell, we reset the interrupt catch
1756 * immediately.  In any case we drain the shell output,
1757 * and finally go through the normal error mechanism, which
1758 * gets a chance to make the shell go away.
1759 */
1760int just_signaled;		/* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1761
1762void
1763pintr(void)
1764{
1765    just_signaled = 1;
1766    pintr1(1);
1767}
1768
1769void
1770pintr1(int wantnl)
1771{
1772    if (setintr) {
1773	if (pjobs) {
1774	    pjobs = 0;
1775	    xputchar('\n');
1776	    dojobs(jobargv, NULL);
1777	    stderror(ERR_NAME | ERR_INTR);
1778	}
1779    }
1780    /* MH - handle interrupted completions specially */
1781    {
1782	if (InsideCompletion)
1783	    stderror(ERR_SILENT);
1784    }
1785    /* JV - Make sure we shut off inputl */
1786    {
1787	(void) Cookedmode();
1788	GettingInput = 0;
1789    }
1790    drainoline();
1791#ifdef HAVE_GETPWENT
1792    (void) endpwent();
1793#endif
1794
1795    /*
1796     * If we have an active "onintr" then we search for the label. Note that if
1797     * one does "onintr -" then we shan't be interruptible so we needn't worry
1798     * about that here.
1799     */
1800    if (gointr) {
1801	gotolab(gointr);
1802	reset();
1803    }
1804    else if (intty && wantnl) {
1805	if (editing) {
1806	    /*
1807	     * If we are editing a multi-line input command, and move to
1808	     * the beginning of the line, we don't want to trash it when
1809	     * we hit ^C
1810	     */
1811	    PastBottom();
1812	    ClearLines();
1813	    ClearDisp();
1814	}
1815	else {
1816	    /* xputchar('\n'); *//* Some like this, others don't */
1817	    (void) putraw('\r');
1818	    (void) putraw('\n');
1819	}
1820    }
1821    stderror(ERR_SILENT);
1822}
1823
1824/*
1825 * Process is the main driving routine for the shell.
1826 * It runs all command processing, except for those within { ... }
1827 * in expressions (which is run by a routine evalav in sh.exp.c which
1828 * is a stripped down process), and `...` evaluation which is run
1829 * also by a subset of this code in sh.glob.c in the routine backeval.
1830 *
1831 * The code here is a little strange because part of it is interruptible
1832 * and hence freeing of structures appears to occur when none is necessary
1833 * if this is ignored.
1834 *
1835 * Note that if catch is not set then we will unwind on any error.
1836 * If an end-of-file occurs, we return.
1837 */
1838void
1839process(int catch)
1840{
1841    jmp_buf_t osetexit;
1842    /* PWP: This might get nuked my longjmp so don't make it a register var */
1843    size_t omark;
1844    volatile int didexitset = 0;
1845
1846    getexit(osetexit);
1847    omark = cleanup_push_mark();
1848    for (;;) {
1849	struct command *t;
1850	int hadhist, old_pintr_disabled;
1851
1852	(void)setexit();
1853	if (didexitset == 0) {
1854	    exitset++;
1855	    didexitset++;
1856	}
1857	pendjob();
1858
1859	justpr = enterhist;	/* execute if not entering history */
1860
1861	if (haderr) {
1862	    if (!catch) {
1863		/* unwind */
1864		doneinp = 0;
1865		cleanup_pop_mark(omark);
1866		resexit(osetexit);
1867		reset();
1868	    }
1869	    haderr = 0;
1870	    /*
1871	     * Every error is eventually caught here or the shell dies.  It is
1872	     * at this point that we clean up any left-over open files, by
1873	     * closing all but a fixed number of pre-defined files.  Thus
1874	     * routines don't have to worry about leaving files open due to
1875	     * deeper errors... they will get closed here.
1876	     */
1877	    closem();
1878	    continue;
1879	}
1880	if (doneinp) {
1881	    doneinp = 0;
1882	    break;
1883	}
1884	if (chkstop)
1885	    chkstop--;
1886	if (neednote)
1887	    pnote();
1888	if (intty && prompt && evalvec == 0) {
1889	    just_signaled = 0;
1890	    mailchk();
1891	    /*
1892	     * Watch for logins/logouts. Next is scheduled commands stored
1893	     * previously using "sched." Then execute periodic commands.
1894	     * Following that, the prompt precmd is run.
1895	     */
1896#ifndef HAVENOUTMP
1897	    watch_login(0);
1898#endif /* !HAVENOUTMP */
1899	    sched_run();
1900	    period_cmd();
1901	    precmd();
1902	    /*
1903	     * If we are at the end of the input buffer then we are going to
1904	     * read fresh stuff. Otherwise, we are rereading input and don't
1905	     * need or want to prompt.
1906	     */
1907	    if (fseekp == feobp && aret == TCSH_F_SEEK)
1908		printprompt(0, NULL);
1909	    flush();
1910	    setalarm(1);
1911	}
1912	if (seterr) {
1913	    xfree(seterr);
1914	    seterr = NULL;
1915	}
1916
1917	/*
1918	 * Interruptible during interactive reads
1919	 */
1920	if (setintr)
1921	    pintr_push_enable(&old_pintr_disabled);
1922	hadhist = lex(&paraml);
1923	if (setintr)
1924	    cleanup_until(&old_pintr_disabled);
1925	cleanup_push(&paraml, lex_cleanup);
1926
1927	/*
1928	 * Echo not only on VERBOSE, but also with history expansion. If there
1929	 * is a lexical error then we forego history echo.
1930	 */
1931	if ((hadhist && !seterr && intty && !tellwhat && !Expand && !whyles) ||
1932	    adrof(STRverbose)) {
1933	    int odidfds = didfds;
1934	    haderr = 1;
1935	    didfds = 0;
1936	    prlex(&paraml);
1937	    flush();
1938	    haderr = 0;
1939	    didfds = odidfds;
1940	}
1941	(void) alarm(0);	/* Autologout OFF */
1942	alrmcatch_disabled = 1;
1943
1944	/*
1945	 * Save input text on the history list if reading in old history, or it
1946	 * is from the terminal at the top level and not in a loop.
1947	 *
1948	 * PWP: entry of items in the history list while in a while loop is done
1949	 * elsewhere...
1950	 */
1951	if (enterhist || (catch && intty && !whyles && !tellwhat && !arun))
1952	    savehist(&paraml, enterhist > 1);
1953
1954	if (Expand && seterr)
1955	    Expand = 0;
1956
1957	/*
1958	 * Print lexical error messages, except when sourcing history lists.
1959	 */
1960	if (!enterhist && seterr)
1961	    stderror(ERR_OLD);
1962
1963	/*
1964	 * If had a history command :p modifier then this is as far as we
1965	 * should go
1966	 */
1967	if (justpr)
1968	    goto cmd_done;
1969
1970	/*
1971	 * If had a tellwhat from twenex() then do
1972	 */
1973	if (tellwhat) {
1974	    (void) tellmewhat(&paraml, NULL);
1975	    goto cmd_done;
1976	}
1977
1978	alias(&paraml);
1979
1980#ifdef BSDJOBS
1981	/*
1982	 * If we are interactive, try to continue jobs that we have stopped
1983	 */
1984	if (prompt)
1985	    continue_jobs(&paraml);
1986#endif				/* BSDJOBS */
1987
1988	/*
1989	 * Check to see if the user typed "rm * .o" or something
1990	 */
1991	if (prompt)
1992	    rmstar(&paraml);
1993	/*
1994	 * Parse the words of the input into a parse tree.
1995	 */
1996	t = syntax(paraml.next, &paraml, 0);
1997	cleanup_push(t, syntax_cleanup);
1998	if (seterr)
1999	    stderror(ERR_OLD);
2000
2001	postcmd();
2002	/*
2003	 * Execute the parse tree From: Michael Schroeder
2004	 * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
2005	 */
2006	execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
2007
2008	/*
2009	 * Made it!
2010	 */
2011#ifdef SIG_WINDOW
2012	if (windowchg || (catch && intty && !whyles && !tellwhat)) {
2013	    (void) check_window_size(0);	/* for window systems */
2014	}
2015#endif /* SIG_WINDOW */
2016	setcopy(STR_, InputBuf, VAR_READWRITE | VAR_NOGLOB);
2017    cmd_done:
2018	cleanup_until(&paraml);
2019    }
2020    cleanup_pop_mark(omark);
2021    resexit(osetexit);
2022    exitset--;
2023}
2024
2025/*ARGSUSED*/
2026void
2027dosource(Char **t, struct command *c)
2028{
2029    Char *f;
2030    int    hflg = 0;
2031    char *file;
2032
2033    USE(c);
2034    t++;
2035    if (*t && eq(*t, STRmh)) {
2036	if (*++t == NULL)
2037	    stderror(ERR_NAME | ERR_HFLAG);
2038	hflg++;
2039    }
2040    else if (*t && eq(*t, STRmm)) {
2041    	if (*++t == NULL)
2042	    stderror(ERR_NAME | ERR_MFLAG);
2043	hflg = 2;
2044    }
2045
2046    f = globone(*t++, G_ERROR);
2047    file = strsave(short2str(f));
2048    cleanup_push(file, xfree);
2049    xfree(f);
2050    t = glob_all_or_error(t);
2051    if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
2052	stderror(ERR_SYSTEM, file, strerror(errno));
2053    cleanup_until(file);
2054}
2055
2056/*
2057 * Check for mail.
2058 * If we are a login shell, then we don't want to tell
2059 * about any mail file unless its been modified
2060 * after the time we started.
2061 * This prevents us from telling the user things he already
2062 * knows, since the login program insists on saying
2063 * "You have mail."
2064 */
2065
2066/*
2067 * The AMS version.
2068 * This version checks if the file is a directory, and if so,
2069 * tells you the number of files in it, otherwise do the old thang.
2070 * The magic "+1" in the time calculation is to compensate for
2071 * an AFS bug where directory mtimes are set to 1 second in
2072 * the future.
2073 */
2074
2075static void
2076mailchk(void)
2077{
2078    struct varent *v;
2079    Char **vp;
2080    time_t  t;
2081    int     intvl, cnt;
2082    struct stat stb;
2083    int    new;
2084
2085    v = adrof(STRmail);
2086    if (v == NULL || v->vec == NULL)
2087	return;
2088    (void) time(&t);
2089    vp = v->vec;
2090    cnt = blklen(vp);
2091    intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
2092    if (intvl < 1)
2093	intvl = 1;
2094    if (chktim + intvl > t)
2095	return;
2096    for (; *vp; vp++) {
2097	char *filename = short2str(*vp);
2098	char *mboxdir = filename;
2099
2100	if (stat(filename, &stb) < 0)
2101	    continue;
2102#if defined(BSDTIMES) || defined(_SEQUENT_)
2103	new = stb.st_mtime > time0.tv_sec;
2104#else
2105	new = stb.st_mtime > seconds0;
2106#endif
2107	if (S_ISDIR(stb.st_mode)) {
2108	    DIR *mailbox;
2109	    int mailcount = 0;
2110	    char *tempfilename;
2111	    struct stat stc;
2112
2113	    tempfilename = xasprintf("%s/new", filename);
2114
2115	    if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) {
2116		/*
2117		 * "filename/new" exists and is a directory; you are
2118		 * using Qmail.
2119		 */
2120		stb = stc;
2121#if defined(BSDTIMES) || defined(_SEQUENT_)
2122		new = stb.st_mtime > time0.tv_sec;
2123#else
2124		new = stb.st_mtime > seconds0;
2125#endif
2126		mboxdir = tempfilename;
2127	    }
2128
2129	    if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) {
2130		xfree(tempfilename);
2131		continue;
2132	    }
2133
2134	    mailbox = opendir(mboxdir);
2135	    xfree(tempfilename);
2136	    if (mailbox == NULL)
2137		continue;
2138
2139	    /* skip . and .. */
2140	    if (!readdir(mailbox) || !readdir(mailbox))
2141		continue;
2142
2143	    while (readdir(mailbox))
2144		mailcount++;
2145
2146	    if (mailcount == 0)
2147		continue;
2148
2149	    if (cnt == 1)
2150		xprintf(CGETS(11, 3, "You have %d mail messages.\n"),
2151			mailcount);
2152	    else
2153		xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"),
2154			mailcount, filename);
2155	}
2156	else {
2157	    char *type;
2158
2159	    if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
2160		(stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
2161		(loginsh && !new))
2162		continue;
2163	    type = strsave(new ? CGETS(11, 6, "new ") : "");
2164	    cleanup_push(type, xfree);
2165	    if (cnt == 1)
2166		xprintf(CGETS(11, 5, "You have %smail.\n"), type);
2167	    else
2168	        xprintf(CGETS(11, 7, "You have %smail in %s.\n"), type, filename);
2169	    cleanup_until(type);
2170	}
2171    }
2172    chktim = t;
2173}
2174
2175/*
2176 * Extract a home directory from the password file
2177 * The argument points to a buffer where the name of the
2178 * user whose home directory is sought is currently.
2179 * We return home directory of the user, or NULL.
2180 */
2181Char *
2182gethdir(const Char *home)
2183{
2184    Char   *h;
2185
2186    /*
2187     * Is it us?
2188     */
2189    if (*home == '\0') {
2190	if ((h = varval(STRhome)) != STRNULL)
2191	    return Strsave(h);
2192	else
2193	    return NULL;
2194    }
2195
2196    /*
2197     * Look in the cache
2198     */
2199    if ((h = gettilde(home)) == NULL)
2200	return NULL;
2201    else
2202	return Strsave(h);
2203}
2204
2205/*
2206 * Move the initial descriptors to their eventual
2207 * resting places, closing all other units.
2208 */
2209void
2210initdesc(void)
2211{
2212#ifdef NLS_BUGS
2213#ifdef NLS_CATALOGS
2214    nlsclose();
2215#endif /* NLS_CATALOGS */
2216#endif /* NLS_BUGS */
2217
2218
2219    didfds = 0;			/* 0, 1, 2 aren't set up */
2220    (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1);
2221    (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1);
2222    (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1);
2223    (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1);
2224#ifndef CLOSE_ON_EXEC
2225    didcch = 0;			/* Havent closed for child */
2226#endif /* CLOSE_ON_EXEC */
2227    isdiagatty = isatty(SHDIAG);
2228    isoutatty = isatty(SHOUT);
2229#ifdef NLS_BUGS
2230#ifdef NLS_CATALOGS
2231    nlsinit();
2232#endif /* NLS_CATALOGS */
2233#endif /* NLS_BUGS */
2234}
2235
2236
2237void
2238#ifdef PROF
2239done(int i)
2240#else
2241xexit(int i)
2242#endif
2243{
2244#ifdef TESLA
2245    if (loginsh && do_logout) {
2246	/* this is to send hangup signal to the develcon */
2247	/* we toggle DTR. clear dtr - sleep 1 - set dtr */
2248	/* ioctl will return ENOTTY for pty's but we ignore it 	 */
2249	/* exitstat will run after disconnect */
2250	/* we sleep for 2 seconds to let things happen in */
2251	/* .logout and rechist() */
2252#ifdef TIOCCDTR
2253	(void) sleep(2);
2254	(void) ioctl(FSHTTY, TIOCCDTR, NULL);
2255	(void) sleep(1);
2256	(void) ioctl(FSHTTY, TIOCSDTR, NULL);
2257#endif /* TIOCCDTR */
2258    }
2259#endif /* TESLA */
2260
2261    {
2262	struct process *pp, *np;
2263
2264	/* Kill all processes marked for hup'ing */
2265	for (pp = proclist.p_next; pp; pp = pp->p_next) {
2266	    np = pp;
2267	    do
2268		if ((np->p_flags & PHUP) && np->p_jobid != shpgrp) {
2269		    if (killpg(np->p_jobid, SIGHUP) != -1) {
2270			/* In case the job was suspended... */
2271#ifdef SIGCONT
2272			(void) killpg(np->p_jobid, SIGCONT);
2273#endif
2274			break;
2275		    }
2276		}
2277	    while ((np = np->p_friends) != pp);
2278	}
2279    }
2280    untty();
2281#ifdef NLS_CATALOGS
2282    /*
2283     * We need to call catclose, because SVR4 leaves symlinks behind otherwise
2284     * in the catalog directories. We cannot close on a vforked() child,
2285     * because messages will stop working on the parent too.
2286     */
2287    if (child == 0)
2288	nlsclose();
2289#endif /* NLS_CATALOGS */
2290#ifdef WINNT_NATIVE
2291    nt_cleanup();
2292#endif /* WINNT_NATIVE */
2293    _exit(i);
2294}
2295
2296#ifndef _PATH_DEFPATH
2297static Char **
2298defaultpath(void)
2299{
2300    char   *ptr;
2301    Char  **blk, **blkp;
2302    struct stat stb;
2303
2304    blkp = blk = xmalloc(sizeof(Char *) * 10);
2305
2306#ifndef NODOT
2307# ifndef DOTLAST
2308    *blkp++ = Strsave(STRdot);
2309# endif
2310#endif
2311
2312#define DIRAPPEND(a)  \
2313	if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
2314		*blkp++ = SAVE(ptr)
2315
2316#ifdef _PATH_LOCAL
2317    DIRAPPEND(_PATH_LOCAL);
2318#endif
2319
2320#ifdef _PATH_USRUCB
2321    DIRAPPEND(_PATH_USRUCB);
2322#endif
2323
2324#ifdef _PATH_USRBSD
2325    DIRAPPEND(_PATH_USRBSD);
2326#endif
2327
2328#ifdef _PATH_BIN
2329    DIRAPPEND(_PATH_BIN);
2330#endif
2331
2332#ifdef _PATH_USRBIN
2333    DIRAPPEND(_PATH_USRBIN);
2334#endif
2335
2336#undef DIRAPPEND
2337
2338#ifndef NODOT
2339# ifdef DOTLAST
2340    *blkp++ = Strsave(STRdot);
2341# endif
2342#endif
2343    *blkp = NULL;
2344    return (blk);
2345}
2346#endif
2347
2348static void
2349record(void)
2350{
2351    if (!fast) {
2352	recdirs(NULL, adrof(STRsavedirs) != NULL);
2353	rechist(NULL, adrof(STRsavehist) != NULL);
2354    }
2355}
2356
2357/*
2358 * Grab the tty repeatedly, and give up if we are not in the correct
2359 * tty process group.
2360 */
2361int
2362grabpgrp(int fd, pid_t desired)
2363{
2364    struct sigaction old;
2365    pid_t pgrp;
2366    size_t i;
2367
2368    for (i = 0; i < 100; i++) {
2369	if ((pgrp = tcgetpgrp(fd)) == -1)
2370	    return -1;
2371	if (pgrp == desired)
2372	    return 0;
2373	(void)sigaction(SIGTTIN, NULL, &old);
2374	(void)signal(SIGTTIN, SIG_DFL);
2375	(void)kill(0, SIGTTIN);
2376	(void)sigaction(SIGTTIN, &old, NULL);
2377    }
2378    errno = EPERM;
2379    return -1;
2380}
2381