xref: /openbsd/bin/csh/csh.c (revision 324a3c8c)
1 /*	$OpenBSD: csh.c,v 1.51 2024/07/28 15:31:22 deraadt 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     rechist();	/* XXX big signal race */
904 
905     /*
906      * We kill the last foreground process group. It then becomes
907      * responsible to propagate the SIGHUP to its progeny.
908      */
909     {
910 	struct process *pp, *np;
911 
912 	for (pp = proclist.p_next; pp; pp = pp->p_next) {
913 	    np = pp;
914 	    /*
915 	     * Find if this job is in the foreground. It could be that
916 	     * the process leader has exited and the foreground flag
917 	     * is cleared for it.
918 	     */
919 	    do
920 		/*
921 		 * If a process is in the foreground; we try to kill
922 		 * its process group. If we succeed, then the
923 		 * whole job is gone. Otherwise we keep going...
924 		 * But avoid sending HUP to the shell again.
925 		 */
926 		if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
927 		    kill(-np->p_jobid, SIGHUP) != -1) {
928 		    /* In case the job was suspended... */
929 		    (void) kill(-np->p_jobid, SIGCONT);
930 		    break;
931 		}
932 	    while ((np = np->p_friends) != pp);
933 	}
934     }
935     xexit(sig);
936 }
937 
938 Char   *jobargv[2] = {STRjobs, 0};
939 
940 /*
941  * Catch an interrupt, e.g. during lexical input.
942  * If we are an interactive shell, we reset the interrupt catch
943  * immediately.  In any case we drain the shell output,
944  * and finally go through the normal error mechanism, which
945  * gets a chance to make the shell go away.
946  */
947 void
pintr(int notused)948 pintr(int notused)
949 {
950     int save_errno = errno;
951 
952     pintr1(1);
953     errno = save_errno;
954 }
955 
956 void
pintr1(bool wantnl)957 pintr1(bool wantnl)
958 {
959     sigset_t sigset, osigset;
960 
961     sigemptyset(&sigset);
962     sigprocmask(SIG_BLOCK, &sigset, &osigset);
963     if (setintr) {
964 	sigset = osigset;
965 	sigdelset(&sigset, SIGINT);
966 	sigprocmask(SIG_SETMASK, &sigset, NULL);
967 	if (pjobs) {
968 	    pjobs = 0;
969 	    (void) fprintf(cshout, "\n");
970 	    dojobs(jobargv, NULL);
971 	    stderror(ERR_NAME | ERR_INTR);
972 	}
973     }
974     sigdelset(&osigset, SIGCHLD);
975     sigprocmask(SIG_SETMASK, &osigset, NULL);
976     (void) fpurge(cshout);
977     (void) endpwent();
978 
979     /*
980      * If we have an active "onintr" then we search for the label. Note that if
981      * one does "onintr -" then we shan't be interruptible so we needn't worry
982      * about that here.
983      */
984     if (gointr) {
985 	gotolab(gointr);
986 	timflg = 0;
987 	blkfree(pargv);
988 	pargv = NULL;
989 	blkfree(gargv);
990 	gargv = NULL;
991 	reset();
992     }
993     else if (intty && wantnl) {
994 	(void) fputc('\r', cshout);
995 	(void) fputc('\n', cshout);
996     }
997     stderror(ERR_SILENT);
998 }
999 
1000 /*
1001  * Process is the main driving routine for the shell.
1002  * It runs all command processing, except for those within { ... }
1003  * in expressions (which is run by a routine evalav in sh.exp.c which
1004  * is a stripped down process), and `...` evaluation which is run
1005  * also by a subset of this code in sh.glob.c in the routine backeval.
1006  *
1007  * The code here is a little strange because part of it is interruptible
1008  * and hence freeing of structures appears to occur when none is necessary
1009  * if this is ignored.
1010  *
1011  * Note that if catch is not set then we will unwind on any error.
1012  * If an end-of-file occurs, we return.
1013  */
1014 static struct command *savet = NULL;
1015 void
process(bool catch)1016 process(bool catch)
1017 {
1018     jmp_buf osetexit;
1019     struct command *t = savet;
1020     sigset_t sigset;
1021 
1022     savet = NULL;
1023     getexit(osetexit);
1024     for (;;) {
1025 	pendjob();
1026 	paraml.next = paraml.prev = &paraml;
1027 	paraml.word = STRNULL;
1028 	(void) setexit();
1029 	justpr = enterhist;	/* execute if not entering history */
1030 
1031 	/*
1032 	 * Interruptible during interactive reads
1033 	 */
1034 	if (setintr) {
1035 	    sigemptyset(&sigset);
1036 	    sigaddset(&sigset, SIGINT);
1037 	    sigprocmask(SIG_UNBLOCK, &sigset, NULL);
1038 	}
1039 
1040 	/*
1041 	 * For the sake of reset()
1042 	 */
1043 	freelex(&paraml);
1044 	if (savet)
1045 	    freesyn(savet), savet = NULL;
1046 
1047 	if (haderr) {
1048 	    if (!catch) {
1049 		/* unwind */
1050 		doneinp = 0;
1051 		resexit(osetexit);
1052 		savet = t;
1053 		reset();
1054 	    }
1055 	    haderr = 0;
1056 	    /*
1057 	     * Every error is eventually caught here or the shell dies.  It is
1058 	     * at this point that we clean up any left-over open files, by
1059 	     * closing all but a fixed number of pre-defined files.  Thus
1060 	     * routines don't have to worry about leaving files open due to
1061 	     * deeper errors... they will get closed here.
1062 	     */
1063 	    closem();
1064 	    continue;
1065 	}
1066 	if (doneinp) {
1067 	    doneinp = 0;
1068 	    break;
1069 	}
1070 	if (chkstop)
1071 	    chkstop--;
1072 	if (neednote)
1073 	    pnote();
1074 	if (intty && prompt && evalvec == 0) {
1075 	    mailchk();
1076 	    /*
1077 	     * If we are at the end of the input buffer then we are going to
1078 	     * read fresh stuff. Otherwise, we are rereading input and don't
1079 	     * need or want to prompt.
1080 	     */
1081 	    needprompt = aret == F_SEEK && fseekp == feobp;
1082 	    if (!filec && needprompt)
1083 		printprompt();
1084 	    (void) fflush(cshout);
1085 	}
1086 	free(seterr);
1087 	seterr = NULL;
1088 
1089 	/*
1090 	 * Echo not only on VERBOSE, but also with history expansion. If there
1091 	 * is a lexical error then we forego history echo.
1092 	 */
1093 	if ((lex(&paraml) && !seterr && intty) || adrof(STRverbose)) {
1094 	    prlex(csherr, &paraml);
1095 	}
1096 
1097 	/*
1098 	 * The parser may lose space if interrupted.
1099 	 */
1100 	if (setintr)
1101 	    sigprocmask(SIG_BLOCK, &sigset, NULL);
1102 
1103 	/*
1104 	 * Save input text on the history list if reading in old history, or it
1105 	 * is from the terminal at the top level and not in a loop.
1106 	 *
1107 	 * PWP: entry of items in the history list while in a while loop is done
1108 	 * elsewhere...
1109 	 */
1110 	if (enterhist || (catch && intty && !whyles))
1111 	    savehist(&paraml);
1112 
1113 	/*
1114 	 * Print lexical error messages, except when sourcing history lists.
1115 	 */
1116 	if (!enterhist && seterr)
1117 	    stderror(ERR_OLD);
1118 
1119 	/*
1120 	 * If had a history command :p modifier then this is as far as we
1121 	 * should go
1122 	 */
1123 	if (justpr)
1124 	    reset();
1125 
1126 	alias(&paraml);
1127 
1128 	/*
1129 	 * Parse the words of the input into a parse tree.
1130 	 */
1131 	savet = syntax(paraml.next, &paraml, 0);
1132 	if (seterr)
1133 	    stderror(ERR_OLD);
1134 
1135 	execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
1136 
1137 	/*
1138 	 * Made it!
1139 	 */
1140 	freelex(&paraml);
1141 	freesyn((struct command *) savet), savet = NULL;
1142     }
1143     resexit(osetexit);
1144     savet = t;
1145 }
1146 
1147 void
dosource(Char ** v,struct command * t)1148 dosource(Char **v, struct command *t)
1149 {
1150     Char *f;
1151     bool    hflg = 0;
1152     Char    buf[BUFSIZ];
1153     char    sbuf[BUFSIZ];
1154 
1155     v++;
1156     if (*v && eq(*v, STRmh)) {
1157 	if (*++v == NULL)
1158 	    stderror(ERR_NAME | ERR_HFLAG);
1159 	hflg++;
1160     }
1161     (void) Strlcpy(buf, *v, sizeof buf/sizeof(Char));
1162     f = globone(buf, G_ERROR);
1163     (void) strlcpy(sbuf, short2str(f), sizeof sbuf);
1164     free(f);
1165     if (!srcfile(sbuf, 0, hflg) && !hflg)
1166 	stderror(ERR_SYSTEM, sbuf, strerror(errno));
1167 }
1168 
1169 /*
1170  * Check for mail.
1171  * If we are a login shell, then we don't want to tell
1172  * about any mail file unless its been modified
1173  * after the time we started.
1174  * This prevents us from telling the user things he already
1175  * knows, since the login program insists on saying
1176  * "You have mail."
1177  */
1178 static void
mailchk(void)1179 mailchk(void)
1180 {
1181     struct varent *v;
1182     Char **vp;
1183     time_t  t;
1184     int     intvl, cnt;
1185     struct stat stb;
1186     bool    new;
1187 
1188     v = adrof(STRmail);
1189     if (v == 0)
1190 	return;
1191     (void) time(&t);
1192     vp = v->vec;
1193     cnt = blklen(vp);
1194     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1195     if (intvl < 1)
1196 	intvl = 1;
1197     if (chktim + intvl > t)
1198 	return;
1199     for (; *vp; vp++) {
1200 	if (stat(short2str(*vp), &stb) == -1)
1201 	    continue;
1202 	new = stb.st_mtime > time0.tv_sec;
1203 	if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1204 	    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
1205 	    (loginsh && !new))
1206 	    continue;
1207 	if (cnt == 1)
1208 	    (void) fprintf(cshout, "You have %smail.\n", new ? "new " : "");
1209 	else
1210 	    (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
1211 			   vis_str(*vp));
1212     }
1213     chktim = t;
1214 }
1215 
1216 /*
1217  * Extract a home directory from the password file
1218  * The argument points to a buffer where the name of the
1219  * user whose home directory is sought is currently.
1220  * We write the home directory of the user back there.
1221  */
1222 int
gethdir(Char * home,int len)1223 gethdir(Char *home, int len)
1224 {
1225     Char   *h;
1226     struct passwd *pw;
1227 
1228     /*
1229      * Is it us?
1230      */
1231     if (*home == '\0') {
1232 	if ((h = value(STRhome)) != NULL) {
1233 	    if (Strlcpy(home, h, len) >= len)
1234 		return 1;
1235 	    return 0;
1236 	}
1237 	else
1238 	    return 1;
1239     }
1240 
1241     if ((pw = getpwnam(short2str(home))) != NULL) {
1242 	if (Strlcpy(home, str2short(pw->pw_dir), len) >= len)
1243 	    return 1;
1244 	return 0;
1245     }
1246     else
1247 	return 1;
1248 }
1249 
1250 /*
1251  * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
1252  * We also check if the shell has already changed the descriptor to point to
1253  * 0, 1, 2 when didfds is set.
1254  */
1255 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
1256 
1257 static int
readf(void * oreo,char * buf,int siz)1258 readf(void *oreo, char *buf, int siz)
1259 {
1260     return read(DESC(oreo), buf, siz);
1261 }
1262 
1263 
1264 static int
writef(void * oreo,const char * buf,int siz)1265 writef(void *oreo, const char *buf, int siz)
1266 {
1267     return write(DESC(oreo), buf, siz);
1268 }
1269 
1270 static off_t
seekf(void * oreo,off_t off,int whence)1271 seekf(void *oreo, off_t off, int whence)
1272 {
1273     return lseek(DESC(oreo), off, whence);
1274 }
1275 
1276 
1277 static int
closef(void * oreo)1278 closef(void *oreo)
1279 {
1280     return close(DESC(oreo));
1281 }
1282 
1283 
1284 /*
1285  * Print the visible version of a string.
1286  */
1287 int
vis_fputc(int ch,FILE * fp)1288 vis_fputc(int ch, FILE *fp)
1289 {
1290     char uenc[5];	/* 4 + NUL */
1291 
1292     if (ch & QUOTE)
1293 	return fputc(ch & TRIM, fp);
1294     (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0);
1295     return fputs(uenc, fp);
1296 }
1297 
1298 /*
1299  * Move the initial descriptors to their eventual
1300  * resting places, closing all other units.
1301  */
1302 void
initdesc(void)1303 initdesc(void)
1304 {
1305 
1306     didfds = 0;			/* 0, 1, 2 aren't set up */
1307     (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, FD_CLOEXEC);
1308     (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, FD_CLOEXEC);
1309     (void) fcntl(SHERR = dcopy(2, FSHERR), F_SETFD, FD_CLOEXEC);
1310     (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, FD_CLOEXEC);
1311     closem();
1312 }
1313 
1314 
1315 void
xexit(int i)1316 xexit(int i)
1317 {
1318     untty();
1319     _exit(i);
1320 }
1321 
1322 static Char **
defaultpath(void)1323 defaultpath(void)
1324 {
1325     char   *ptr;
1326     Char  **blk, **blkp;
1327     struct stat stb;
1328 
1329     blkp = blk = xreallocarray(NULL, 10, sizeof(Char *));
1330 
1331 #define DIRAPPEND(a)  \
1332 	if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
1333 		*blkp++ = SAVE(ptr)
1334 
1335     DIRAPPEND(_PATH_BIN);
1336     DIRAPPEND(_PATH_USRBIN);
1337 
1338 #undef DIRAPPEND
1339 
1340     *blkp = NULL;
1341     return (blk);
1342 }
1343 
1344 void
printprompt(void)1345 printprompt(void)
1346 {
1347     Char *cp;
1348 
1349     if (!whyles) {
1350 	for (cp = value(STRprompt); *cp; cp++)
1351 	    if (*cp == HIST)
1352 		(void) fprintf(cshout, "%d", eventno + 1);
1353 	    else if (*cp == '%' && *(cp + 1) == 'm') {
1354 		char hostname[HOST_NAME_MAX + 1];
1355 		char *p;
1356 
1357 		gethostname(hostname, sizeof hostname);
1358 		if ((p = strchr(hostname, '.')) != NULL)
1359 		    *p = '\0';
1360 		fprintf(cshout, "%s", hostname);
1361 		cp++;
1362 	    } else {
1363 		if (*cp == '\\' && cp[1] == HIST)
1364 		    cp++;
1365 		(void) vis_fputc(*cp | QUOTE, cshout);
1366 	    }
1367     }
1368     else
1369 	/*
1370 	 * Prompt for forward reading loop body content.
1371 	 */
1372 	(void) fprintf(cshout, "? ");
1373     (void) fflush(cshout);
1374 }
1375