xref: /original-bsd/bin/csh/csh.c (revision a4c1bdd8)
1 /*-
2  * Copyright (c) 1980, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)csh.c	5.31 (Berkeley) 10/28/91";
16 #endif /* not lint */
17 
18 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <pwd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <locale.h>
27 #include <unistd.h>
28 #if __STDC__
29 # include <stdarg.h>
30 #else
31 # include <varargs.h>
32 #endif
33 
34 #include "csh.h"
35 #include "extern.h"
36 #include "pathnames.h"
37 
38 extern bool MapsAreInited;
39 extern bool NLSMapsAreInited;
40 
41 /*
42  * C Shell
43  *
44  * Bill Joy, UC Berkeley, California, USA
45  * October 1978, May 1980
46  *
47  * Jim Kulp, IIASA, Laxenburg, Austria
48  * April 1980
49  *
50  * Christos Zoulas, Cornell University
51  * June, 1991
52  */
53 
54 Char   *dumphist[] = {STRhistory, STRmh, 0, 0};
55 Char   *loadhist[] = {STRsource, STRmh, STRhistfile, 0};
56 
57 int     nofile = 0;
58 bool    reenter = 0;
59 bool    nverbose = 0;
60 bool    nexececho = 0;
61 bool    quitit = 0;
62 bool    fast = 0;
63 bool    batch = 0;
64 bool    mflag = 0;
65 bool    prompt = 1;
66 bool    enterhist = 0;
67 bool    tellwhat = 0;
68 
69 extern char **environ;
70 
71 static int	readf __P((void *, char *, int));
72 static fpos_t	seekf __P((void *, fpos_t, int));
73 static int	writef __P((void *, const char *, int));
74 static int	closef __P((void *));
75 static int	srccat __P((Char *, Char *));
76 static int	srcfile __P((char *, bool, bool));
77 static void	phup __P((int));
78 static void	srcunit __P((int, bool, bool));
79 static void	mailchk __P((void));
80 static Char   **defaultpath __P((void));
81 
82 int
83 main(argc, argv)
84     int     argc;
85     char  **argv;
86 {
87     register Char *cp;
88     register char *tcp;
89     register int f;
90     register char **tempv;
91     struct sigvec osv;
92 
93     cshin = stdin;
94     cshout = stdout;
95     csherr = stderr;
96 
97     settimes();			/* Immed. estab. timing base */
98 
99     /*
100      * Initialize non constant strings
101      */
102 #ifdef _PATH_BSHELL
103     STR_BSHELL = SAVE(_PATH_BSHELL);
104 #endif
105 #ifdef _PATH_CSHELL
106     STR_SHELLPATH = SAVE(_PATH_CSHELL);
107 #endif
108     STR_environ = blk2short(environ);
109     environ = short2blk(STR_environ);	/* So that we can free it */
110     STR_WORD_CHARS = SAVE(WORD_CHARS);
111 
112     HIST = '!';
113     HISTSUB = '^';
114     word_chars = STR_WORD_CHARS;
115 
116     tempv = argv;
117     if (eq(str2short(tempv[0]), STRaout))	/* A.out's are quittable */
118 	quitit = 1;
119     uid = getuid();
120     gid = getgid();
121     /*
122      * We are a login shell if: 1. we were invoked as -<something> and we had
123      * no arguments 2. or we were invoked only with the -l flag
124      */
125     loginsh = (**tempv == '-' && argc == 1) ||
126 	(argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' &&
127 	 tempv[1][2] == '\0');
128 
129     if (loginsh && **tempv != '-') {
130 	/*
131 	 * Mangle the argv space
132 	 */
133 	tempv[1][0] = '\0';
134 	tempv[1][1] = '\0';
135 	tempv[1] = NULL;
136 	for (tcp = *tempv; *tcp++;)
137 	    continue;
138 	for (tcp--; tcp >= *tempv; tcp--)
139 	    tcp[1] = tcp[0];
140 	*++tcp = '-';
141 	argc--;
142     }
143     if (loginsh)
144 	(void) time(&chktim);
145 
146     AsciiOnly = 1;
147 #ifdef NLS
148     (void) setlocale(LC_ALL, "");
149     {
150 	int     k;
151 
152 	for (k = 0200; k <= 0377 && !Isprint(k); k++)
153 	    continue;
154 	AsciiOnly = k > 0377;
155     }
156 #else
157     AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
158 #endif				/* NLS */
159 
160     /*
161      * Move the descriptors to safe places. The variable didfds is 0 while we
162      * have only FSH* to work with. When didfds is true, we have 0,1,2 and
163      * prefer to use these.
164      */
165     initdesc();
166     (void) fclose(cshin);
167     (void) fclose(cshout);
168     (void) fclose(csherr);
169     if (!(cshin  = funopen((void *) &SHIN,  readf, writef, seekf, closef)))
170 	exit(1);
171     if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef)))
172 	exit(1);
173     if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef)))
174 	exit(1);
175     (void) setvbuf(cshin,  NULL, _IOLBF, 0);
176     (void) setvbuf(cshout, NULL, _IOLBF, 0);
177     (void) setvbuf(csherr, NULL, _IOLBF, 0);
178 
179 
180     /*
181      * Initialize the shell variables. ARGV and PROMPT are initialized later.
182      * STATUS is also munged in several places. CHILD is munged when
183      * forking/waiting
184      */
185     set(STRstatus, Strsave(STR0));
186 
187     if ((tcp = getenv("HOME")) != NULL)
188 	cp = SAVE(tcp);
189     else
190 	cp = NULL;
191 
192     if (cp == NULL)
193 	fast = 1;		/* No home -> can't read scripts */
194     else
195 	set(STRhome, cp);
196     dinit(cp);			/* dinit thinks that HOME == cwd in a login
197 				 * shell */
198     /*
199      * Grab other useful things from the environment. Should we grab
200      * everything??
201      */
202     if ((tcp = getenv("LOGNAME")) != NULL ||
203 	(tcp = getenv("USER")) != NULL)
204 	set(STRuser, SAVE(tcp));
205     if ((tcp = getenv("TERM")) != NULL)
206 	set(STRterm, SAVE(tcp));
207 
208     /*
209      * Re-initialize path if set in environment
210      */
211     if ((tcp = getenv("PATH")) == NULL)
212 	set1(STRpath, defaultpath(), &shvhed);
213     else
214 	importpath(SAVE(tcp));
215 
216     set(STRshell, Strsave(STR_SHELLPATH));
217 
218     doldol = putn((int) getpid());	/* For $$ */
219     shtemp = Strspl(STRtmpsh, doldol);	/* For << */
220 
221     /*
222      * Record the interrupt states from the parent process. If the parent is
223      * non-interruptible our hand must be forced or we (and our children) won't
224      * be either. Our children inherit termination from our parent. We catch it
225      * only if we are the login shell.
226      */
227     /* parents interruptibility */
228     (void) sigvec(SIGINT, NULL, &osv);
229     parintr = (void (*) ()) osv.sv_handler;
230     (void) sigvec(SIGTERM, NULL, &osv);
231     parterm = (void (*) ()) osv.sv_handler;
232 
233     if (loginsh) {
234 	(void) signal(SIGHUP, phup);	/* exit processing on HUP */
235 	(void) signal(SIGXCPU, phup);	/* ...and on XCPU */
236 	(void) signal(SIGXFSZ, phup);	/* ...and on XFSZ */
237     }
238 
239     /*
240      * Process the arguments.
241      *
242      * Note that processing of -v/-x is actually delayed till after script
243      * processing.
244      *
245      * We set the first character of our name to be '-' if we are a shell
246      * running interruptible commands.  Many programs which examine ps'es
247      * use this to filter such shells out.
248      */
249     argc--, tempv++;
250     while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) {
251 	do
252 	    switch (*tcp++) {
253 
254 	    case 0:		/* -	Interruptible, no prompt */
255 		prompt = 0;
256 		setintr = 1;
257 		nofile = 1;
258 		break;
259 
260 	    case 'b':		/* -b	Next arg is input file */
261 		batch = 1;
262 		break;
263 
264 	    case 'c':		/* -c	Command input from arg */
265 		if (argc == 1)
266 		    xexit(0);
267 		argc--, tempv++;
268 		arginp = SAVE(tempv[0]);
269 		prompt = 0;
270 		nofile = 1;
271 		break;
272 
273 	    case 'e':		/* -e	Exit on any error */
274 		exiterr = 1;
275 		break;
276 
277 	    case 'f':		/* -f	Fast start */
278 		fast = 1;
279 		break;
280 
281 	    case 'i':		/* -i	Interactive, even if !intty */
282 		intact = 1;
283 		nofile = 1;
284 		break;
285 
286 	    case 'm':		/* -m	read .cshrc (from su) */
287 		mflag = 1;
288 		break;
289 
290 	    case 'n':		/* -n	Don't execute */
291 		noexec = 1;
292 		break;
293 
294 	    case 'q':		/* -q	(Undoc'd) ... die on quit */
295 		quitit = 1;
296 		break;
297 
298 	    case 's':		/* -s	Read from std input */
299 		nofile = 1;
300 		break;
301 
302 	    case 't':		/* -t	Read one line from input */
303 		onelflg = 2;
304 		prompt = 0;
305 		nofile = 1;
306 		break;
307 
308 	    case 'v':		/* -v	Echo hist expanded input */
309 		nverbose = 1;	/* ... later */
310 		break;
311 
312 	    case 'x':		/* -x	Echo just before execution */
313 		nexececho = 1;	/* ... later */
314 		break;
315 
316 	    case 'V':		/* -V	Echo hist expanded input */
317 		setNS(STRverbose);	/* NOW! */
318 		break;
319 
320 	    case 'X':		/* -X	Echo just before execution */
321 		setNS(STRecho);	/* NOW! */
322 		break;
323 
324 	} while (*tcp);
325 	tempv++, argc--;
326     }
327 
328     if (quitit)			/* With all due haste, for debugging */
329 	(void) signal(SIGQUIT, SIG_DFL);
330 
331     /*
332      * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
333      * arguments the first of them is the name of a shell file from which to
334      * read commands.
335      */
336     if (nofile == 0 && argc > 0) {
337 	nofile = open(tempv[0], O_RDONLY);
338 	if (nofile < 0) {
339 	    child = 1;		/* So this doesn't return */
340 	    stderror(ERR_SYSTEM, tempv[0], strerror(errno));
341 	}
342 	ffile = SAVE(tempv[0]);
343 	/*
344 	 * Replace FSHIN. Handle /dev/std{in,out,err} specially
345 	 * since once they are closed we cannot open them again.
346 	 * In that case we use our own saved descriptors
347 	 */
348 	if ((SHIN = dmove(nofile, FSHIN)) < 0)
349 	    switch(nofile) {
350 	    case 0:
351 		SHIN = FSHIN;
352 		break;
353 	    case 1:
354 		SHIN = FSHOUT;
355 		break;
356 	    case 2:
357 		SHIN = FSHERR;
358 		break;
359 	    default:
360 		stderror(ERR_SYSTEM, tempv[0], strerror(errno));
361 		break;
362 	    }
363 	(void) ioctl(SHIN, FIOCLEX, NULL);
364 	prompt = 0;
365 	 /* argc not used any more */ tempv++;
366     }
367 
368     intty = isatty(SHIN);
369     intty |= intact;
370     if (intty || (intact && isatty(SHOUT))) {
371 	if (!batch && (uid != geteuid() || gid != getegid())) {
372 	    errno = EACCES;
373 	    child = 1;		/* So this doesn't return */
374 	    stderror(ERR_SYSTEM, "csh", strerror(errno));
375 	}
376     }
377     /*
378      * Decide whether we should play with signals or not. If we are explicitly
379      * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
380      * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
381      * Note that in only the login shell is it likely that parent may have set
382      * signals to be ignored
383      */
384     if (loginsh || intact || intty && isatty(SHOUT))
385 	setintr = 1;
386     settell();
387     /*
388      * Save the remaining arguments in argv.
389      */
390     setq(STRargv, blk2short(tempv), &shvhed);
391 
392     /*
393      * Set up the prompt.
394      */
395     if (prompt) {
396 	set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent));
397 	/* that's a meta-questionmark */
398 	set(STRprompt2, Strsave(STRmquestion));
399     }
400 
401     /*
402      * If we are an interactive shell, then start fiddling with the signals;
403      * this is a tricky game.
404      */
405     shpgrp = getpgrp();
406     opgrp = tpgrp = -1;
407     if (setintr) {
408 	**argv = '-';
409 	if (!quitit)		/* Wary! */
410 	    (void) signal(SIGQUIT, SIG_IGN);
411 	(void) signal(SIGINT, pintr);
412 	(void) sigblock(sigmask(SIGINT));
413 	(void) signal(SIGTERM, SIG_IGN);
414 	if (quitit == 0 && arginp == 0) {
415 	    (void) signal(SIGTSTP, SIG_IGN);
416 	    (void) signal(SIGTTIN, SIG_IGN);
417 	    (void) signal(SIGTTOU, SIG_IGN);
418 	    /*
419 	     * Wait till in foreground, in case someone stupidly runs csh &
420 	     * dont want to try to grab away the tty.
421 	     */
422 	    if (isatty(FSHERR))
423 		f = FSHERR;
424 	    else if (isatty(FSHOUT))
425 		f = FSHOUT;
426 	    else if (isatty(OLDSTD))
427 		f = OLDSTD;
428 	    else
429 		f = -1;
430     retry:
431 	    if ((tpgrp = tcgetpgrp(f)) != -1) {
432 		if (tpgrp != shpgrp) {
433 		    sig_t old = signal(SIGTTIN, SIG_DFL);
434 		    (void) kill(0, SIGTTIN);
435 		    (void) signal(SIGTTIN, old);
436 		    goto retry;
437 		}
438 		opgrp = shpgrp;
439 		shpgrp = getpid();
440 		tpgrp = shpgrp;
441 		/*
442 		 * Setpgid will fail if we are a session leader and
443 		 * mypid == mypgrp (POSIX 4.3.3)
444 		 */
445 		if (opgrp != shpgrp)
446 		    if (setpgid(0, shpgrp) == -1)
447 			goto notty;
448 		/*
449 		 * We do that after we set our process group, to make sure
450 		 * that the process group belongs to a process in the same
451 		 * session as the tty (our process and our group) (POSIX 7.2.4)
452 		 */
453 		if (tcsetpgrp(f, shpgrp) == -1)
454 		    goto notty;
455 		(void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL);
456 	    }
457 	    if (tpgrp == -1) {
458 notty:
459 		(void) fprintf(csherr, "Warning: no access to tty (%s).\n",
460 			       strerror(errno));
461 		(void) fprintf(csherr, "Thus no job control in this shell.\n");
462 	    }
463 	}
464     }
465     if ((setintr == 0) && (parintr == SIG_DFL))
466 	setintr = 1;
467     (void) signal(SIGCHLD, pchild);	/* while signals not ready */
468 
469     /*
470      * Set an exit here in case of an interrupt or error reading the shell
471      * start-up scripts.
472      */
473     reenter = setexit();	/* PWP */
474     haderr = 0;			/* In case second time through */
475     if (!fast && reenter == 0) {
476 	/* Will have value(STRhome) here because set fast if don't */
477 	{
478 	    int     osetintr = setintr;
479 	    sigset_t omask = sigblock(sigmask(SIGINT));
480 
481 	    setintr = 0;
482 #ifdef _PATH_DOTCSHRC
483 	    (void) srcfile(_PATH_DOTCSHRC, 0, 0);
484 #endif
485 	    if (!fast && !arginp && !onelflg)
486 		dohash(NULL, NULL);
487 #ifdef _PATH_DOTLOGIN
488 	    if (loginsh)
489 		(void) srcfile(_PATH_DOTLOGIN, 0, 0);
490 #endif
491 	    (void) sigsetmask(omask);
492 	    setintr = osetintr;
493 	}
494 	(void) srccat(value(STRhome), STRsldotcshrc);
495 
496 	if (!fast && !arginp && !onelflg && !havhash)
497 	    dohash(NULL, NULL);
498         if (loginsh)
499 	      (void) srccat(value(STRhome), STRsldotlogin);
500 	dosource(loadhist, NULL);
501     }
502 
503     /*
504      * Now are ready for the -v and -x flags
505      */
506     if (nverbose)
507 	setNS(STRverbose);
508     if (nexececho)
509 	setNS(STRecho);
510 
511     /*
512      * All the rest of the world is inside this call. The argument to process
513      * indicates whether it should catch "error unwinds".  Thus if we are a
514      * interactive shell our call here will never return by being blown past on
515      * an error.
516      */
517     process(setintr);
518 
519     /*
520      * Mop-up.
521      */
522     if (intty) {
523 	if (loginsh) {
524 	    (void) fprintf(cshout, "logout\n");
525 	    (void) close(SHIN);
526 	    child = 1;
527 	    goodbye();
528 	}
529 	else {
530 	    (void) fprintf(cshout, "exit\n");
531 	}
532     }
533     rechist();
534     exitstat();
535     return (0);
536 }
537 
538 void
539 untty()
540 {
541     if (tpgrp > 0) {
542 	(void) setpgid(0, opgrp);
543 	(void) tcsetpgrp(FSHTTY, opgrp);
544     }
545 }
546 
547 void
548 importpath(cp)
549     Char   *cp;
550 {
551     register int i = 0;
552     register Char *dp;
553     register Char **pv;
554     int     c;
555 
556     for (dp = cp; *dp; dp++)
557 	if (*dp == ':')
558 	    i++;
559     /*
560      * i+2 where i is the number of colons in the path. There are i+1
561      * directories in the path plus we need room for a zero terminator.
562      */
563     pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **));
564     dp = cp;
565     i = 0;
566     if (*dp)
567 	for (;;) {
568 	    if ((c = *dp) == ':' || c == 0) {
569 		*dp = 0;
570 		pv[i++] = Strsave(*cp ? cp : STRdot);
571 		if (c) {
572 		    cp = dp + 1;
573 		    *dp = ':';
574 		}
575 		else
576 		    break;
577 	    }
578 	    dp++;
579 	}
580     pv[i] = 0;
581     set1(STRpath, pv, &shvhed);
582 }
583 
584 /*
585  * Source to the file which is the catenation of the argument names.
586  */
587 static int
588 srccat(cp, dp)
589     Char   *cp, *dp;
590 {
591     register Char *ep = Strspl(cp, dp);
592     char   *ptr = short2str(ep);
593 
594     xfree((ptr_t) ep);
595     return srcfile(ptr, mflag ? 0 : 1, 0);
596 }
597 
598 /*
599  * Source to a file putting the file descriptor in a safe place (> 2).
600  */
601 static int
602 srcfile(f, onlyown, flag)
603     char   *f;
604     bool    onlyown, flag;
605 {
606     register int unit;
607 
608     if ((unit = open(f, O_RDONLY)) == -1)
609 	return 0;
610     unit = dmove(unit, -1);
611 
612     (void) ioctl(unit, FIOCLEX, NULL);
613     srcunit(unit, onlyown, flag);
614     return 1;
615 }
616 
617 /*
618  * Source to a unit.  If onlyown it must be our file or our group or
619  * we don't chance it.	This occurs on ".cshrc"s and the like.
620  */
621 int     insource;
622 static void
623 srcunit(unit, onlyown, hflg)
624     register int unit;
625     bool    onlyown, hflg;
626 {
627     /* We have to push down a lot of state here */
628     /* All this could go into a structure */
629     int     oSHIN = -1, oldintty = intty, oinsource = insource;
630     struct whyle *oldwhyl = whyles;
631     Char   *ogointr = gointr, *oarginp = arginp;
632     Char   *oevalp = evalp, **oevalvec = evalvec;
633     int     oonelflg = onelflg;
634     bool    oenterhist = enterhist;
635     char    OHIST = HIST;
636     bool    otell = cantell;
637 
638     struct Bin saveB;
639     volatile sigset_t omask;
640     jmp_buf oldexit;
641 
642     /* The (few) real local variables */
643     int     my_reenter;
644 
645     if (unit < 0)
646 	return;
647     if (didfds)
648 	donefds();
649     if (onlyown) {
650 	struct stat stb;
651 
652 	if (fstat(unit, &stb) < 0) {
653 	    (void) close(unit);
654 	    return;
655 	}
656     }
657 
658     /*
659      * There is a critical section here while we are pushing down the input
660      * stream since we have stuff in different structures. If we weren't
661      * careful an interrupt could corrupt SHIN's Bin structure and kill the
662      * shell.
663      *
664      * We could avoid the critical region by grouping all the stuff in a single
665      * structure and pointing at it to move it all at once.  This is less
666      * efficient globally on many variable references however.
667      */
668     insource = 1;
669     getexit(oldexit);
670     omask = 0;
671 
672     if (setintr)
673 	omask = sigblock(sigmask(SIGINT));
674     /* Setup the new values of the state stuff saved above */
675     bcopy((char *) &B, (char *) &(saveB), sizeof(B));
676     fbuf = NULL;
677     fseekp = feobp = fblocks = 0;
678     oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
679     intty = isatty(SHIN), whyles = 0, gointr = 0;
680     evalvec = 0;
681     evalp = 0;
682     enterhist = hflg;
683     if (enterhist)
684 	HIST = '\0';
685 
686     /*
687      * Now if we are allowing commands to be interrupted, we let ourselves be
688      * interrupted.
689      */
690     if (setintr)
691 	(void) sigsetmask(omask);
692     settell();
693 
694     if ((my_reenter = setexit()) == 0)
695 	process(0);		/* 0 -> blow away on errors */
696 
697     if (setintr)
698 	(void) sigsetmask(omask);
699     if (oSHIN >= 0) {
700 	register int i;
701 
702 	/* We made it to the new state... free up its storage */
703 	/* This code could get run twice but xfree doesn't care */
704 	for (i = 0; i < fblocks; i++)
705 	    xfree((ptr_t) fbuf[i]);
706 	xfree((ptr_t) fbuf);
707 
708 	/* Reset input arena */
709 	bcopy((char *) &(saveB), (char *) &B, sizeof(B));
710 
711 	(void) close(SHIN), SHIN = oSHIN;
712 	arginp = oarginp, onelflg = oonelflg;
713 	evalp = oevalp, evalvec = oevalvec;
714 	intty = oldintty, whyles = oldwhyl, gointr = ogointr;
715 	if (enterhist)
716 	    HIST = OHIST;
717 	enterhist = oenterhist;
718 	cantell = otell;
719     }
720 
721     resexit(oldexit);
722     /*
723      * If process reset() (effectively an unwind) then we must also unwind.
724      */
725     if (my_reenter)
726 	stderror(ERR_SILENT);
727     insource = oinsource;
728 }
729 
730 void
731 rechist()
732 {
733     Char    buf[BUFSIZ];
734     int     fp, ftmp, oldidfds;
735 
736     if (!fast) {
737 	if (value(STRsavehist)[0] == '\0')
738 	    return;
739 	(void) Strcpy(buf, value(STRhome));
740 	(void) Strcat(buf, STRsldthist);
741 	fp = creat(short2str(buf), 0600);
742 	if (fp == -1)
743 	    return;
744 	oldidfds = didfds;
745 	didfds = 0;
746 	ftmp = SHOUT;
747 	SHOUT = fp;
748 	(void) Strcpy(buf, value(STRsavehist));
749 	dumphist[2] = buf;
750 	dohist(dumphist, NULL);
751 	SHOUT = ftmp;
752 	(void) close(fp);
753 	didfds = oldidfds;
754     }
755 }
756 
757 void
758 goodbye()
759 {
760     rechist();
761 
762     if (loginsh) {
763 	(void) signal(SIGQUIT, SIG_IGN);
764 	(void) signal(SIGINT, SIG_IGN);
765 	(void) signal(SIGTERM, SIG_IGN);
766 	setintr = 0;		/* No interrupts after "logout" */
767 	if (!(adrof(STRlogout)))
768 	    set(STRlogout, STRnormal);
769 #ifdef _PATH_DOTLOGOUT
770 	(void) srcfile(_PATH_DOTLOGOUT, 0, 0);
771 #endif
772 	if (adrof(STRhome))
773 	    (void) srccat(value(STRhome), STRsldtlogout);
774     }
775     exitstat();
776 }
777 
778 void
779 exitstat()
780 {
781     Char *s;
782 #ifdef PROF
783     monitor(0);
784 #endif
785     /*
786      * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
787      * directly because we poke child here. Otherwise we might continue
788      * unwarrantedly (sic).
789      */
790     child = 1;
791     s = value(STRstatus);
792     xexit(s ? getn(s) : 0);
793 }
794 
795 /*
796  * in the event of a HUP we want to save the history
797  */
798 static void
799 phup(sig)
800 int sig;
801 {
802     rechist();
803     xexit(sig);
804 }
805 
806 Char   *jobargv[2] = {STRjobs, 0};
807 
808 /*
809  * Catch an interrupt, e.g. during lexical input.
810  * If we are an interactive shell, we reset the interrupt catch
811  * immediately.  In any case we drain the shell output,
812  * and finally go through the normal error mechanism, which
813  * gets a chance to make the shell go away.
814  */
815 /* ARGSUSED */
816 void
817 pintr(notused)
818 	int notused;
819 {
820     pintr1(1);
821 }
822 
823 void
824 pintr1(wantnl)
825     bool    wantnl;
826 {
827     Char **v;
828     sigset_t omask;
829 
830     omask = sigblock((sigset_t) 0);
831     if (setintr) {
832 	(void) sigsetmask(omask & ~sigmask(SIGINT));
833 	if (pjobs) {
834 	    pjobs = 0;
835 	    (void) fprintf(cshout, "\n");
836 	    dojobs(jobargv, NULL);
837 	    stderror(ERR_NAME | ERR_INTR);
838 	}
839     }
840     (void) sigsetmask(omask & ~sigmask(SIGCHLD));
841     (void) fpurge(cshout);
842     (void) endpwent();
843 
844     /*
845      * If we have an active "onintr" then we search for the label. Note that if
846      * one does "onintr -" then we shan't be interruptible so we needn't worry
847      * about that here.
848      */
849     if (gointr) {
850 	gotolab(gointr);
851 	timflg = 0;
852 	if (v = pargv)
853 	    pargv = 0, blkfree(v);
854 	if (v = gargv)
855 	    gargv = 0, blkfree(v);
856 	reset();
857     }
858     else if (intty && wantnl) {
859 	(void) fputc('\r', cshout);
860 	(void) fputc('\n', cshout);
861     }
862     stderror(ERR_SILENT);
863 }
864 
865 /*
866  * Process is the main driving routine for the shell.
867  * It runs all command processing, except for those within { ... }
868  * in expressions (which is run by a routine evalav in sh.exp.c which
869  * is a stripped down process), and `...` evaluation which is run
870  * also by a subset of this code in sh.glob.c in the routine backeval.
871  *
872  * The code here is a little strange because part of it is interruptible
873  * and hence freeing of structures appears to occur when none is necessary
874  * if this is ignored.
875  *
876  * Note that if catch is not set then we will unwind on any error.
877  * If an end-of-file occurs, we return.
878  */
879 static struct command *savet = NULL;
880 void
881 process(catch)
882     bool    catch;
883 {
884     jmp_buf osetexit;
885     struct command *t = savet;
886 
887     savet = NULL;
888     getexit(osetexit);
889     for (;;) {
890 	pendjob();
891 	paraml.next = paraml.prev = &paraml;
892 	paraml.word = STRNULL;
893 	(void) setexit();
894 	justpr = enterhist;	/* execute if not entering history */
895 
896 	/*
897 	 * Interruptible during interactive reads
898 	 */
899 	if (setintr)
900 	    (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
901 
902 	/*
903 	 * For the sake of reset()
904 	 */
905 	freelex(&paraml);
906 	if (savet)
907 	    freesyn(savet), savet = NULL;
908 
909 	if (haderr) {
910 	    if (!catch) {
911 		/* unwind */
912 		doneinp = 0;
913 		resexit(osetexit);
914 		savet = t;
915 		reset();
916 	    }
917 	    haderr = 0;
918 	    /*
919 	     * Every error is eventually caught here or the shell dies.  It is
920 	     * at this point that we clean up any left-over open files, by
921 	     * closing all but a fixed number of pre-defined files.  Thus
922 	     * routines don't have to worry about leaving files open due to
923 	     * deeper errors... they will get closed here.
924 	     */
925 	    closem();
926 	    continue;
927 	}
928 	if (doneinp) {
929 	    doneinp = 0;
930 	    break;
931 	}
932 	if (chkstop)
933 	    chkstop--;
934 	if (neednote)
935 	    pnote();
936 	if (intty && prompt && evalvec == 0) {
937 	    mailchk();
938 	    /*
939 	     * If we are at the end of the input buffer then we are going to
940 	     * read fresh stuff. Otherwise, we are rereading input and don't
941 	     * need or want to prompt.
942 	     */
943 	    if (aret == F_SEEK && fseekp == feobp)
944 		printprompt();
945 	    (void) fflush(cshout);
946 	}
947 	if (seterr) {
948 	    xfree((ptr_t) seterr);
949 	    seterr = NULL;
950 	}
951 
952 	/*
953 	 * Echo not only on VERBOSE, but also with history expansion. If there
954 	 * is a lexical error then we forego history echo.
955 	 */
956 	if (lex(&paraml) && !seterr && intty || adrof(STRverbose)) {
957 	    prlex(csherr, &paraml);
958 	}
959 
960 	/*
961 	 * The parser may lose space if interrupted.
962 	 */
963 	if (setintr)
964 	    (void) sigblock(sigmask(SIGINT));
965 
966 	/*
967 	 * Save input text on the history list if reading in old history, or it
968 	 * is from the terminal at the top level and not in a loop.
969 	 *
970 	 * PWP: entry of items in the history list while in a while loop is done
971 	 * elsewhere...
972 	 */
973 	if (enterhist || catch && intty && !whyles)
974 	    savehist(&paraml);
975 
976 	/*
977 	 * Print lexical error messages, except when sourcing history lists.
978 	 */
979 	if (!enterhist && seterr)
980 	    stderror(ERR_OLD);
981 
982 	/*
983 	 * If had a history command :p modifier then this is as far as we
984 	 * should go
985 	 */
986 	if (justpr)
987 	    reset();
988 
989 	alias(&paraml);
990 
991 	/*
992 	 * Parse the words of the input into a parse tree.
993 	 */
994 	savet = syntax(paraml.next, &paraml, 0);
995 	if (seterr)
996 	    stderror(ERR_OLD);
997 
998 	execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
999 
1000 	/*
1001 	 * Made it!
1002 	 */
1003 	freelex(&paraml);
1004 	freesyn((struct command *) savet), savet = NULL;
1005     }
1006     resexit(osetexit);
1007     savet = t;
1008 }
1009 
1010 void
1011 /*ARGSUSED*/
1012 dosource(v, t)
1013     Char **v;
1014     struct command *t;
1015 
1016 {
1017     register Char *f;
1018     bool    hflg = 0;
1019     Char    buf[BUFSIZ];
1020 
1021     v++;
1022     if (*v && eq(*v, STRmh)) {
1023 	if (*++v == NULL)
1024 	    stderror(ERR_NAME | ERR_HFLAG);
1025 	hflg++;
1026     }
1027     (void) Strcpy(buf, *v);
1028     f = globone(buf, G_ERROR);
1029     (void) strcpy((char *) buf, short2str(f));
1030     xfree((ptr_t) f);
1031     if (!srcfile((char *) buf, 0, hflg) && !hflg)
1032 	stderror(ERR_SYSTEM, (char *) buf, strerror(errno));
1033 }
1034 
1035 /*
1036  * Check for mail.
1037  * If we are a login shell, then we don't want to tell
1038  * about any mail file unless its been modified
1039  * after the time we started.
1040  * This prevents us from telling the user things he already
1041  * knows, since the login program insists on saying
1042  * "You have mail."
1043  */
1044 static void
1045 mailchk()
1046 {
1047     register struct varent *v;
1048     register Char **vp;
1049     time_t  t;
1050     int     intvl, cnt;
1051     struct stat stb;
1052     bool    new;
1053 
1054     v = adrof(STRmail);
1055     if (v == 0)
1056 	return;
1057     (void) time(&t);
1058     vp = v->vec;
1059     cnt = blklen(vp);
1060     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1061     if (intvl < 1)
1062 	intvl = 1;
1063     if (chktim + intvl > t)
1064 	return;
1065     for (; *vp; vp++) {
1066 	if (stat(short2str(*vp), &stb) < 0)
1067 	    continue;
1068 	new = stb.st_mtime > time0.tv_sec;
1069 	if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1070 	    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
1071 	    loginsh && !new)
1072 	    continue;
1073 	if (cnt == 1)
1074 	    (void) fprintf(cshout, "You have %smail.\n", new ? "new " : "");
1075 	else
1076 	    (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
1077 			   short2str(*vp));
1078     }
1079     chktim = t;
1080 }
1081 
1082 /*
1083  * Extract a home directory from the password file
1084  * The argument points to a buffer where the name of the
1085  * user whose home directory is sought is currently.
1086  * We write the home directory of the user back there.
1087  */
1088 int
1089 gethdir(home)
1090     Char   *home;
1091 {
1092     Char   *h;
1093     struct passwd *pw;
1094 
1095     /*
1096      * Is it us?
1097      */
1098     if (*home == '\0') {
1099 	if (h = value(STRhome)) {
1100 	    (void) Strcpy(home, h);
1101 	    return 0;
1102 	}
1103 	else
1104 	    return 1;
1105     }
1106 
1107     if (pw = getpwnam(short2str(home))) {
1108 	(void) Strcpy(home, str2short(pw->pw_dir));
1109 	return 0;
1110     }
1111     else
1112 	return 1;
1113 }
1114 
1115 /*
1116  * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
1117  * We also check if the shell has already changed the decriptor to point to
1118  * 0, 1, 2 when didfds is set.
1119  */
1120 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
1121 
1122 static int
1123 readf(oreo, buf, siz)
1124     void *oreo;
1125     char *buf;
1126     int siz;
1127 {
1128     return read(DESC(oreo), buf, siz);
1129 }
1130 
1131 
1132 static int
1133 writef(oreo, buf, siz)
1134     void *oreo;
1135     const char *buf;
1136     int siz;
1137 {
1138     return write(DESC(oreo), buf, siz);
1139 }
1140 
1141 static fpos_t
1142 seekf(oreo, off, whence)
1143     void *oreo;
1144     fpos_t off;
1145     int whence;
1146 {
1147     return lseek(DESC(oreo), off, whence);
1148 }
1149 
1150 
1151 static int
1152 closef(oreo)
1153     void *oreo;
1154 {
1155     return close(DESC(oreo));
1156 }
1157 
1158 
1159 /*
1160  * Move the initial descriptors to their eventual
1161  * resting places, closin all other units.
1162  */
1163 void
1164 initdesc()
1165 {
1166 
1167     didfds = 0;			/* 0, 1, 2 aren't set up */
1168     (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL);
1169     (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
1170     (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL);
1171     (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
1172     closem();
1173 }
1174 
1175 
1176 void
1177 #ifdef PROF
1178 done(i)
1179 #else
1180 xexit(i)
1181 #endif
1182     int     i;
1183 {
1184     untty();
1185     _exit(i);
1186 }
1187 
1188 static Char **
1189 defaultpath()
1190 {
1191     char   *ptr;
1192     Char  **blk, **blkp;
1193     struct stat stb;
1194 
1195     blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10);
1196 
1197 #define DIRAPPEND(a)  \
1198 	if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \
1199 		*blkp++ = SAVE(ptr)
1200 
1201     DIRAPPEND(_PATH_BIN);
1202     DIRAPPEND(_PATH_USRBIN);
1203 
1204 #undef DIRAPPEND
1205 
1206     *blkp++ = Strsave(STRdot);
1207     *blkp = NULL;
1208     return (blk);
1209 }
1210 
1211 void
1212 printprompt()
1213 {
1214     register Char *cp;
1215 
1216     if (!whyles) {
1217 	for (cp = value(STRprompt); *cp; cp++)
1218 	    if (*cp == HIST)
1219 		(void) fprintf(cshout, "%d", eventno + 1);
1220 	    else {
1221 		if (*cp == '\\' && cp[1] == HIST)
1222 		    cp++;
1223 		(void) fputc(*cp | QUOTE, cshout);
1224 	    }
1225     }
1226     else
1227 	/*
1228 	 * Prompt for forward reading loop body content.
1229 	 */
1230 	(void) fprintf(cshout, "? ");
1231     (void) fflush(cshout);
1232 }
1233