1/*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
6/*	  All Rights Reserved  	*/
7
8/*
9 * Copyright (c) 1980 Regents of the University of California.
10 * All rights reserved.  The Berkeley Software License Agreement
11 * specifies the terms and conditions for redistribution.
12 */
13
14#include <locale.h>
15#include "sh.h"
16/* #include <sys/ioctl.h> */
17#include <fcntl.h>
18#include <sys/filio.h>
19#include "sh.tconst.h"
20#include <pwd.h>
21#include <stdlib.h>
22#ifdef	TRACE
23#include <stdio.h>
24#endif
25
26/*
27 * We use these csh(1) private versions of the select macros, (see select(3C))
28 * so as not to be limited by the size of struct fd_set (ie 1024).
29 */
30#define	CSH_FD_SET(n, p)   ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS)))
31#define	CSH_FD_CLR(n, p)   ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS)))
32#define	CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS)))
33#define	CSH_FD_ZERO(p, n)  memset((void *)(p), 0,  (n))
34
35tchar *pathlist[] =	{ S_usrbin /* "/usr/bin" */, S_DOT /* "." */, 0 };
36tchar *dumphist[] =	{ S_history /* "history" */, S_h /* "-h" */, 0, 0 };
37tchar *loadhist[] =	{ S_source /* "source" */, S_h /* "-h" */,
38    S_NDOThistory /* "~/.history" */, 0 };
39tchar HIST = '!';
40tchar HISTSUB = '^';
41int	nofile;
42bool	reenter;
43bool	nverbose;
44bool	nexececho;
45bool	quitit;
46bool	fast;
47bool	batch;
48bool	prompt = 1;
49bool	enterhist = 0;
50
51extern	gid_t getegid(), getgid();
52extern	uid_t geteuid(), getuid();
53extern tchar **strblktotsblk(/* char **, int */);
54
55extern void hupforegnd(void);
56void interactive_hup(void);
57void interactive_login_hup(void);
58
59void	importpath(tchar *);
60void	srccat(tchar *, tchar *);
61void	srccat_inlogin(tchar *, tchar *);
62void	srcunit(int, bool, bool);
63void	rechist(void);
64void	goodbye(void);
65void	pintr1(bool);
66void	process(bool);
67void	dosource(tchar **);
68void	mailchk(void);
69void	printprompt(void);
70void	sigwaiting(void);
71void	siglwp(void);
72void	initdesc(int, char *[]);
73void	initdesc_x(int, char *[], int);
74void	closem(void);
75void	unsetfd(int);
76void	phup(void);
77
78#ifdef	TRACE
79FILE *trace;
80/*
81 * Trace routines
82 */
83#define	TRACEFILE	"/tmp/trace.XXXXXX"
84
85/*
86 * Initialize trace file.
87 * Called from main.
88 */
89void
90trace_init(void)
91{
92	char name[128];
93	char *p;
94
95	strcpy(name, TRACEFILE);
96	p = mktemp(name);
97	trace = fopen(p, "w");
98}
99
100/*
101 * write message to trace file
102 */
103/*VARARGS1*/
104void
105tprintf(fmt, a, b, c, d, e, f, g, h, i, j)
106	char *fmt;
107{
108	if (trace) {
109		fprintf(trace, fmt, a, b, c, d, e, f, g, h, i, j);
110		fflush(trace);
111	}
112}
113#endif
114
115int
116main(int c, char **av)
117{
118	tchar **v, *cp, *r;
119	int f;
120	struct sigvec osv;
121	struct sigaction sa;
122	tchar s_prompt[MAXHOSTNAMELEN+3];
123	char *c_max_var_len;
124	int c_max_var_len_size;
125
126	/*
127	 * set up the error exit, if there is an error before
128	 * this is done, it will core dump, and we don't
129	 * tolerate core dumps
130	 */
131	haderr = 0;
132	setexit();
133	if (haderr) {
134		/*
135		 *  if were here, there was an error in the csh
136		 *  startup so just punt
137		 */
138		printf("csh startup error, csh exiting...\n");
139		flush();
140		exitstat();
141	}
142
143
144	(void) setlocale(LC_ALL, "");
145#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
146#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
147#endif
148	(void) textdomain(TEXT_DOMAIN);
149
150	/* Copy arguments */
151	v = strblktotsblk(av, c);
152
153	/*
154	 * Initialize paraml list
155	 */
156	paraml.next = paraml.prev = &paraml;
157
158	settimes();			/* Immed. estab. timing base */
159
160	if (eq(v[0], S_aout /* "a.out" */))	/* A.out's are quittable */
161		quitit = 1;
162	uid = getuid();
163	loginsh = **v == '-';
164	if (loginsh)
165		(void) time(&chktim);
166
167	/*
168	 * Move the descriptors to safe places.
169	 * The variable didfds is 0 while we have only FSH* to work with.
170	 * When didfds is true, we have 0,1,2 and prefer to use these.
171	 *
172	 * Also, setup data for csh internal file descriptor book keeping.
173	 */
174	initdesc(c, av);
175
176	/*
177	 * Initialize the shell variables.
178	 * ARGV and PROMPT are initialized later.
179	 * STATUS is also munged in several places.
180	 * CHILD is munged when forking/waiting
181	 */
182
183	c_max_var_len_size = snprintf(NULL, 0, "%ld", MAX_VAR_LEN);
184	c_max_var_len = (char *)xalloc(c_max_var_len_size + 1);
185	(void) snprintf(c_max_var_len, (c_max_var_len_size + 1),
186	    "%ld", MAX_VAR_LEN);
187	set(S_SUNW_VARLEN,  strtots(NOSTR, c_max_var_len));
188	xfree(c_max_var_len);
189
190	/* don't do globbing here, just set exact copies */
191	setNS(S_noglob);
192
193	set(S_status /* "status" */, S_0 /* "0" */);
194	dinit(cp = getenvs_("HOME"));	/* dinit thinks that HOME==cwd in a */
195					/* login shell */
196	if (cp == NOSTR)
197		fast++;			/* No home -> can't read scripts */
198	else {
199		if (strlen_(cp) >= BUFSIZ - 10) {
200			cp = NOSTR;
201			fast++;
202			printf("%s\n", gettext("Pathname too long"));
203			set(S_home /* "home" */, savestr(cp));
204			local_setenv(S_HOME, savestr(cp));
205		}
206		set(S_home /* "home" */, savestr(cp));
207	}
208	/*
209	 * Grab other useful things from the environment.
210	 * Should we grab everything??
211	 */
212	if ((cp = getenvs_("USER")) != NOSTR)
213		set(S_user /* "user" */, savestr(cp));
214	else {
215		/*
216		 * If USER is not defined, set it here.
217		 */
218		struct passwd *pw;
219		pw = getpwuid(getuid());
220
221		if (pw != NULL) {
222			set(S_user, strtots((tchar *)0, pw->pw_name));
223			local_setenv(S_USER, strtots((tchar *)0, pw->pw_name));
224		} else if (loginsh) { /* Give up setting USER variable. */
225	printf("Warning: USER environment variable could not be set.\n");
226		}
227	}
228	if ((cp = getenvs_("TERM")) != NOSTR)
229		set(S_term /* "term" */, savestr(cp));
230	/*
231	 * Re-initialize path if set in environment
232	 */
233	if ((cp = getenvs_("PATH")) == NOSTR)
234		set1(S_path /* "path" */, saveblk(pathlist), &shvhed);
235	else
236		importpath(cp);
237	set(S_shell /* "shell" */, S_SHELLPATH);
238
239	doldol = putn(getpid());		/* For $$ */
240
241	/* restore globbing until the user says otherwise */
242	unsetv(S_noglob);
243
244	/*
245	 * Record the interrupt states from the parent process.
246	 * If the parent is non-interruptible our hand must be forced
247	 * or we (and our children) won't be either.
248	 * Our children inherit termination from our parent.
249	 * We catch it only if we are the login shell.
250	 */
251		/* parents interruptibility */
252	(void) sigvec(SIGINT, (struct sigvec *)0, &osv);
253	parintr = osv.sv_handler;
254		/* parents terminability */
255	(void) sigvec(SIGTERM, (struct sigvec *)0, &osv);
256	parterm = osv.sv_handler;
257
258	_signal(SIGLWP, siglwp);
259	_signal(SIGWAITING, sigwaiting);
260	if (loginsh) {
261		(void) signal(SIGHUP, phup);	/* exit processing on HUP */
262		(void) signal(SIGXCPU, phup);	/* ...and on XCPU */
263		(void) signal(SIGXFSZ, phup);	/* ...and on XFSZ */
264	}
265
266	/*
267	 * Process the arguments.
268	 *
269	 * Note that processing of -v/-x is actually delayed till after
270	 * script processing.
271	 */
272	c--, v++;
273	while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) {
274		do switch (*cp++) {
275
276		case 'b':		/* -b	Next arg is input file */
277			batch++;
278			break;
279
280		case 'c':		/* -c	Command input from arg */
281			if (c == 1)
282				exit(0);
283			c--, v++;
284			arginp = v[0];
285			prompt = 0;
286			nofile++;
287			cflg++;
288			break;
289
290		case 'e':		/* -e	Exit on any error */
291			exiterr++;
292			break;
293
294		case 'f':		/* -f	Fast start */
295			fast++;
296			break;
297
298		case 'i':		/* -i	Interactive, even if !intty */
299			intact++;
300			nofile++;
301			break;
302
303		case 'n':		/* -n	Don't execute */
304			noexec++;
305			break;
306
307		case 'q':		/* -q	(Undoc'd) ... die on quit */
308			quitit = 1;
309			break;
310
311		case 's':		/* -s	Read from std input */
312			nofile++;
313			break;
314
315		case 't':		/* -t	Read one line from input */
316			onelflg = 2;
317			prompt = 0;
318			nofile++;
319			break;
320#ifdef TRACE
321		case 'T':		/* -T 	trace switch on */
322			trace_init();
323			break;
324#endif
325
326		case 'v':		/* -v	Echo hist expanded input */
327			nverbose = 1;			/* ... later */
328			break;
329
330		case 'x':		/* -x	Echo just before execution */
331			nexececho = 1;			/* ... later */
332			break;
333
334		case 'V':		/* -V	Echo hist expanded input */
335			setNS(S_verbose /* "verbose" */);	/* NOW! */
336			break;
337
338		case 'X':		/* -X	Echo just before execution */
339			setNS(S_echo /* "echo" */);		/* NOW! */
340			break;
341
342		} while (*cp);
343		v++, c--;
344	}
345
346	if (quitit)			/* With all due haste, for debugging */
347		(void) signal(SIGQUIT, SIG_DFL);
348
349	/*
350	 * Unless prevented by -c, -i, -s, or -t, if there
351	 * are remaining arguments the first of them is the name
352	 * of a shell file from which to read commands.
353	 */
354	if (!batch && (uid != geteuid() || getgid() != getegid())) {
355		errno = EACCES;
356		child++;			/* So this ... */
357		Perror(S_csh /* "csh" */);	/* ... doesn't return */
358	}
359
360	if (nofile == 0 && c > 0) {
361		nofile = open_(v[0], 0);
362		if (nofile < 0) {
363			child++;		/* So this ... */
364			Perror(v[0]);		/* ... doesn't return */
365		}
366		file = v[0];
367		SHIN = dmove(nofile, FSHIN);	/* Replace FSHIN */
368		(void) fcntl(SHIN, F_SETFD, 1);
369		prompt = 0;
370		c--, v++;
371	}
372
373	/*
374	 * Consider input a tty if it really is or we are interactive.
375	 */
376	intty = intact || isatty(SHIN);
377
378	/*
379	 * Decide whether we should play with signals or not.
380	 * If we are explicitly told (via -i, or -) or we are a login
381	 * shell (arg0 starts with -) or the input and output are both
382	 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
383	 * Note that in only the login shell is it likely that parent
384	 * may have set signals to be ignored
385	 */
386	if (loginsh || intact || intty && isatty(SHOUT))
387		setintr = 1;
388#ifdef TELL
389	settell();
390#endif
391	/*
392	 * Save the remaining arguments in argv.
393	 */
394	setq(S_argv /* "argv" */, copyblk(v), &shvhed);
395
396	/*
397	 * Set up the prompt.
398	 */
399	if (prompt) {
400		gethostname_(s_prompt, MAXHOSTNAMELEN);
401		strcat_(s_prompt,
402		    uid == 0 ? S_SHARPSP /* "# " */ : S_PERSENTSP /* "% " */);
403		set(S_prompt /* "prompt" */, s_prompt);
404	}
405
406	/*
407	 * If we are an interactive shell, then start fiddling
408	 * with the signals; this is a tricky game.
409	 */
410	shpgrp = getpgid(0);
411	opgrp = tpgrp = -1;
412	if (setintr) {
413		**av = '-';
414		if (!quitit)		/* Wary! */
415			(void) signal(SIGQUIT, SIG_IGN);
416		(void) signal(SIGINT, pintr);
417		(void) sigblock(sigmask(SIGINT));
418		(void) signal(SIGTERM, SIG_IGN);
419
420		/*
421		 * Explicitly terminate foreground jobs and exit if we are
422		 * interactive shell
423		 */
424		if (loginsh) {
425			(void) signal(SIGHUP, interactive_login_hup);
426		} else {
427			(void) signal(SIGHUP, interactive_hup);
428		}
429
430		if (quitit == 0 && arginp == 0) {
431			(void) signal(SIGTSTP, SIG_IGN);
432			(void) signal(SIGTTIN, SIG_IGN);
433			(void) signal(SIGTTOU, SIG_IGN);
434			/*
435			 * Wait till in foreground, in case someone
436			 * stupidly runs
437			 *	csh &
438			 * dont want to try to grab away the tty.
439			 */
440			if (isatty(FSHDIAG))
441				f = FSHDIAG;
442			else if (isatty(FSHOUT))
443				f = FSHOUT;
444			else if (isatty(OLDSTD))
445				f = OLDSTD;
446			else
447				f = -1;
448retry:
449			if (ioctl(f, TIOCGPGRP,  (char *)&tpgrp) == 0 &&
450			    tpgrp != -1) {
451				if (tpgrp != shpgrp) {
452					void (*old)() = (void (*)())
453					    signal(SIGTTIN, SIG_DFL);
454					(void) kill(0, SIGTTIN);
455					(void) signal(SIGTTIN, old);
456					goto retry;
457				}
458				opgrp = shpgrp;
459				shpgrp = getpid();
460				tpgrp = shpgrp;
461				(void) setpgid(0, shpgrp);
462				(void) ioctl(f, TIOCSPGRP,  (char *)&shpgrp);
463				(void) fcntl(dcopy(f, FSHTTY), F_SETFD, 1);
464			} else {
465notty:
466printf("Warning: no access to tty; thus no job control in this shell...\n");
467				tpgrp = -1;
468			}
469		}
470	}
471	if (setintr == 0 && parintr == SIG_DFL)
472		setintr++;
473
474	/*
475	 * Set SIGCHLD handler, making sure that reads restart after it runs.
476	 */
477	sigemptyset(&sa.sa_mask);
478	sa.sa_handler = pchild;
479	sa.sa_flags = SA_RESTART;
480	(void) sigaction(SIGCHLD, &sa, (struct sigaction *)NULL);
481
482	/*
483	 * Set an exit here in case of an interrupt or error reading
484	 * the shell start-up scripts.
485	 */
486	setexit();
487	haderr = 0;		/* In case second time through */
488	if (!fast && reenter == 0) {
489		reenter++;
490
491		/*
492		 * If this is a login csh, and /etc/.login exists,
493		 * source /etc/.login first.
494		 */
495		if (loginsh) {
496			tchar tmp_etc[4+1];	/* strlen("/etc")+1 */
497			tchar tmp_login[7+1];	/* strlen("/.login")+1 */
498
499			strtots(tmp_etc, "/etc");
500			strtots(tmp_login, "/.login");
501			srccat_inlogin(tmp_etc, tmp_login);
502		}
503
504		/* Will have value("home") here because set fast if don't */
505		srccat(value(S_home /* "home" */),
506		    S_SLADOTcshrc /* "/.cshrc" */);
507
508		/* Hash path */
509		if (!fast && !arginp && !onelflg && !havhash)
510			dohash(xhash);
511
512
513		/*
514		 * Reconstruct the history list now, so that it's
515		 * available from within .login.
516		 */
517		dosource(loadhist);
518		if (loginsh) {
519			srccat_inlogin(value(S_home /* "home" */),
520			    S_SLADOTlogin /* "/.login" */);
521		}
522
523		/*
524		 * To get cdpath hashing $cdpath must have a
525		 * value, not $CDPATH.  So if after reading
526		 * the startup files ( .cshrc ), and
527		 * user has specified a value for cdpath, then
528		 * cache $cdpath paths. xhash2 is global array
529		 * for $cdpath caching.
530		 */
531		if (!fast && !arginp && !onelflg && !havhash2)
532				dohash(xhash2);
533	}
534
535	/*
536	 * Now are ready for the -v and -x flags
537	 */
538	if (nverbose)
539		setNS(S_verbose /* "verbose" */);
540	if (nexececho)
541		setNS(S_echo /* "echo" */);
542
543	/*
544	 * All the rest of the world is inside this call.
545	 * The argument to process indicates whether it should
546	 * catch "error unwinds".  Thus if we are a interactive shell
547	 * our call here will never return by being blown past on an error.
548	 */
549	process(setintr);
550
551	/*
552	 * Mop-up.
553	 */
554	if (loginsh) {
555		printf("logout\n");
556		(void) close(SHIN);	/* No need for unsetfd(). */
557		child++;
558		goodbye();
559	}
560	rechist();
561	exitstat();
562}
563
564void
565untty(void)
566{
567
568	if (tpgrp > 0) {
569		(void) setpgid(0, opgrp);
570		(void) ioctl(FSHTTY, TIOCSPGRP,  (char *)&opgrp);
571	}
572}
573
574void
575importpath(tchar *cp)
576{
577	int i = 0;
578	tchar *dp;
579	tchar **pv;
580	int c;
581	static tchar dot[2] = {'.', 0};
582
583	for (dp = cp; *dp; dp++)
584		if (*dp == ':')
585			i++;
586	/*
587	 * i+2 where i is the number of colons in the path.
588	 * There are i+1 directories in the path plus we need
589	 * room for a zero terminator.
590	 */
591	pv =  (tchar **)xcalloc((unsigned)(i + 2), sizeof (tchar **));
592	dp = cp;
593	i = 0;
594	if (*dp)
595	for (;;) {
596		if ((c = *dp) == ':' || c == 0) {
597			*dp = 0;
598			pv[i++] = savestr(*cp ? cp : dot);
599			if (c) {
600				cp = dp + 1;
601				*dp = ':';
602			} else
603				break;
604		}
605		dp++;
606	}
607	pv[i] = 0;
608	set1(S_path /* "path" */, pv, &shvhed);
609}
610
611/*
612 * Source to the file which is the catenation of the argument names.
613 */
614void
615srccat(tchar *cp, tchar *dp)
616{
617	tchar *ep = strspl(cp, dp);
618	int unit = dmove(open_(ep, 0), -1);
619
620	(void) fcntl(unit, F_SETFD, 1);
621	xfree(ep);
622#ifdef INGRES
623	srcunit(unit, 0, 0);
624#else
625	srcunit(unit, 1, 0);
626#endif
627}
628
629/*
630 * Source to the file which is the catenation of the argument names.
631 * 	This one does not check the ownership.
632 */
633void
634srccat_inlogin(tchar *cp, tchar *dp)
635{
636	tchar *ep = strspl(cp, dp);
637	int unit = dmove(open_(ep, 0), -1);
638
639	(void) fcntl(unit, F_SETFD, 1);
640	xfree(ep);
641	srcunit(unit, 0, 0);
642}
643
644/*
645 * Source to a unit.  If onlyown it must be our file or our group or
646 * we don't chance it.	This occurs on ".cshrc"s and the like.
647 */
648void
649srcunit(int unit, bool onlyown, bool hflg)
650{
651	/* We have to push down a lot of state here */
652	/* All this could go into a structure */
653	int oSHIN = -1, oldintty = intty;
654	struct whyle *oldwhyl = whyles;
655	tchar *ogointr = gointr, *oarginp = arginp;
656	tchar *oevalp = evalp, **oevalvec = evalvec;
657	int oonelflg = onelflg;
658	bool oenterhist = enterhist;
659	tchar OHIST = HIST;
660#ifdef TELL
661	bool otell = cantell;
662#endif
663	struct Bin saveB;
664
665	/* The (few) real local variables */
666	jmp_buf oldexit;
667	int reenter, omask;
668
669	if (unit < 0)
670		return;
671	if (didfds)
672		donefds();
673	if (onlyown) {
674		struct stat stb;
675
676		if (fstat(unit, &stb) < 0 ||
677		    (stb.st_uid != uid && stb.st_gid != getgid())) {
678			(void) close(unit);
679			unsetfd(unit);
680			return;
681		}
682	}
683
684	/*
685	 * There is a critical section here while we are pushing down the
686	 * input stream since we have stuff in different structures.
687	 * If we weren't careful an interrupt could corrupt SHIN's Bin
688	 * structure and kill the shell.
689	 *
690	 * We could avoid the critical region by grouping all the stuff
691	 * in a single structure and pointing at it to move it all at
692	 * once.  This is less efficient globally on many variable references
693	 * however.
694	 */
695	getexit(oldexit);
696	reenter = 0;
697	if (setintr)
698		omask = sigblock(sigmask(SIGINT));
699	setexit();
700	reenter++;
701	if (reenter == 1) {
702		/* Setup the new values of the state stuff saved above */
703		copy((char *)&saveB, (char *)&B, sizeof (saveB));
704		fbuf =  (tchar **) 0;
705		fseekp = feobp = fblocks = 0;
706		oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
707		intty = isatty(SHIN), whyles = 0, gointr = 0;
708		evalvec = 0; evalp = 0;
709		enterhist = hflg;
710		if (enterhist)
711			HIST = '\0';
712		/*
713		 * Now if we are allowing commands to be interrupted,
714		 * we let ourselves be interrupted.
715		 */
716		if (setintr)
717			(void) sigsetmask(omask);
718#ifdef TELL
719		settell();
720#endif
721		process(0);		/* 0 -> blow away on errors */
722	}
723	if (setintr)
724		(void) sigsetmask(omask);
725	if (oSHIN >= 0) {
726		int i;
727
728		/* We made it to the new state... free up its storage */
729		/* This code could get run twice but xfree doesn't care */
730		for (i = 0; i < fblocks; i++)
731			xfree(fbuf[i]);
732		xfree((char *)fbuf);
733
734		/* Reset input arena */
735		copy((char *)&B, (char *)&saveB, sizeof (B));
736
737		(void) close(SHIN), SHIN = oSHIN;
738		unsetfd(SHIN);
739		arginp = oarginp, onelflg = oonelflg;
740		evalp = oevalp, evalvec = oevalvec;
741		intty = oldintty, whyles = oldwhyl, gointr = ogointr;
742		if (enterhist)
743			HIST = OHIST;
744		enterhist = oenterhist;
745#ifdef TELL
746		cantell = otell;
747#endif
748	}
749
750	resexit(oldexit);
751	/*
752	 * If process reset() (effectively an unwind) then
753	 * we must also unwind.
754	 */
755	if (reenter >= 2)
756		error(NULL);
757}
758
759void
760rechist(void)
761{
762	tchar buf[BUFSIZ];
763	int fp, ftmp, oldidfds;
764
765	if (!fast) {
766		if (value(S_savehist /* "savehist" */)[0] == '\0')
767			return;
768		(void) strcpy_(buf, value(S_home /* "home" */));
769		(void) strcat_(buf, S_SLADOThistory /* "/.history" */);
770		fp = creat_(buf, 0666);
771		if (fp == -1)
772			return;
773		oldidfds = didfds;
774		didfds = 0;
775		ftmp = SHOUT;
776		SHOUT = fp;
777		(void) strcpy_(buf, value(S_savehist /* "savehist" */));
778		dumphist[2] = buf;
779		dohist(dumphist);
780		(void) close(fp);
781		unsetfd(fp);
782		SHOUT = ftmp;
783		didfds = oldidfds;
784	}
785}
786
787void
788goodbye(void)
789{
790	if (loginsh) {
791		(void) signal(SIGQUIT, SIG_IGN);
792		(void) signal(SIGINT, SIG_IGN);
793		(void) signal(SIGTERM, SIG_IGN);
794		setintr = 0;		/* No interrupts after "logout" */
795		if (adrof(S_home /* "home" */))
796			srccat(value(S_home /* "home" */),
797			    S_SLADOTlogout /* "/.logout" */);
798	}
799	rechist();
800	exitstat();
801}
802
803void
804exitstat(void)
805{
806
807#ifdef PROF
808	monitor(0);
809#endif
810	/*
811	 * Note that if STATUS is corrupted (i.e. getn bombs)
812	 * then error will exit directly because we poke child here.
813	 * Otherwise we might continue unwarrantedly (sic).
814	 */
815	child++;
816	untty();
817	exit(getn(value(S_status /* "status" */)));
818}
819
820/*
821 * in the event of a HUP we want to save the history
822 */
823void
824phup(void)
825{
826	rechist();
827	exit(1);
828}
829
830void
831interactive_hup(void)
832{
833	hupforegnd();
834	exit(1);
835}
836
837void
838interactive_login_hup(void)
839{
840	rechist();
841	hupforegnd();
842	exit(1);
843}
844
845tchar *jobargv[2] = { S_jobs /* "jobs" */, 0 };
846/*
847 * Catch an interrupt, e.g. during lexical input.
848 * If we are an interactive shell, we reset the interrupt catch
849 * immediately.  In any case we drain the shell output,
850 * and finally go through the normal error mechanism, which
851 * gets a chance to make the shell go away.
852 */
853void
854pintr(void)
855{
856	pintr1(1);
857}
858
859void
860pintr1(bool wantnl)
861{
862	tchar **v;
863	int omask;
864
865	omask = sigblock(0);
866	if (setintr) {
867		(void) sigsetmask(omask & ~sigmask(SIGINT));
868		if (pjobs) {
869			pjobs = 0;
870			printf("\n");
871			dojobs(jobargv);
872			bferr("Interrupted");
873		}
874	}
875	(void) sigsetmask(omask & ~sigmask(SIGCHLD));
876	draino();
877
878	/*
879	 * If we have an active "onintr" then we search for the label.
880	 * Note that if one does "onintr -" then we shan't be interruptible
881	 * so we needn't worry about that here.
882	 */
883	if (gointr) {
884		search(ZGOTO, 0, gointr);
885		timflg = 0;
886		if (v = pargv)
887			pargv = 0, blkfree(v);
888		if (v = gargv)
889			gargv = 0, blkfree(v);
890		reset();
891	} else if (intty && wantnl)
892		printf("\n");		/* Some like this, others don't */
893	error(NULL);
894}
895
896/*
897 * Process is the main driving routine for the shell.
898 * It runs all command processing, except for those within { ... }
899 * in expressions (which is run by a routine evalav in sh.exp.c which
900 * is a stripped down process), and `...` evaluation which is run
901 * also by a subset of this code in sh.glob.c in the routine backeval.
902 *
903 * The code here is a little strange because part of it is interruptible
904 * and hence freeing of structures appears to occur when none is necessary
905 * if this is ignored.
906 *
907 * Note that if catch is not set then we will unwind on any error.
908 * If an end-of-file occurs, we return.
909 */
910void
911process(bool catch)
912{
913	jmp_buf osetexit;
914	struct command *t;
915
916	getexit(osetexit);
917	for (;;) {
918		pendjob();
919		freelex(&paraml);
920		paraml.next = paraml.prev = &paraml;
921		paraml.word = S_ /* "" */;
922		t = 0;
923		setexit();
924		justpr = enterhist;	/* execute if not entering history */
925
926		/*
927		 * Interruptible during interactive reads
928		 */
929		if (setintr)
930			(void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
931
932		/*
933		 * For the sake of reset()
934		 */
935		freelex(&paraml), freesyn(t), t = 0;
936
937		if (haderr) {
938			if (!catch) {
939				/* unwind */
940				doneinp = 0;
941				resexit(osetexit);
942				reset();
943			}
944			haderr = 0;
945			/*
946			 * Every error is eventually caught here or
947			 * the shell dies.  It is at this
948			 * point that we clean up any left-over open
949			 * files, by closing all but a fixed number
950			 * of pre-defined files.  Thus routines don't
951			 * have to worry about leaving files open due
952			 * to deeper errors... they will get closed here.
953			 */
954			closem();
955			continue;
956		}
957		if (doneinp) {
958			doneinp = 0;
959			break;
960		}
961		if (chkstop)
962			chkstop--;
963		if (neednote)
964			pnote();
965		if (intty && prompt && evalvec == 0) {
966			mailchk();
967			/*
968			 * If we are at the end of the input buffer
969			 * then we are going to read fresh stuff.
970			 * Otherwise, we are rereading input and don't
971			 * need or want to prompt.
972			 */
973			if (fseekp == feobp)
974				printprompt();
975		}
976		err = 0;
977
978		/*
979		 * Echo not only on VERBOSE, but also with history expansion.
980		 */
981		if (lex(&paraml) && intty ||
982		    adrof(S_verbose /* "verbose" */)) {
983			haderr = 1;
984			prlex(&paraml);
985			haderr = 0;
986		}
987
988		/*
989		 * The parser may lose space if interrupted.
990		 */
991		if (setintr)
992			(void) sigblock(sigmask(SIGINT));
993
994		/*
995		 * Save input text on the history list if
996		 * reading in old history, or it
997		 * is from the terminal at the top level and not
998		 * in a loop.
999		 */
1000		if (enterhist || catch && intty && !whyles)
1001			savehist(&paraml);
1002
1003		/*
1004		 * Print lexical error messages, except when sourcing
1005		 * history lists.
1006		 */
1007		if (!enterhist && err)
1008			error("%s", gettext(err));
1009
1010		/*
1011		 * If had a history command :p modifier then
1012		 * this is as far as we should go
1013		 */
1014		if (justpr)
1015			reset();
1016
1017		alias(&paraml);
1018
1019		/*
1020		 * Parse the words of the input into a parse tree.
1021		 */
1022		t = syntax(paraml.next, &paraml, 0);
1023		if (err)
1024			error("%s", gettext(err));
1025
1026		/*
1027		 * Execute the parse tree
1028		 */
1029		{
1030			/*
1031			 * POSIX requires SIGCHLD to be held
1032			 * until all processes have joined the
1033			 * process group in order to avoid race
1034			 * condition.
1035			 */
1036			int omask;
1037
1038			omask = sigblock(sigmask(SIGCHLD));
1039			execute(t, tpgrp);
1040			(void) sigsetmask(omask &~ sigmask(SIGCHLD));
1041		}
1042
1043		if (err)
1044			error("%s", gettext(err));
1045		/*
1046		 * Made it!
1047		 */
1048		freelex(&paraml), freesyn(t);
1049	}
1050	resexit(osetexit);
1051}
1052
1053void
1054dosource(tchar **t)
1055{
1056	tchar *f;
1057	int u;
1058	bool hflg = 0;
1059	tchar buf[BUFSIZ];
1060
1061	t++;
1062	if (*t && eq(*t, S_h /* "-h" */)) {
1063		if (*++t == NOSTR)
1064			bferr("Too few arguments.");
1065		hflg++;
1066	}
1067	(void) strcpy_(buf, *t);
1068	f = globone(buf);
1069	u = dmove(open_(f, 0), -1);
1070	xfree(f);
1071	freelex(&paraml);
1072	if (u < 0 && !hflg)
1073		Perror(f);
1074	(void) fcntl(u, F_SETFD, 1);
1075	srcunit(u, 0, hflg);
1076}
1077
1078/*
1079 * Check for mail.
1080 * If we are a login shell, then we don't want to tell
1081 * about any mail file unless its been modified
1082 * after the time we started.
1083 * This prevents us from telling the user things he already
1084 * knows, since the login program insists on saying
1085 * "You have mail."
1086 */
1087void
1088mailchk(void)
1089{
1090	struct varent *v;
1091	tchar **vp;
1092	time_t t;
1093	int intvl, cnt;
1094	struct stat stb;
1095	bool new;
1096
1097	v = adrof(S_mail /* "mail" */);
1098	if (v == 0)
1099		return;
1100	(void) time(&t);
1101	vp = v->vec;
1102	cnt = blklen(vp);
1103	intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1104	if (intvl < 1)
1105		intvl = 1;
1106	if (chktim + intvl > t)
1107		return;
1108	for (; *vp; vp++) {
1109		if (stat_(*vp, &stb) < 0)
1110			continue;
1111		new = stb.st_mtime > time0.tv_sec;
1112		if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime ||
1113		    (stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
1114		    loginsh && !new)
1115			continue;
1116		if (cnt == 1)
1117			printf("You have %smail.\n", new ? "new " : "");
1118		else
1119			printf("%s in %t.\n", new ? "New mail" : "Mail", *vp);
1120	}
1121	chktim = t;
1122}
1123
1124/*
1125 * Extract a home directory from the password file
1126 * The argument points to a buffer where the name of the
1127 * user whose home directory is sought is currently.
1128 * We write the home directory of the user back there.
1129 */
1130int
1131gethdir(tchar *home)
1132{
1133	/* getpwname will not be modified, so we need temp. buffer */
1134	char home_str[BUFSIZ];
1135	tchar home_ts[BUFSIZ];
1136	struct passwd *pp /* = getpwnam(home) */;
1137
1138	pp = getpwnam(tstostr(home_str, home));
1139	if (pp == 0)
1140		return (1);
1141	(void) strcpy_(home, strtots(home_ts, pp->pw_dir));
1142	return (0);
1143}
1144
1145
1146#if 0
1147void
1148#ifdef PROF
1149done(int i)
1150#else
1151exit(int i)
1152#endif
1153{
1154
1155	untty();
1156	_exit(i);
1157}
1158#endif
1159
1160void
1161printprompt(void)
1162{
1163	tchar *cp;
1164
1165	if (!whyles) {
1166		/*
1167		 * Print the prompt string
1168		 */
1169		for (cp = value(S_prompt /* "prompt" */); *cp; cp++)
1170			if (*cp == HIST)
1171				printf("%d", eventno + 1);
1172			else {
1173				if (*cp == '\\' && cp[1] == HIST)
1174					cp++;
1175				Putchar(*cp | QUOTE);
1176			}
1177	} else
1178		/*
1179		 * Prompt for forward reading loop
1180		 * body content.
1181		 */
1182		printf("? ");
1183	flush();
1184}
1185
1186/*
1187 * Save char * block.
1188 */
1189tchar **
1190strblktotsblk(char **v, int num)
1191{
1192	tchar **newv =
1193	    (tchar **)xcalloc((unsigned)(num+ 1), sizeof (tchar **));
1194	tchar **onewv = newv;
1195
1196	while (*v && num--)
1197		*newv++ = strtots(NOSTR, *v++);
1198	*newv = 0;
1199	return (onewv);
1200}
1201
1202void
1203sigwaiting(void)
1204{
1205	_signal(SIGWAITING, sigwaiting);
1206}
1207
1208void
1209siglwp(void)
1210{
1211	_signal(SIGLWP, siglwp);
1212}
1213
1214
1215/*
1216 * Following functions and data are used for csh to do its
1217 * file descriptors book keeping.
1218 */
1219
1220static int *fdinuse = NULL;	/* The list of files opened by csh */
1221static int nbytesused = 0;	/* no of bytes allocated to fdinuse */
1222static int max_fd = 0;		/* The maximum descriptor in fdinuse */
1223static int my_pid;		/* The process id set in initdesc() */
1224static int NoFile = NOFILE;	/* The number of files I can use. */
1225
1226/*
1227 * Get the number of files this csh can use.
1228 *
1229 * Move the initial descriptors to their eventual
1230 * resting places, closing all other units.
1231 *
1232 * Also, reserve 0/1/2, so NIS+ routines do not get
1233 * hold of them. And initialize fdinuse list and set
1234 * the current process id.
1235 *
1236 * If this csh was invoked from setuid'ed script file,
1237 * do not close the third argument passed. The file
1238 * must be one of /dev/fd/0,1,2,,,
1239 *	(execv() always passes three arguments when it execs a script
1240 *	 file in a form of #! /bin/csh -b.)
1241 *
1242 * If is_reinit is set in initdesc_x(), then we only close the file
1243 * descriptors that we actually opened (as recorded in fdinuse).
1244 */
1245void
1246initdesc(int argc, char *argv[])
1247{
1248	initdesc_x(argc, argv, 0);
1249}
1250
1251void
1252reinitdesc(int argc, char *argv[])
1253{
1254	initdesc_x(argc, argv, 1);
1255}
1256
1257/*
1258 * Callback functions for closing all file descriptors.
1259 */
1260static int
1261close_except(void *cd, int fd)
1262{
1263	int script_fd = *(int *)cd;
1264
1265	if (fd >= 3 && fd < NoFile && fd != script_fd)
1266		(void) close(fd);
1267	return (0);
1268}
1269
1270static int
1271close_inuse(void *cd, int fd)
1272{
1273	int script_fd = *(int *)cd;
1274
1275	if (fd >= 3 && fd < NoFile && fd != script_fd &&
1276	    CSH_FD_ISSET(fd, fdinuse)) {
1277		(void) close(fd);
1278		unsetfd(fd);
1279	}
1280	return (0);
1281}
1282
1283void
1284initdesc_x(int argc, char *argv[], int is_reinit)
1285{
1286
1287	int script_fd = -1;
1288	struct stat buf;
1289	struct rlimit rlp;
1290
1291	/*
1292	 * Get pid of this shell
1293	 */
1294	my_pid = getpid();
1295
1296	/*
1297	 * Get the hard limit numbers of descriptors
1298	 * this csh can use.
1299	 */
1300	if (getrlimit(RLIMIT_NOFILE, &rlp) == 0)
1301		NoFile = rlp.rlim_cur;
1302
1303	/*
1304	 * If this csh was invoked for executing setuid script file,
1305	 * the third argument passed is the special file name
1306	 * which should not be closed.  This special file name is
1307	 * in the form /dev/fd/X.
1308	 */
1309	if (argc >= 3)
1310		if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1)
1311			script_fd = -1;
1312		else
1313			/* Make sure to close this file on exec.  */
1314			fcntl(script_fd, F_SETFD, 1);
1315
1316	if (fdinuse == NULL) {
1317		nbytesused = sizeof (int) *
1318		    howmany(NoFile, sizeof (int) * NBBY);
1319		fdinuse = (int *)xalloc(nbytesused);
1320	}
1321
1322	/*
1323	 * Close all files except 0/1/2 to get a clean
1324	 * file descritor space.
1325	 */
1326	if (!is_reinit)
1327		(void) fdwalk(close_except, &script_fd);
1328	else
1329		(void) fdwalk(close_inuse, &script_fd);
1330
1331	didfds = 0;			/* 0, 1, 2 aren't set up */
1332
1333	if (fstat(0, &buf) < 0)
1334		open("/dev/null", 0);
1335
1336	(void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD,  1);
1337	(void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD,  1);
1338	(void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD,  1);
1339	(void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD,  1);
1340
1341	/*
1342	 * Open 0/1/2 to avoid Nis+ functions to pick them up.
1343	 *	Now, 0/1/2 are saved, close them and open them.
1344	 */
1345	close(0); close(1); close(2);
1346	open("/dev/null", 0);
1347	dup(0);
1348	dup(0);
1349
1350	/*
1351	 * Clear fd_set mask
1352	 */
1353	if (!is_reinit)
1354		CSH_FD_ZERO(fdinuse, nbytesused);
1355}
1356
1357/*
1358 * This routine is called after an error to close up
1359 * any units which may have been left open accidentally.
1360 *
1361 * You only need to remove files in fdinuse list.
1362 * After you have removed the files, you can clear the
1363 * list and max_fd.
1364 */
1365void
1366closem(void)
1367{
1368	int f;
1369
1370	for (f = 3; f <= max_fd; f++) {
1371		if (CSH_FD_ISSET(f, fdinuse) &&
1372		    f != SHIN && f != SHOUT && f != SHDIAG &&
1373		    f != OLDSTD && f != FSHTTY)
1374			close(f);
1375	}
1376	CSH_FD_ZERO(fdinuse, nbytesused);
1377	max_fd = 0;
1378}
1379
1380/*
1381 * Reset my_pid when a new process is created.  Only call this
1382 * if you want the process to affect fdinuse (e.g., fork, but
1383 * not vfork).
1384 */
1385void
1386new_process(void)
1387{
1388	my_pid = getpid();
1389}
1390
1391
1392/*
1393 * Whenever Csh open/create/dup/pipe a file or files,
1394 * Csh keeps track of its open files. The open files
1395 * are kept in "fdinuse, Fd In Use" list.
1396 *
1397 * When a file descriptor is newly allocated, setfd() is
1398 * used to mark the fact in "fdinuse" list.
1399 *	For example,
1400 *		fd = open("newfile", 0);
1401 *		setfd(fd);
1402 *
1403 * When a file is freed by close() function, unsetfd() is
1404 * used to remove the fd from "fdinuse" list.
1405 *	For example,
1406 *		close(fd);
1407 *		unsetfd(fd);
1408 */
1409void
1410setfd(int fd)
1411{
1412	/*
1413	 * Because you want to avoid
1414	 * conflict due to vfork().
1415	 */
1416	if (my_pid != getpid())
1417		return;
1418
1419	if (fd >= NoFile || fd < 0)
1420		return;
1421
1422	if (fd > max_fd)
1423		max_fd = fd;
1424	CSH_FD_SET(fd, fdinuse);
1425}
1426
1427void
1428unsetfd(int fd)
1429{
1430	int i;
1431
1432	/*
1433	 * Because you want to avoid
1434	 * conflict due to vfork().
1435	 */
1436	if (my_pid != getpid())
1437		return;
1438
1439	if (fd >= NoFile || fd < 0)
1440		return;
1441
1442	CSH_FD_CLR(fd, fdinuse);
1443	if (fd == max_fd) {
1444		for (i = max_fd-1; i >= 3; i--)
1445			if (CSH_FD_ISSET(i, fdinuse)) {
1446				max_fd = i;
1447				return;
1448			}
1449		max_fd = 0;
1450	}
1451}
1452