xref: /original-bsd/bin/csh/csh.c (revision f052b07a)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley Software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)csh.c	5.6 (Berkeley) 05/19/88";
9 #endif
10 
11 #include "sh.h"
12 #include <sys/ioctl.h>
13 /*
14  * C Shell
15  *
16  * Bill Joy, UC Berkeley, California, USA
17  * October 1978, May 1980
18  *
19  * Jim Kulp, IIASA, Laxenburg, Austria
20  * April 1980
21  */
22 
23 char	*pathlist[] =	{ ".", "/usr/ucb", "/bin", "/usr/bin", 0 };
24 char	*dumphist[] =	{ "history", "-h", 0, 0 };
25 char	*loadhist[] =	{ "source", "-h", "~/.history", 0 };
26 char	HIST = '!';
27 char	HISTSUB = '^';
28 bool	nofile;
29 bool	reenter;
30 bool	nverbose;
31 bool	nexececho;
32 bool	quitit;
33 bool	fast;
34 bool	batch;
35 bool	prompt = 1;
36 bool	enterhist = 0;
37 
38 extern	gid_t getegid(), getgid();
39 extern	uid_t geteuid(), getuid();
40 
41 main(c, av)
42 	int c;
43 	char **av;
44 {
45 	register char **v, *cp;
46 	register int f;
47 	struct sigvec osv;
48 
49 	settimes();			/* Immed. estab. timing base */
50 	v = av;
51 	if (eq(v[0], "a.out"))		/* A.out's are quittable */
52 		quitit = 1;
53 	uid = getuid();
54 	loginsh = **v == '-' && c == 1;
55 	if (loginsh)
56 		(void) time(&chktim);
57 
58 	/*
59 	 * Move the descriptors to safe places.
60 	 * The variable didfds is 0 while we have only FSH* to work with.
61 	 * When didfds is true, we have 0,1,2 and prefer to use these.
62 	 */
63 	initdesc();
64 
65 	/*
66 	 * Initialize the shell variables.
67 	 * ARGV and PROMPT are initialized later.
68 	 * STATUS is also munged in several places.
69 	 * CHILD is munged when forking/waiting
70 	 */
71 
72 	set("status", "0");
73 	dinit(cp = getenv("HOME"));	/* dinit thinks that HOME == cwd in a
74 					 * login shell */
75 	if (cp == NOSTR)
76 		fast++;			/* No home -> can't read scripts */
77 	else
78 		set("home", savestr(cp));
79 	/*
80 	 * Grab other useful things from the environment.
81 	 * Should we grab everything??
82 	 */
83 	if ((cp = getenv("USER")) != NOSTR)
84 		set("user", savestr(cp));
85 	if ((cp = getenv("TERM")) != NOSTR)
86 		set("term", savestr(cp));
87 	/*
88 	 * Re-initialize path if set in environment
89 	 */
90 	if ((cp = getenv("PATH")) == NOSTR)
91 		set1("path", saveblk(pathlist), &shvhed);
92 	else
93 		importpath(cp);
94 	set("shell", SHELLPATH);
95 
96 	doldol = putn(getpid());		/* For $$ */
97 	shtemp = strspl("/tmp/sh", doldol);	/* For << */
98 
99 	/*
100 	 * Record the interrupt states from the parent process.
101 	 * If the parent is non-interruptible our hand must be forced
102 	 * or we (and our children) won't be either.
103 	 * Our children inherit termination from our parent.
104 	 * We catch it only if we are the login shell.
105 	 */
106 		/* parents interruptibility */
107 	(void) sigvec(SIGINT, (struct sigvec *)0, &osv);
108 	parintr = osv.sv_handler;
109 		/* parents terminability */
110 	(void) sigvec(SIGTERM, (struct sigvec *)0, &osv);
111 	parterm = osv.sv_handler;
112 	if (loginsh) {
113 		(void) signal(SIGHUP, phup);	/* exit processing on HUP */
114 		(void) signal(SIGXCPU, phup);	/* ...and on XCPU */
115 		(void) signal(SIGXFSZ, phup);	/* ...and on XFSZ */
116 	}
117 
118 	/*
119 	 * Process the arguments.
120 	 *
121 	 * Note that processing of -v/-x is actually delayed till after
122 	 * script processing.
123 	 */
124 	c--, v++;
125 	while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) {
126 		do switch (*cp++) {
127 
128 		case 'b':		/* -b	Next arg is input file */
129 			batch++;
130 			break;
131 
132 		case 'c':		/* -c	Command input from arg */
133 			if (c == 1)
134 				exit(0);
135 			c--, v++;
136 			arginp = v[0];
137 			prompt = 0;
138 			nofile++;
139 			break;
140 
141 		case 'e':		/* -e	Exit on any error */
142 			exiterr++;
143 			break;
144 
145 		case 'f':		/* -f	Fast start */
146 			fast++;
147 			break;
148 
149 		case 'i':		/* -i	Interactive, even if !intty */
150 			intact++;
151 			nofile++;
152 			break;
153 
154 		case 'n':		/* -n	Don't execute */
155 			noexec++;
156 			break;
157 
158 		case 'q':		/* -q	(Undoc'd) ... die on quit */
159 			quitit = 1;
160 			break;
161 
162 		case 's':		/* -s	Read from std input */
163 			nofile++;
164 			break;
165 
166 		case 't':		/* -t	Read one line from input */
167 			onelflg = 2;
168 			prompt = 0;
169 			nofile++;
170 			break;
171 
172 		case 'v':		/* -v	Echo hist expanded input */
173 			nverbose = 1;			/* ... later */
174 			break;
175 
176 		case 'x':		/* -x	Echo just before execution */
177 			nexececho = 1;			/* ... later */
178 			break;
179 
180 		case 'V':		/* -V	Echo hist expanded input */
181 			setNS("verbose");		/* NOW! */
182 			break;
183 
184 		case 'X':		/* -X	Echo just before execution */
185 			setNS("echo");			/* NOW! */
186 			break;
187 
188 		} while (*cp);
189 		v++, c--;
190 	}
191 
192 	if (quitit)			/* With all due haste, for debugging */
193 		(void) signal(SIGQUIT, SIG_DFL);
194 
195 	/*
196 	 * Unless prevented by -c, -i, -s, or -t, if there
197 	 * are remaining arguments the first of them is the name
198 	 * of a shell file from which to read commands.
199 	 */
200 	if (nofile == 0 && c > 0) {
201 		nofile = open(v[0], 0);
202 		if (nofile < 0) {
203 			child++;		/* So this ... */
204 			Perror(v[0]);		/* ... doesn't return */
205 		}
206 		file = v[0];
207 		SHIN = dmove(nofile, FSHIN);	/* Replace FSHIN */
208 		(void) ioctl(SHIN, FIOCLEX, (char *)0);
209 		prompt = 0;
210 		c--, v++;
211 	}
212 	if (!batch && (uid != geteuid() || getgid() != getegid())) {
213 		errno = EACCES;
214 		child++;			/* So this ... */
215 		Perror("csh");			/* ... doesn't return */
216 	}
217 	/*
218 	 * Consider input a tty if it really is or we are interactive.
219 	 */
220 	intty = intact || isatty(SHIN);
221 	/*
222 	 * Decide whether we should play with signals or not.
223 	 * If we are explicitly told (via -i, or -) or we are a login
224 	 * shell (arg0 starts with -) or the input and output are both
225 	 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
226 	 * Note that in only the login shell is it likely that parent
227 	 * may have set signals to be ignored
228 	 */
229 	if (loginsh || intact || intty && isatty(SHOUT))
230 		setintr = 1;
231 #ifdef TELL
232 	settell();
233 #endif
234 	/*
235 	 * Save the remaining arguments in argv.
236 	 */
237 	setq("argv", v, &shvhed);
238 
239 	/*
240 	 * Set up the prompt.
241 	 */
242 	if (prompt)
243 		set("prompt", uid == 0 ? "# " : "% ");
244 
245 	/*
246 	 * If we are an interactive shell, then start fiddling
247 	 * with the signals; this is a tricky game.
248 	 */
249 	shpgrp = getpgrp(0);
250 	opgrp = tpgrp = -1;
251 	oldisc = -1;
252 	if (setintr) {
253 		**av = '-';
254 		if (!quitit)		/* Wary! */
255 			(void) signal(SIGQUIT, SIG_IGN);
256 		(void) signal(SIGINT, pintr);
257 		(void) sigblock(sigmask(SIGINT));
258 		(void) signal(SIGTERM, SIG_IGN);
259 		if (quitit == 0 && arginp == 0) {
260 			(void) signal(SIGTSTP, SIG_IGN);
261 			(void) signal(SIGTTIN, SIG_IGN);
262 			(void) signal(SIGTTOU, SIG_IGN);
263 			/*
264 			 * Wait till in foreground, in case someone
265 			 * stupidly runs
266 			 *	csh &
267 			 * dont want to try to grab away the tty.
268 			 */
269 			if (isatty(FSHDIAG))
270 				f = FSHDIAG;
271 			else if (isatty(FSHOUT))
272 				f = FSHOUT;
273 			else if (isatty(OLDSTD))
274 				f = OLDSTD;
275 			else
276 				f = -1;
277 retry:
278 			if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 &&
279 			    tpgrp != -1) {
280 				int ldisc;
281 				if (tpgrp != shpgrp) {
282 					int (*old)() = signal(SIGTTIN, SIG_DFL);
283 					(void) kill(0, SIGTTIN);
284 					(void) signal(SIGTTIN, old);
285 					goto retry;
286 				}
287 				if (ioctl(f, TIOCGETD, (char *)&oldisc) != 0)
288 					goto notty;
289 				if (oldisc != NTTYDISC) {
290 #ifdef DEBUG
291 					printf("Switching to new tty driver...\n");
292 #endif DEBUG
293 					ldisc = NTTYDISC;
294 					(void) ioctl(f, TIOCSETD,
295 						(char *)&ldisc);
296 				} else
297 					oldisc = -1;
298 				opgrp = shpgrp;
299 				shpgrp = getpid();
300 				tpgrp = shpgrp;
301 				(void) ioctl(f, TIOCSPGRP, (char *)&shpgrp);
302 				(void) setpgrp(0, shpgrp);
303 				(void) ioctl(dcopy(f, FSHTTY), FIOCLEX,
304 					(char *)0);
305 			} else {
306 notty:
307   printf("Warning: no access to tty; thus no job control in this shell...\n");
308 				tpgrp = -1;
309 			}
310 		}
311 	}
312 	if (setintr == 0 && parintr == SIG_DFL)
313 		setintr++;
314 	(void) signal(SIGCHLD, pchild);	/* while signals not ready */
315 
316 	/*
317 	 * Set an exit here in case of an interrupt or error reading
318 	 * the shell start-up scripts.
319 	 */
320 	setexit();
321 	haderr = 0;		/* In case second time through */
322 	if (!fast && reenter == 0) {
323 		reenter++;
324 		/* Will have value("home") here because set fast if don't */
325 		srccat(value("home"), "/.cshrc");
326 		if (!fast && !arginp && !onelflg && !havhash)
327 			dohash();
328 		if (loginsh) {
329 			srccat(value("home"), "/.login");
330 		}
331 		dosource(loadhist);
332 	}
333 
334 	/*
335 	 * Now are ready for the -v and -x flags
336 	 */
337 	if (nverbose)
338 		setNS("verbose");
339 	if (nexececho)
340 		setNS("echo");
341 
342 	/*
343 	 * All the rest of the world is inside this call.
344 	 * The argument to process indicates whether it should
345 	 * catch "error unwinds".  Thus if we are a interactive shell
346 	 * our call here will never return by being blown past on an error.
347 	 */
348 	process(setintr);
349 
350 	/*
351 	 * Mop-up.
352 	 */
353 	if (loginsh) {
354 		printf("logout\n");
355 		(void) close(SHIN);
356 		child++;
357 		goodbye();
358 	}
359 	rechist();
360 	exitstat();
361 }
362 
363 untty()
364 {
365 
366 	if (tpgrp > 0) {
367 		(void) setpgrp(0, opgrp);
368 		(void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp);
369 		if (oldisc != -1 && oldisc != NTTYDISC) {
370 #ifdef DEBUG
371 			printf("\nReverting to old tty driver...\n");
372 #endif DEBUG
373 			(void) ioctl(FSHTTY, TIOCSETD, (char *)&oldisc);
374 		}
375 	}
376 }
377 
378 importpath(cp)
379 	char *cp;
380 {
381 	register int i = 0;
382 	register char *dp;
383 	register char **pv;
384 	int c;
385 	static char dot[2] = {'.', 0};
386 
387 	for (dp = cp; *dp; dp++)
388 		if (*dp == ':')
389 			i++;
390 	/*
391 	 * i+2 where i is the number of colons in the path.
392 	 * There are i+1 directories in the path plus we need
393 	 * room for a zero terminator.
394 	 */
395 	pv = (char **) calloc((unsigned) (i + 2), sizeof (char **));
396 	dp = cp;
397 	i = 0;
398 	if (*dp)
399 	for (;;) {
400 		if ((c = *dp) == ':' || c == 0) {
401 			*dp = 0;
402 			pv[i++] = savestr(*cp ? cp : dot);
403 			if (c) {
404 				cp = dp + 1;
405 				*dp = ':';
406 			} else
407 				break;
408 		}
409 		dp++;
410 	}
411 	pv[i] = 0;
412 	set1("path", pv, &shvhed);
413 }
414 
415 /*
416  * Source to the file which is the catenation of the argument names.
417  */
418 srccat(cp, dp)
419 	char *cp, *dp;
420 {
421 	register char *ep = strspl(cp, dp);
422 	register int unit = dmove(open(ep, 0), -1);
423 
424 	(void) ioctl(unit, FIOCLEX, (char *)0);
425 	xfree(ep);
426 #ifdef INGRES
427 	srcunit(unit, 0, 0);
428 #else
429 	srcunit(unit, 1, 0);
430 #endif
431 }
432 
433 /*
434  * Source to a unit.  If onlyown it must be our file or our group or
435  * we don't chance it.	This occurs on ".cshrc"s and the like.
436  */
437 int	insource;
438 static
439 srcunit(unit, onlyown, hflg)
440 	register int unit;
441 	bool onlyown;
442 	bool hflg;
443 {
444 	/* We have to push down a lot of state here */
445 	/* All this could go into a structure */
446 	int oSHIN = -1, oldintty = intty;
447 	struct whyle *oldwhyl = whyles;
448 	char *ogointr = gointr, *oarginp = arginp;
449 	char *oevalp = evalp, **oevalvec = evalvec;
450 	int oonelflg = onelflg;
451 	bool oenterhist = enterhist;
452 	char OHIST = HIST;
453 #ifdef TELL
454 	bool otell = cantell;
455 #endif
456 	struct Bin saveB;
457 
458 	/* The (few) real local variables */
459 	jmp_buf oldexit;
460 	int reenter;
461 	long omask;
462 
463 	if (unit < 0)
464 		return;
465 	if (didfds)
466 		donefds();
467 	if (onlyown) {
468 		struct stat stb;
469 
470 		if (fstat(unit, &stb) < 0 ||
471 		    (stb.st_uid != uid && stb.st_gid != getgid())) {
472 			(void) close(unit);
473 			return;
474 		}
475 	}
476 
477 	/*
478 	 * There is a critical section here while we are pushing down the
479 	 * input stream since we have stuff in different structures.
480 	 * If we weren't careful an interrupt could corrupt SHIN's Bin
481 	 * structure and kill the shell.
482 	 *
483 	 * We could avoid the critical region by grouping all the stuff
484 	 * in a single structure and pointing at it to move it all at
485 	 * once.  This is less efficient globally on many variable references
486 	 * however.
487 	 */
488 	insource = 1;
489 	getexit(oldexit);
490 	reenter = 0;
491 	if (setintr)
492 		omask = sigblock(sigmask(SIGINT));
493 	setexit();
494 	reenter++;
495 	if (reenter == 1) {
496 		/* Setup the new values of the state stuff saved above */
497 		copy((char *)&saveB, (char *)&B, sizeof saveB);
498 		fbuf = (char **) 0;
499 		fseekp = feobp = fblocks = 0;
500 		oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
501 		intty = isatty(SHIN), whyles = 0, gointr = 0;
502 		evalvec = 0; evalp = 0;
503 		enterhist = hflg;
504 		if (enterhist)
505 			HIST = '\0';
506 		/*
507 		 * Now if we are allowing commands to be interrupted,
508 		 * we let ourselves be interrupted.
509 		 */
510 		if (setintr)
511 			(void) sigsetmask(omask);
512 #ifdef TELL
513 		settell();
514 #endif
515 		process(0);		/* 0 -> blow away on errors */
516 	}
517 	if (setintr)
518 		(void) sigsetmask(omask);
519 	if (oSHIN >= 0) {
520 		register int i;
521 
522 		/* We made it to the new state... free up its storage */
523 		/* This code could get run twice but xfree doesn't care */
524 		for (i = 0; i < fblocks; i++)
525 			xfree(fbuf[i]);
526 		xfree((char *)fbuf);
527 
528 		/* Reset input arena */
529 		copy((char *)&B, (char *)&saveB, sizeof B);
530 
531 		(void) close(SHIN), SHIN = oSHIN;
532 		arginp = oarginp, onelflg = oonelflg;
533 		evalp = oevalp, evalvec = oevalvec;
534 		intty = oldintty, whyles = oldwhyl, gointr = ogointr;
535 		if (enterhist)
536 			HIST = OHIST;
537 		enterhist = oenterhist;
538 #ifdef TELL
539 		cantell = otell;
540 #endif
541 	}
542 
543 	resexit(oldexit);
544 	/*
545 	 * If process reset() (effectively an unwind) then
546 	 * we must also unwind.
547 	 */
548 	if (reenter >= 2)
549 		error(NOSTR);
550 	insource = 0;
551 }
552 
553 rechist()
554 {
555 	char buf[BUFSIZ];
556 	int fp, ftmp, oldidfds;
557 
558 	if (!fast) {
559 		if (value("savehist")[0] == '\0')
560 			return;
561 		(void) strcpy(buf, value("home"));
562 		(void) strcat(buf, "/.history");
563 		fp = creat(buf, 0666);
564 		if (fp == -1)
565 			return;
566 		oldidfds = didfds;
567 		didfds = 0;
568 		ftmp = SHOUT;
569 		SHOUT = fp;
570 		(void) strcpy(buf, value("savehist"));
571 		dumphist[2] = buf;
572 		dohist(dumphist);
573 		(void) close(fp);
574 		SHOUT = ftmp;
575 		didfds = oldidfds;
576 	}
577 }
578 
579 goodbye()
580 {
581 	if (loginsh) {
582 		(void) signal(SIGQUIT, SIG_IGN);
583 		(void) signal(SIGINT, SIG_IGN);
584 		(void) signal(SIGTERM, SIG_IGN);
585 		setintr = 0;		/* No interrupts after "logout" */
586 		if (adrof("home"))
587 			srccat(value("home"), "/.logout");
588 	}
589 	rechist();
590 	exitstat();
591 }
592 
593 exitstat()
594 {
595 
596 #ifdef PROF
597 	monitor(0);
598 #endif
599 	/*
600 	 * Note that if STATUS is corrupted (i.e. getn bombs)
601 	 * then error will exit directly because we poke child here.
602 	 * Otherwise we might continue unwarrantedly (sic).
603 	 */
604 	child++;
605 	exit(getn(value("status")));
606 }
607 
608 /*
609  * in the event of a HUP we want to save the history
610  */
611 phup()
612 {
613 	rechist();
614 	exit(1);
615 }
616 
617 char	*jobargv[2] = { "jobs", 0 };
618 /*
619  * Catch an interrupt, e.g. during lexical input.
620  * If we are an interactive shell, we reset the interrupt catch
621  * immediately.  In any case we drain the shell output,
622  * and finally go through the normal error mechanism, which
623  * gets a chance to make the shell go away.
624  */
625 pintr()
626 {
627 	pintr1(1);
628 }
629 
630 pintr1(wantnl)
631 	bool wantnl;
632 {
633 	register char **v;
634 	long omask;
635 
636 	omask = sigblock(0L);
637 	if (setintr) {
638 		(void) sigsetmask(omask & ~sigmask(SIGINT));
639 		if (pjobs) {
640 			pjobs = 0;
641 			printf("\n");
642 			dojobs(jobargv);
643 			bferr("Interrupted");
644 		}
645 	}
646 	(void) sigsetmask(omask & ~sigmask(SIGCHLD));
647 	draino();
648 
649 	/*
650 	 * If we have an active "onintr" then we search for the label.
651 	 * Note that if one does "onintr -" then we shan't be interruptible
652 	 * so we needn't worry about that here.
653 	 */
654 	if (gointr) {
655 		search(ZGOTO, 0, gointr);
656 		timflg = 0;
657 		if (v = pargv)
658 			pargv = 0, blkfree(v);
659 		if (v = gargv)
660 			gargv = 0, blkfree(v);
661 		reset();
662 	} else if (intty && wantnl)
663 		printf("\n");		/* Some like this, others don't */
664 	error(NOSTR);
665 }
666 
667 /*
668  * Process is the main driving routine for the shell.
669  * It runs all command processing, except for those within { ... }
670  * in expressions (which is run by a routine evalav in sh.exp.c which
671  * is a stripped down process), and `...` evaluation which is run
672  * also by a subset of this code in sh.glob.c in the routine backeval.
673  *
674  * The code here is a little strange because part of it is interruptible
675  * and hence freeing of structures appears to occur when none is necessary
676  * if this is ignored.
677  *
678  * Note that if catch is not set then we will unwind on any error.
679  * If an end-of-file occurs, we return.
680  */
681 process(catch)
682 	bool catch;
683 {
684 	jmp_buf osetexit;
685 	register struct command *t;
686 
687 	getexit(osetexit);
688 	for (;;) {
689 		pendjob();
690 		paraml.next = paraml.prev = &paraml;
691 		paraml.word = "";
692 		t = 0;
693 		setexit();
694 		justpr = enterhist;	/* execute if not entering history */
695 
696 		/*
697 		 * Interruptible during interactive reads
698 		 */
699 		if (setintr)
700 			(void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT));
701 
702 		/*
703 		 * For the sake of reset()
704 		 */
705 		freelex(&paraml), freesyn(t), t = 0;
706 
707 		if (haderr) {
708 			if (!catch) {
709 				/* unwind */
710 				doneinp = 0;
711 				resexit(osetexit);
712 				reset();
713 			}
714 			haderr = 0;
715 			/*
716 			 * Every error is eventually caught here or
717 			 * the shell dies.  It is at this
718 			 * point that we clean up any left-over open
719 			 * files, by closing all but a fixed number
720 			 * of pre-defined files.  Thus routines don't
721 			 * have to worry about leaving files open due
722 			 * to deeper errors... they will get closed here.
723 			 */
724 			closem();
725 			continue;
726 		}
727 		if (doneinp) {
728 			doneinp = 0;
729 			break;
730 		}
731 		if (chkstop)
732 			chkstop--;
733 		if (neednote)
734 			pnote();
735 		if (intty && prompt && evalvec == 0) {
736 			mailchk();
737 			/*
738 			 * If we are at the end of the input buffer
739 			 * then we are going to read fresh stuff.
740 			 * Otherwise, we are rereading input and don't
741 			 * need or want to prompt.
742 			 */
743 			if (fseekp == feobp)
744 				printprompt();
745 		}
746 		err = 0;
747 
748 		/*
749 		 * Echo not only on VERBOSE, but also with history expansion.
750 		 * If there is a lexical error then we forego history echo.
751 		 */
752 		if (lex(&paraml) && !err && intty ||
753 		    adrof("verbose")) {
754 			haderr = 1;
755 			prlex(&paraml);
756 			haderr = 0;
757 		}
758 
759 		/*
760 		 * The parser may lose space if interrupted.
761 		 */
762 		if (setintr)
763 			(void) sigblock(sigmask(SIGINT));
764 
765 		/*
766 		 * Save input text on the history list if
767 		 * reading in old history, or it
768 		 * is from the terminal at the top level and not
769 		 * in a loop.
770 		 */
771 		if (enterhist || catch && intty && !whyles)
772 			savehist(&paraml);
773 
774 		/*
775 		 * Print lexical error messages, except when sourcing
776 		 * history lists.
777 		 */
778 		if (!enterhist && err)
779 			error(err);
780 
781 		/*
782 		 * If had a history command :p modifier then
783 		 * this is as far as we should go
784 		 */
785 		if (justpr)
786 			reset();
787 
788 		alias(&paraml);
789 
790 		/*
791 		 * Parse the words of the input into a parse tree.
792 		 */
793 		t = syntax(paraml.next, &paraml, 0);
794 		if (err)
795 			error(err);
796 
797 		/*
798 		 * Execute the parse tree
799 		 */
800 		execute(t, tpgrp);
801 
802 		/*
803 		 * Made it!
804 		 */
805 		freelex(&paraml), freesyn(t);
806 	}
807 	resexit(osetexit);
808 }
809 
810 dosource(t)
811 	register char **t;
812 {
813 	register char *f;
814 	register int u;
815 	bool hflg = 0;
816 	char buf[BUFSIZ];
817 
818 	t++;
819 	if (*t && eq(*t, "-h")) {
820 		t++;
821 		hflg++;
822 	}
823 	(void) strcpy(buf, *t);
824 	f = globone(buf);
825 	u = dmove(open(f, 0), -1);
826 	xfree(f);
827 	if (u < 0 && !hflg)
828 		Perror(f);
829 	(void) ioctl(u, FIOCLEX, (char *)0);
830 	srcunit(u, 0, hflg);
831 }
832 
833 /*
834  * Check for mail.
835  * If we are a login shell, then we don't want to tell
836  * about any mail file unless its been modified
837  * after the time we started.
838  * This prevents us from telling the user things he already
839  * knows, since the login program insists on saying
840  * "You have mail."
841  */
842 mailchk()
843 {
844 	register struct varent *v;
845 	register char **vp;
846 	time_t t;
847 	int intvl, cnt;
848 	struct stat stb;
849 	bool new;
850 
851 	v = adrof("mail");
852 	if (v == 0)
853 		return;
854 	(void) time(&t);
855 	vp = v->vec;
856 	cnt = blklen(vp);
857 	intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
858 	if (intvl < 1)
859 		intvl = 1;
860 	if (chktim + intvl > t)
861 		return;
862 	for (; *vp; vp++) {
863 		if (stat(*vp, &stb) < 0)
864 			continue;
865 		new = stb.st_mtime > time0.tv_sec;
866 		if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
867 		    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
868 		    loginsh && !new)
869 			continue;
870 		if (cnt == 1)
871 			printf("You have %smail.\n", new ? "new " : "");
872 		else
873 			printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
874 	}
875 	chktim = t;
876 }
877 
878 #include <pwd.h>
879 /*
880  * Extract a home directory from the password file
881  * The argument points to a buffer where the name of the
882  * user whose home directory is sought is currently.
883  * We write the home directory of the user back there.
884  */
885 gethdir(home)
886 	char *home;
887 {
888 	register struct passwd *pp = getpwnam(home);
889 
890 	if (pp == 0)
891 		return (1);
892 	(void) strcpy(home, pp->pw_dir);
893 	return (0);
894 }
895 
896 /*
897  * Move the initial descriptors to their eventual
898  * resting places, closin all other units.
899  */
900 initdesc()
901 {
902 
903 	didfds = 0;			/* 0, 1, 2 aren't set up */
904 	(void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0);
905 	(void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0);
906 	(void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0);
907 	(void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0);
908 	closem();
909 }
910 
911 #ifdef PROF
912 done(i)
913 #else
914 exit(i)
915 #endif
916 	int i;
917 {
918 
919 	untty();
920 	_exit(i);
921 }
922 
923 printprompt()
924 {
925 	register char *cp;
926 
927 	if (!whyles) {
928 		for (cp = value("prompt"); *cp; cp++)
929 			if (*cp == HIST)
930 				printf("%d", eventno + 1);
931 			else {
932 				if (*cp == '\\' && cp[1] == HIST)
933 					cp++;
934 				cshputchar(*cp | QUOTE);
935 			}
936 	} else
937 		/*
938 		 * Prompt for forward reading loop
939 		 * body content.
940 		 */
941 		printf("? ");
942 	flush();
943 }
944