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