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