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