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