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