1 /*        $NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $       */
2 
3 /*-
4  * Copyright (c) 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)eval.c      8.9 (Berkeley) 6/8/95";
39 #else
40 __RCSID("$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <stdbool.h>
45 #include <stdlib.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <limits.h>
51 #include <unistd.h>
52 #include <sys/fcntl.h>
53 #include <sys/stat.h>
54 #include <sys/times.h>
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <sys/wait.h>
58 #include <sys/sysctl.h>
59 
60 /*
61  * Evaluate a command.
62  */
63 
64 #include "shell.h"
65 #include "nodes.h"
66 #include "syntax.h"
67 #include "expand.h"
68 #include "parser.h"
69 #include "jobs.h"
70 #include "eval.h"
71 #include "builtins.h"
72 #include "options.h"
73 #include "exec.h"
74 #include "redir.h"
75 #include "input.h"
76 #include "output.h"
77 #include "trap.h"
78 #include "var.h"
79 #include "memalloc.h"
80 #include "error.h"
81 #include "show.h"
82 #include "mystring.h"
83 #include "main.h"
84 #ifndef SMALL
85 #include "nodenames.h"
86 #include "myhistedit.h"
87 #endif
88 
89 
90 STATIC struct skipsave s_k_i_p;
91 #define   evalskip  (s_k_i_p.state)
92 #define   skipcount (s_k_i_p.count)
93 
94 STATIC int loopnest;                    /* current loop nesting level */
95 STATIC int funcnest;                    /* depth of function calls */
96 STATIC int builtin_flags;     /* evalcommand flags for builtins */
97 /*
98  * Base function nesting level inside a dot command.  Set to 0 initially
99  * and to (funcnest + 1) before every dot command to enable
100  *   1) detection of being in a file sourced by a dot command and
101  *   2) counting of function nesting in that file for the implementation
102  *      of the return command.
103  * The value is reset to its previous value after the dot command.
104  */
105 STATIC int dot_funcnest;
106 
107 
108 const char *commandname;
109 struct strlist *cmdenviron;
110 int exitstatus;                         /* exit status of last command */
111 int back_exitstatus;                    /* exit status of backquoted command */
112 
113 
114 STATIC void evalloop(union node *, int);
115 STATIC void evalfor(union node *, int);
116 STATIC void evalcase(union node *, int);
117 STATIC void evalsubshell(union node *, int);
118 STATIC void expredir(union node *);
119 STATIC void evalredir(union node *, int);
120 STATIC void evalpipe(union node *);
121 STATIC void evalcommand(union node *, int, struct backcmd *);
122 STATIC void prehash(union node *);
123 
124 STATIC char *find_dot_file(char *);
125 
126 /*
127  * Called to reset things after an exception.
128  */
129 
130 #ifdef mkinit
131 INCLUDE "eval.h"
132 
133 RESET {
134           reset_eval();
135 }
136 
137 SHELLPROC {
138           exitstatus = 0;
139 }
140 #endif
141 
142 void
reset_eval(void)143 reset_eval(void)
144 {
145           evalskip = SKIPNONE;
146           dot_funcnest = 0;
147           loopnest = 0;
148           funcnest = 0;
149 }
150 
151 static int
sh_pipe(int fds[2])152 sh_pipe(int fds[2])
153 {
154           int nfd;
155 
156           if (pipe(fds))
157                     return -1;
158 
159           if (fds[0] < 3) {
160                     nfd = fcntl(fds[0], F_DUPFD, 3);
161                     if (nfd != -1) {
162                               close(fds[0]);
163                               fds[0] = nfd;
164                     }
165           }
166 
167           if (fds[1] < 3) {
168                     nfd = fcntl(fds[1], F_DUPFD, 3);
169                     if (nfd != -1) {
170                               close(fds[1]);
171                               fds[1] = nfd;
172                     }
173           }
174           return 0;
175 }
176 
177 
178 /*
179  * The eval command.
180  */
181 
182 int
evalcmd(int argc,char ** argv)183 evalcmd(int argc, char **argv)
184 {
185           char *p;
186           char *concat;
187           char **ap;
188 
189           if (argc > 1) {
190                     p = argv[1];
191                     if (argc > 2) {
192                               STARTSTACKSTR(concat);
193                               ap = argv + 2;
194                               for (;;) {
195                                         while (*p)
196                                                   STPUTC(*p++, concat);
197                                         if ((p = *ap++) == NULL)
198                                                   break;
199                                         STPUTC(' ', concat);
200                               }
201                               STPUTC('\0', concat);
202                               p = grabstackstr(concat);
203                     }
204                     evalstring(p, builtin_flags & EV_TESTED);
205           } else
206                     exitstatus = 0;
207           return exitstatus;
208 }
209 
210 
211 /*
212  * Execute a command or commands contained in a string.
213  */
214 
215 void
evalstring(const char * s,int flag)216 evalstring(const char *s, int flag)
217 {
218           union node *n;
219           struct stackmark smark;
220           int last;
221           int any;
222 
223           last = flag & EV_EXIT;
224           flag &= ~EV_EXIT;
225 
226           setstackmark(&smark);
227           setinputstring(s, 1, line_number);
228 
229           any = 0;  /* to determine if exitstatus will have been set */
230           while ((n = parsecmd(0)) != NEOF) {
231                     XTRACE(DBG_EVAL, ("evalstring: "), showtree(n));
232                     if (n && nflag == 0) {
233                               if (last && at_eof())
234                                         evaltree(n, flag | EV_EXIT);
235                               else
236                                         evaltree(n, flag);
237                               any = 1;
238                               if (evalskip)
239                                         break;
240                     }
241                     rststackmark(&smark);
242           }
243           popfile();
244           popstackmark(&smark);
245           if (!any)
246                     exitstatus = 0;
247           if (last)
248                     exraise(EXEXIT);
249 }
250 
251 
252 
253 /*
254  * Evaluate a parse tree.  The value is left in the global variable
255  * exitstatus.
256  */
257 
258 void
evaltree(union node * n,int flags)259 evaltree(union node *n, int flags)
260 {
261           bool do_etest;
262           int sflags = flags & ~EV_EXIT;
263           union node *next;
264           struct stackmark smark;
265 
266           do_etest = false;
267           if (n == NULL || nflag) {
268                     VTRACE(DBG_EVAL, ("evaltree(%s) called\n",
269                         n == NULL ? "NULL" : "-n"));
270                     if (nflag == 0)
271                               exitstatus = 0;
272                     goto out2;
273           }
274 
275           setstackmark(&smark);
276           do {
277 #ifndef SMALL
278                     displayhist = 1; /* show history substitutions done with fc */
279 #endif
280                     next = NULL;
281                     CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n",
282                         getpid(), n, NODETYPENAME(n->type), n->type, flags));
283           /*
284                     if (n->type != NCMD && traps_invalid)
285                               free_traps();
286           */
287                     switch (n->type) {
288                     case NSEMI:
289                               evaltree(n->nbinary.ch1, sflags);
290                               if (nflag || evalskip)
291                                         goto out1;
292                               next = n->nbinary.ch2;
293                               break;
294                     case NAND:
295                               evaltree(n->nbinary.ch1, EV_TESTED);
296                               if (nflag || evalskip || exitstatus != 0)
297                                         goto out1;
298                               next = n->nbinary.ch2;
299                               break;
300                     case NOR:
301                               evaltree(n->nbinary.ch1, EV_TESTED);
302                               if (nflag || evalskip || exitstatus == 0)
303                                         goto out1;
304                               next = n->nbinary.ch2;
305                               break;
306                     case NREDIR:
307                               if (traps_invalid)
308                                         free_traps();
309                               evalredir(n, flags);
310                               break;
311                     case NSUBSHELL:
312                               evalsubshell(n, flags);
313                               do_etest = !(flags & EV_TESTED);
314                               break;
315                     case NBACKGND:
316                               if (traps_invalid)
317                                         free_traps();
318                               evalsubshell(n, flags);
319                               break;
320                     case NIF: {
321                               if (traps_invalid)
322                                         free_traps();
323                               evaltree(n->nif.test, EV_TESTED);
324                               if (nflag || evalskip)
325                                         goto out1;
326                               if (exitstatus == 0)
327                                         next = n->nif.ifpart;
328                               else if (n->nif.elsepart)
329                                         next = n->nif.elsepart;
330                               else
331                                         exitstatus = 0;
332                               break;
333                     }
334                     case NWHILE:
335                     case NUNTIL:
336                               if (traps_invalid)
337                                         free_traps();
338                               evalloop(n, sflags);
339                               break;
340                     case NFOR:
341                               if (traps_invalid)
342                                         free_traps();
343                               evalfor(n, sflags);
344                               break;
345                     case NCASE:
346                               if (traps_invalid)
347                                         free_traps();
348                               evalcase(n, sflags);
349                               break;
350                     case NDEFUN:
351                               if (traps_invalid)
352                                         free_traps();
353                               CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n",
354                                   n->narg.text, n->narg.lineno,
355                                   fnline1 ? " LINENO=1" : ""));
356                               defun(n->narg.text, n->narg.next, n->narg.lineno);
357                               exitstatus = 0;
358                               break;
359                     case NNOT:
360                               evaltree(n->nnot.com, EV_TESTED);
361                               exitstatus = !exitstatus;
362                               break;
363                     case NDNOT:
364                               evaltree(n->nnot.com, EV_TESTED);
365                               if (exitstatus != 0)
366                                         exitstatus = 1;
367                               break;
368                     case NPIPE:
369                               if (traps_invalid)
370                                         free_traps();
371                               evalpipe(n);
372                               do_etest = !(flags & EV_TESTED);
373                               break;
374                     case NCMD:
375                               evalcommand(n, flags, NULL);
376                               do_etest = !(flags & EV_TESTED);
377                               break;
378                     default:
379 #ifdef NODETYPENAME
380                               out1fmt("Node type = %d(%s)\n",
381                                         n->type, NODETYPENAME(n->type));
382 #else
383                               out1fmt("Node type = %d\n", n->type);
384 #endif
385                               flushout(&output);
386                               break;
387                     }
388                     n = next;
389                     rststackmark(&smark);
390           } while(n != NULL);
391  out1:
392           popstackmark(&smark);
393  out2:
394           if (pendingsigs)
395                     dotrap();
396           if (eflag && exitstatus != 0 && do_etest)
397                     exitshell(exitstatus);
398           if (flags & EV_EXIT)
399                     exraise(EXEXIT);
400 }
401 
402 
403 STATIC void
evalloop(union node * n,int flags)404 evalloop(union node *n, int flags)
405 {
406           int status;
407 
408           loopnest++;
409           status = 0;
410 
411           CTRACE(DBG_EVAL,  ("evalloop %s:", NODETYPENAME(n->type)));
412           VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1));
413           VXTRACE(DBG_EVAL, ("evalloop    do: "), showtree(n->nbinary.ch2));
414           VTRACE(DBG_EVAL,  ("evalloop  done\n"));
415           CTRACE(DBG_EVAL,  ("\n"));
416 
417           for (;;) {
418                     evaltree(n->nbinary.ch1, EV_TESTED);
419                     if (nflag)
420                               break;
421                     if (evalskip) {
422  skipping:                    if (evalskip == SKIPCONT && --skipcount <= 0) {
423                                         evalskip = SKIPNONE;
424                                         continue;
425                               }
426                               if (evalskip == SKIPBREAK && --skipcount <= 0)
427                                         evalskip = SKIPNONE;
428                               if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
429                                         status = exitstatus;
430                               break;
431                     }
432                     if (n->type == NWHILE) {
433                               if (exitstatus != 0)
434                                         break;
435                     } else {
436                               if (exitstatus == 0)
437                                         break;
438                     }
439                     evaltree(n->nbinary.ch2, flags & EV_TESTED);
440                     status = exitstatus;
441                     if (evalskip)
442                               goto skipping;
443           }
444           loopnest--;
445           exitstatus = status;
446 }
447 
448 
449 
450 STATIC void
evalfor(union node * n,int flags)451 evalfor(union node *n, int flags)
452 {
453           struct arglist arglist;
454           union node *argp;
455           struct strlist *sp;
456           struct stackmark smark;
457           int status;
458 
459           status = nflag ? exitstatus : 0;
460 
461           setstackmark(&smark);
462           arglist.lastp = &arglist.list;
463           for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
464                     expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
465                     if (evalskip)
466                               goto out;
467           }
468           *arglist.lastp = NULL;
469 
470           loopnest++;
471           for (sp = arglist.list ; sp ; sp = sp->next) {
472                     line_number = n->nfor.lineno;
473                     if (xflag) {
474                               outxstr(expandstr(ps4val(), line_number));
475                               outxstr("for ");
476                               outxstr(n->nfor.var);
477                               outxc('=');
478                               outxshstr(sp->text);
479                               outxc('\n');
480                               flushout(outx);
481                     }
482 
483                     setvar(n->nfor.var, sp->text, 0);
484                     evaltree(n->nfor.body, flags & EV_TESTED);
485                     status = exitstatus;
486                     if (nflag)
487                               break;
488                     if (evalskip) {
489                               if (evalskip == SKIPCONT && --skipcount <= 0) {
490                                         evalskip = SKIPNONE;
491                                         continue;
492                               }
493                               if (evalskip == SKIPBREAK && --skipcount <= 0)
494                                         evalskip = SKIPNONE;
495                               break;
496                     }
497           }
498           loopnest--;
499           exitstatus = status;
500  out:
501           popstackmark(&smark);
502 }
503 
504 
505 
506 STATIC void
evalcase(union node * n,int flags)507 evalcase(union node *n, int flags)
508 {
509           union node *cp, *ncp;
510           union node *patp;
511           struct arglist arglist;
512           struct stackmark smark;
513           int status = 0;
514 
515           setstackmark(&smark);
516           arglist.lastp = &arglist.list;
517           line_number = n->ncase.lineno;
518           expandarg(n->ncase.expr, &arglist, EXP_TILDE);
519           for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
520                     for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
521                               line_number = patp->narg.lineno;
522                               if (casematch(patp, arglist.list->text)) {
523                                         while (cp != NULL && evalskip == 0 &&
524                                             nflag == 0) {
525                                                   if (cp->type == NCLISTCONT)
526                                                             ncp = cp->nclist.next;
527                                                   else
528                                                             ncp = NULL;
529                                                   line_number = cp->nclist.lineno;
530                                                   evaltree(cp->nclist.body, flags);
531                                                   status = exitstatus;
532                                                   cp = ncp;
533                                         }
534                                         goto out;
535                               }
536                     }
537           }
538  out:
539           exitstatus = status;
540           popstackmark(&smark);
541 }
542 
543 
544 
545 /*
546  * Kick off a subshell to evaluate a tree.
547  */
548 
549 STATIC void
evalsubshell(union node * n,int flags)550 evalsubshell(union node *n, int flags)
551 {
552           struct job *jp= NULL;
553           int backgnd = (n->type == NBACKGND);
554 
555           expredir(n->nredir.redirect);
556           if (xflag && n->nredir.redirect) {
557                     union node *rn;
558 
559                     outxstr(expandstr(ps4val(), line_number));
560                     outxstr("using redirections:");
561                     for (rn = n->nredir.redirect; rn; rn = rn->nfile.next)
562                               (void) outredir(outx, rn, ' ');
563                     outxstr(" do subshell ("/*)*/);
564                     if (backgnd)
565                               outxstr(/*(*/") &");
566                     outxc('\n');
567                     flushout(outx);
568           }
569           INTOFF;
570           if ((!backgnd && flags & EV_EXIT && !have_traps() && !anyjobs()) ||
571               forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) {
572                     if (backgnd)
573                               flags &=~ EV_TESTED;
574                     INTON;
575                     redirect(n->nredir.redirect, REDIR_KEEP);
576                     evaltree(n->nredir.n, flags | EV_EXIT);   /* never returns */
577           } else if (backgnd) {
578                     jobstarted(jp);
579                     exitstatus = 0;
580           } else
581                     exitstatus = waitforjob(jp);
582           INTON;
583 
584           if (!backgnd && xflag && n->nredir.redirect) {
585                     outxstr(expandstr(ps4val(), line_number));
586                     outxstr(/*(*/") done subshell\n");
587                     flushout(outx);
588           }
589 }
590 
591 
592 
593 /*
594  * Compute the names of the files in a redirection list.
595  */
596 
597 STATIC void
expredir(union node * n)598 expredir(union node *n)
599 {
600           union node *redir;
601 
602           for (redir = n ; redir ; redir = redir->nfile.next) {
603                     struct arglist fn;
604 
605                     fn.lastp = &fn.list;
606                     switch (redir->type) {
607                     case NFROMTO:
608                     case NFROM:
609                     case NTO:
610                     case NCLOBBER:
611                     case NAPPEND:
612                               expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
613                               redir->nfile.expfname = fn.list->text;
614                               break;
615                     case NFROMFD:
616                     case NTOFD:
617                               if (redir->ndup.vname) {
618                                         expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
619                                         fixredir(redir, fn.list->text, 1);
620                               }
621                               break;
622                     case NHERE:
623                               redir->nhere.text = redir->nhere.doc->narg.text;
624                               break;
625                     case NXHERE:
626                               redir->nhere.text = expandhere(redir->nhere.doc);
627                               break;
628                     }
629           }
630 }
631 
632 /*
633  * Perform redirections for a compound command, and then do it (and restore)
634  */
635 STATIC void
evalredir(union node * n,int flags)636 evalredir(union node *n, int flags)
637 {
638           struct jmploc jmploc;
639           struct jmploc * const savehandler = handler;
640           volatile int in_redirect = 1;
641           const char * volatile PS4 = NULL;
642 
643           expredir(n->nredir.redirect);
644 
645           if (xflag && n->nredir.redirect) {
646                     union node *rn;
647 
648                     outxstr(PS4 = expandstr(ps4val(), line_number));
649                     outxstr("using redirections:");
650                     for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next)
651                               (void) outredir(outx, rn, ' ');
652                     outxstr(" do {\n"); /* } */
653                     flushout(outx);
654           }
655 
656           if (setjmp(jmploc.loc)) {
657                     int e;
658 
659                     handler = savehandler;
660                     e = exception;
661                     popredir(POPREDIR_UNDO);
662                     if (PS4 != NULL) {
663                               outxstr(PS4);
664                               /* { */ outxstr("} failed\n");
665                               flushout(outx);
666                     }
667                     if (e == EXERROR || e == EXEXEC) {
668                               if (in_redirect) {
669                                         exitstatus = 2;
670                                         return;
671                               }
672                     }
673                     longjmp(handler->loc, 1);
674           } else {
675                     INTOFF;
676                     handler = &jmploc;
677                     redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP);
678                     in_redirect = 0;
679                     INTON;
680                     evaltree(n->nredir.n, flags);
681           }
682           INTOFF;
683           handler = savehandler;
684           popredir(POPREDIR_UNDO);
685           INTON;
686 
687           if (PS4 != NULL) {
688                     outxstr(PS4);
689                     /* { */ outxstr("} done\n");
690                     flushout(outx);
691           }
692 }
693 
694 
695 /*
696  * Evaluate a pipeline.  All the processes in the pipeline are children
697  * of the process creating the pipeline.  (This differs from some versions
698  * of the shell, which make the last process in a pipeline the parent
699  * of all the rest.)
700  */
701 
702 STATIC void
evalpipe(union node * n)703 evalpipe(union node *n)
704 {
705           struct job *jp;
706           struct nodelist *lp;
707           int pipelen;
708           int prevfd;
709           int pip[2];
710 
711           CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n));
712           pipelen = 0;
713           for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
714                     pipelen++;
715           INTOFF;
716           jp = makejob(n, pipelen);
717           prevfd = -1;
718           for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
719                     prehash(lp->n);
720                     pip[1] = -1;
721                     if (lp->next) {
722                               if (sh_pipe(pip) < 0) {
723                                         if (prevfd >= 0)
724                                                   close(prevfd);
725                                         error("Pipe call failed: %s", strerror(errno));
726                               }
727                     }
728                     if (forkshell(jp, lp->n,
729                         n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
730                               INTON;
731                               if (prevfd > 0)
732                                         movefd(prevfd, 0);
733                               if (pip[1] >= 0) {
734                                         close(pip[0]);
735                                         movefd(pip[1], 1);
736                               }
737                               evaltree(lp->n, EV_EXIT);
738                     }
739                     if (prevfd >= 0)
740                               close(prevfd);
741                     prevfd = pip[0];
742                     close(pip[1]);
743           }
744           if (n->npipe.backgnd == 0) {
745                     exitstatus = waitforjob(jp);
746                     CTRACE(DBG_EVAL, ("evalpipe:  job done exit status %d\n",
747                         exitstatus));
748           } else {
749                     jobstarted(jp);
750                     exitstatus = 0;
751           }
752           INTON;
753 }
754 
755 
756 
757 /*
758  * Execute a command inside back quotes.  If it's a builtin command, we
759  * want to save its output in a block obtained from malloc.  Otherwise
760  * we fork off a subprocess and get the output of the command via a pipe.
761  * Should be called with interrupts off.
762  */
763 
764 void
evalbackcmd(union node * n,struct backcmd * result)765 evalbackcmd(union node *n, struct backcmd *result)
766 {
767           int pip[2];
768           struct job *jp;
769           struct stackmark smark;                 /* unnecessary (because we fork) */
770 
771           result->fd = -1;
772           result->buf = NULL;
773           result->nleft = 0;
774           result->jp = NULL;
775 
776           if (nflag || n == NULL)
777                     goto out;
778 
779           setstackmark(&smark);
780 
781 #ifdef notyet
782           /*
783            * For now we disable executing builtins in the same
784            * context as the shell, because we are not keeping
785            * enough state to recover from changes that are
786            * supposed only to affect subshells. eg. echo "`cd /`"
787            */
788           if (n->type == NCMD) {
789                     exitstatus = oexitstatus;     /* XXX o... no longer exists */
790                     evalcommand(n, EV_BACKCMD, result);
791           } else
792 #endif
793           {
794                     INTOFF;
795                     if (sh_pipe(pip) < 0)
796                               error("Pipe call failed");
797                     jp = makejob(n, 1);
798                     if (forkshell(jp, n, FORK_NOJOB) == 0) {
799                               FORCEINTON;
800                               close(pip[0]);
801                               movefd(pip[1], 1);
802                               evaltree(n, EV_EXIT);
803                               /* NOTREACHED */
804                     }
805                     close(pip[1]);
806                     result->fd = pip[0];
807                     result->jp = jp;
808                     INTON;
809           }
810           popstackmark(&smark);
811  out:
812           CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
813                     result->fd, result->buf, result->nleft, result->jp));
814 }
815 
816 const char *
syspath(void)817 syspath(void)
818 {
819           static char *sys_path = NULL;
820           static int mib[] = {CTL_USER, USER_CS_PATH};
821           static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
822           size_t len;
823 
824           if (sys_path == NULL) {
825                     if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
826                         (sys_path = ckmalloc(len + 5)) != NULL &&
827                         sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
828                               memcpy(sys_path, "PATH=", 5);
829                     } else {
830                               ckfree(sys_path);
831                               /* something to keep things happy */
832                               sys_path = def_path;
833                     }
834           }
835           return sys_path;
836 }
837 
838 static int
parse_command_args(int argc,char ** argv,int * use_syspath)839 parse_command_args(int argc, char **argv, int *use_syspath)
840 {
841           int sv_argc = argc;
842           char *cp, c;
843 
844           *use_syspath = 0;
845 
846           for (;;) {
847                     argv++;
848                     if (--argc == 0)
849                               break;
850                     cp = *argv;
851                     if (*cp++ != '-')
852                               break;
853                     if (*cp == '-' && cp[1] == 0) {
854                               argv++;
855                               argc--;
856                               break;
857                     }
858                     while ((c = *cp++)) {
859                               switch (c) {
860                               case 'p':
861                                         *use_syspath = 1;
862                                         break;
863                               default:
864                                         /* run 'typecmd' for other options */
865                                         return 0;
866                               }
867                     }
868           }
869           return sv_argc - argc;
870 }
871 
872 int vforked = 0;
873 
874 /*
875  * Execute a simple command.
876  */
877 
878 STATIC void
evalcommand(union node * cmd,int flgs,struct backcmd * backcmd)879 evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
880 {
881           struct stackmark smark;
882           union node *argp;
883           struct arglist arglist;
884           struct arglist varlist;
885           volatile int flags = flgs;
886           char ** volatile argv;
887           volatile int argc;
888           char **envp;
889           int varflag;
890           struct strlist *sp;
891           volatile int mode;
892           int pip[2];
893           struct cmdentry cmdentry;
894           struct job * volatile jp;
895           struct jmploc jmploc;
896           struct jmploc *volatile savehandler = NULL;
897           const char *volatile savecmdname;
898           volatile struct shparam saveparam;
899           struct localvar *volatile savelocalvars;
900           struct parsefile *volatile savetopfile;
901           volatile int e;
902           char * volatile lastarg;
903           const char * volatile path = pathval();
904           volatile int temp_path;
905           const int savefuncline = funclinebase;
906           const int savefuncabs = funclineabs;
907           volatile int cmd_flags = 0;
908 
909           vforked = 0;
910           /* First expand the arguments. */
911           CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags,
912               cmd->ncmd.args ? cmd->ncmd.args->narg.text : ""));
913           setstackmark(&smark);
914           back_exitstatus = 0;
915 
916           line_number = cmd->ncmd.lineno;
917 
918           arglist.lastp = &arglist.list;
919           varflag = 1;
920           /* Expand arguments, ignoring the initial 'name=value' ones */
921           for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
922                     if (varflag && isassignment(argp->narg.text))
923                               continue;
924                     varflag = 0;
925                     line_number = argp->narg.lineno;
926                     expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
927           }
928           *arglist.lastp = NULL;
929 
930           expredir(cmd->ncmd.redirect);
931 
932           /* Now do the initial 'name=value' ones we skipped above */
933           varlist.lastp = &varlist.list;
934           for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
935                     line_number = argp->narg.lineno;
936                     if (!isassignment(argp->narg.text))
937                               break;
938                     /* EXP_CASE handles CTL* chars in expansions properly */
939                     expandarg(argp, &varlist, EXP_VARTILDE | EXP_CASE);
940           }
941           *varlist.lastp = NULL;
942 
943           argc = 0;
944           for (sp = arglist.list ; sp ; sp = sp->next)
945                     argc++;
946           argv = stalloc(sizeof (char *) * (argc + 1));
947 
948           for (sp = arglist.list ; sp ; sp = sp->next) {
949                     VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text));
950                     *argv++ = sp->text;
951           }
952           *argv = NULL;
953           lastarg = NULL;
954           if (iflag && funcnest == 0 && argc > 0)
955                     lastarg = argv[-1];
956           argv -= argc;
957 
958           /* Print the command if xflag is set. */
959           if (xflag) {
960                     char sep = 0;
961                     union node *rn;
962 
963                     outxstr(expandstr(ps4val(), line_number));
964                     for (sp = varlist.list ; sp ; sp = sp->next) {
965                               char *p;
966 
967                               if (sep != 0)
968                                         outxc(sep);
969 
970                               /*
971                                * The "var=" part should not be quoted, regardless
972                                * of the value, or it would not represent an
973                                * assignment, but rather a command
974                                */
975                               p = strchr(sp->text, '=');
976                               if (p != NULL) {
977                                         *p = '\0';          /*XXX*/
978                                         outxshstr(sp->text);
979                                         outxc('=');
980                                         *p++ = '=';         /*XXX*/
981                               } else
982                                         p = sp->text;
983                               outxshstr(p);
984                               sep = ' ';
985                     }
986                     for (sp = arglist.list ; sp ; sp = sp->next) {
987                               if (sep != 0)
988                                         outxc(sep);
989                               outxshstr(sp->text);
990                               sep = ' ';
991                     }
992                     for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next)
993                               if (outredir(outx, rn, sep))
994                                         sep = ' ';
995                     outxc('\n');
996                     flushout(outx);
997           }
998 
999           /* Now locate the command. */
1000           if (argc == 0) {
1001                     /*
1002                      * the empty command begins as a normal builtin, and
1003                      * remains that way while redirects are processed, then
1004                      * will become special before we get to doing the
1005                      * var assigns.
1006                      */
1007                     cmdentry.cmdtype = CMDBUILTIN;
1008                     cmdentry.u.bltin = bltincmd;
1009                     VTRACE(DBG_CMDS, ("No command name, assume \"command\"\n"));
1010           } else {
1011                     static const char PATH[] = "PATH=";
1012 
1013                     /*
1014                      * Modify the command lookup path, if a PATH= assignment
1015                      * is present
1016                      */
1017                     for (sp = varlist.list; sp; sp = sp->next)
1018                               if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
1019                                         path = sp->text + sizeof(PATH) - 1;
1020 
1021                     do {
1022                               int argsused, use_syspath;
1023 
1024                               find_command(argv[0], &cmdentry, cmd_flags, path);
1025                               VTRACE(DBG_CMDS, ("Command %s type %d\n", argv[0],
1026                                   cmdentry.cmdtype));
1027 #if 0
1028                               /*
1029                                * This short circuits all of the processing that
1030                                * should be done (including processing the
1031                                * redirects), so just don't ...
1032                                *
1033                                * (eventually this whole #if'd block will vanish)
1034                                */
1035                               if (cmdentry.cmdtype == CMDUNKNOWN) {
1036                                         exitstatus = 127;
1037                                         flushout(&errout);
1038                                         goto out;
1039                               }
1040 #endif
1041 
1042                               /* implement the 'command' builtin here */
1043                               if (cmdentry.cmdtype != CMDBUILTIN ||
1044                                   cmdentry.u.bltin != bltincmd)
1045                                         break;
1046                               VTRACE(DBG_CMDS, ("Command \"command\"\n"));
1047                               cmd_flags |= DO_NOFUNC;
1048                               argsused = parse_command_args(argc, argv, &use_syspath);
1049                               if (argsused == 0) {
1050                                         /* use 'type' builtin to display info */
1051                                         VTRACE(DBG_CMDS,
1052                                             ("Command \"command\" -> \"type\"\n"));
1053                                         cmdentry.u.bltin = typecmd;
1054                                         break;
1055                               }
1056                               argc -= argsused;
1057                               argv += argsused;
1058                               if (use_syspath)
1059                                         path = syspath() + 5;
1060                     } while (argc != 0);
1061                     if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
1062                               /* posix mandates that 'command <splbltin>' act as if
1063                                  <splbltin> was a normal builtin */
1064                               cmdentry.cmdtype = CMDBUILTIN;
1065           }
1066 
1067           /*
1068            * When traps are invalid, we permit the following:
1069            *        trap
1070            *        command trap
1071            *        eval trap
1072            *        command eval trap
1073            *        eval command trap
1074            * without zapping the traps completely, in all other cases we do.
1075            * Function calls also do not zap the traps (but commands they execute
1076            * probably will) - this allows a function like
1077            *        trapstate() { trap -p; }
1078            * called as save_traps=$(trapstate).
1079            *
1080            * The test here permits eval "anything" but when evalstring() comes
1081            * back here again, the "anything" will be validated.
1082            * This means we can actually do:
1083            *        eval eval eval command eval eval command trap
1084            * as long as we end up with just "trap"
1085            *
1086            * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN
1087            *
1088            * trapcmd() takes care of doing free_traps() if it is needed there.
1089            */
1090           if (traps_invalid &&
1091               cmdentry.cmdtype != CMDFUNCTION &&
1092               ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) ||
1093                (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd)))
1094                     free_traps();
1095 
1096           /* Fork off a child process if necessary. */
1097           if (cmd->ncmd.backgnd
1098             || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
1099                && (have_traps() || (flags & EV_EXIT) == 0))
1100 #ifdef notyet                           /* EV_BACKCMD is never set currently */
1101                               /* this will need more work if/when it gets used */
1102             || ((flags & EV_BACKCMD) != 0
1103                && (cmdentry.cmdtype != CMDBUILTIN
1104                    && cmdentry.cmdtype != CMDSPLBLTIN)
1105                  || cmdentry.u.bltin == dotcmd
1106                  || cmdentry.u.bltin == evalcmd)
1107 #endif
1108            ) {
1109                     INTOFF;
1110                     jp = makejob(cmd, 1);
1111                     mode = cmd->ncmd.backgnd;
1112                     if (flags & EV_BACKCMD) {
1113                               mode = FORK_NOJOB;
1114                               if (sh_pipe(pip) < 0)
1115                                         error("Pipe call failed");
1116                     }
1117 #ifdef DO_SHAREDVFORK
1118                     /* It is essential that if DO_SHAREDVFORK is defined that the
1119                      * child's address space is actually shared with the parent as
1120                      * we rely on this.
1121                      */
1122                     if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL &&
1123                         (!cmd->ncmd.backgnd || cmd->ncmd.redirect == NULL)) {
1124                               pid_t     pid;
1125                               int serrno;
1126 
1127                               savelocalvars = localvars;
1128                               localvars = NULL;
1129                               vforked = 1;
1130           VFORK_BLOCK
1131                               switch (pid = vfork()) {
1132                               case -1:
1133                                         serrno = errno;
1134                                         VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n",
1135                                             serrno));
1136                                         INTON;
1137                                         error("Cannot vfork (%s)", strerror(serrno));
1138                                         break;
1139                               case 0:
1140                                         /* Make sure that exceptions only unwind to
1141                                          * after the vfork(2)
1142                                          */
1143                                         SHELL_FORKED();
1144                                         if (setjmp(jmploc.loc)) {
1145                                                   VTRACE(DBG_EVAL|DBG_ERRS|
1146                                                     DBG_PROCS|DBG_CMDS|DBG_TRAP,
1147                                                     ("vfork child exit exception:%d "
1148                                                      "exitstatus:%d exerrno:%d\n",
1149                                                      exception, exitstatus, exerrno));
1150 
1151                                                   if (exception == EXSHELLPROC) {
1152                                                             /*
1153                                                              * We can't progress with the
1154                                                              * vfork, so, set vforked = 2
1155                                                              * so the parent knows,
1156                                                              * and _exit();
1157                                                              */
1158                                                             vforked = 2;
1159                                                             _exit(0);
1160                                                   } else {
1161                                                             _exit(exception == EXEXIT ?
1162                                                                 exitstatus : exerrno);
1163                                                   }
1164                                         }
1165                                         savehandler = handler;
1166                                         handler = &jmploc;
1167                                         listmklocal(varlist.list,
1168                                             VDOEXPORT | VEXPORT | VNOFUNC);
1169                                         forkchild(jp, cmd, mode, vforked);
1170                                         break;
1171                               default:
1172                                         VFORK_UNDO();
1173                                                             /* restore from vfork(2) */
1174                                         CTRACE(DBG_PROCS|DBG_CMDS,
1175                                             ("parent after vfork - vforked=%d\n",
1176                                               vforked));
1177                                         handler = savehandler;
1178                                         poplocalvars();
1179                                         localvars = savelocalvars;
1180                                         if (vforked == 2) {
1181                                                   vforked = 0;
1182 
1183                                                   (void)waitpid(pid, NULL, 0);
1184                                                   /*
1185                                                    * We need to progress in a
1186                                                    * normal fork fashion
1187                                                    */
1188                                                   goto normal_fork;
1189                                         }
1190                                         /*
1191                                          * Here the child has left home,
1192                                          * getting on with its life, so
1193                                          * so must we...
1194                                          */
1195                                         vforked = 0;
1196                                         forkparent(jp, cmd, mode, pid);
1197                                         goto parent;
1198                               }
1199           VFORK_END
1200                     } else {
1201  normal_fork:
1202 #endif
1203                               if (forkshell(jp, cmd, mode) != 0)
1204                                         goto parent;        /* at end of routine */
1205                               CTRACE(DBG_PROCS|DBG_CMDS, ("Child sets EV_EXIT\n"));
1206                               flags |= EV_EXIT;
1207                               FORCEINTON;
1208 #ifdef DO_SHAREDVFORK
1209                     }
1210 #endif
1211                     if (flags & EV_BACKCMD) {
1212                               if (!vforked) {
1213                                         FORCEINTON;
1214                               }
1215                               close(pip[0]);
1216                               movefd(pip[1], 1);
1217                     }
1218                     flags |= EV_EXIT;
1219           }
1220 
1221           /* This is the child process if a fork occurred. */
1222           /* Execute the command. */
1223           switch (cmdentry.cmdtype) {
1224                     volatile int saved;
1225                     struct funcdef * volatile savefunc;
1226                     const char * volatile savectx;
1227 
1228           case CMDFUNCTION:
1229                     VXTRACE(DBG_EVAL, ("Shell function%s:  ",vforked?" VF":""),
1230                         trargs(argv));
1231                     redirect(cmd->ncmd.redirect, REDIR_KEEP | (saved =
1232                               !(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0));
1233                     saveparam = shellparam;
1234                     shellparam.malloc = 0;
1235                     shellparam.reset = 1;
1236                     shellparam.nparam = argc - 1;
1237                     shellparam.p = argv + 1;
1238                     shellparam.optnext = NULL;
1239                     INTOFF;
1240                     savelocalvars = localvars;
1241                     savectx = currentcontext;
1242                     localvars = NULL;
1243                     reffunc(savefunc = cmdentry.u.func);
1244                     INTON;
1245                     if (setjmp(jmploc.loc)) {
1246                               if (exception == EXSHELLPROC) {
1247                                         freeparam((volatile struct shparam *)
1248                                             &saveparam);
1249                               } else {
1250                                         freeparam(&shellparam);
1251                                         shellparam = saveparam;
1252                               }
1253                               if (saved)
1254                                         popredir(POPREDIR_UNDO);
1255                               unreffunc(savefunc);
1256                               poplocalvars();
1257                               localvars = savelocalvars;
1258                               funclinebase = savefuncline;
1259                               funclineabs = savefuncabs;
1260                               currentcontext = savectx;
1261                               handler = savehandler;
1262                               longjmp(handler->loc, 1);
1263                     }
1264                     savehandler = handler;
1265                     handler = &jmploc;
1266                     if (cmdentry.u.func) {
1267                               if (cmdentry.lno_frel)
1268                                         funclinebase = cmdentry.lineno - 1;
1269                               else
1270                                         funclinebase = 0;
1271                               funclineabs = cmdentry.lineno;
1272                               currentcontext = argv[0];
1273 
1274                               VTRACE(DBG_EVAL,
1275                                 ("function: node: %d '%s' # %d%s; funclinebase=%d\n",
1276                                   getfuncnode(cmdentry.u.func)->type,
1277                                   NODETYPENAME(getfuncnode(cmdentry.u.func)->type),
1278                                   cmdentry.lineno, cmdentry.lno_frel?" (=1)":"",
1279                                   funclinebase));
1280                     }
1281                     listmklocal(varlist.list, VDOEXPORT | VEXPORT);
1282                     /* stop shell blowing its stack */
1283                     if (++funcnest > 1000)
1284                               error("too many nested function calls");
1285                     evaltree(getfuncnode(cmdentry.u.func),
1286                         flags & (EV_TESTED|EV_EXIT));
1287                     funcnest--;
1288                     INTOFF;
1289                     unreffunc(cmdentry.u.func);
1290                     poplocalvars();
1291                     localvars = savelocalvars;
1292                     funclinebase = savefuncline;
1293                     funclineabs = savefuncabs;
1294                     currentcontext = savectx;
1295                     freeparam(&shellparam);
1296                     shellparam = saveparam;
1297                     handler = savehandler;
1298                     if (saved)
1299                               popredir(POPREDIR_UNDO);
1300                     INTON;
1301                     if (evalskip == SKIPFUNC) {
1302                               evalskip = SKIPNONE;
1303                               skipcount = 0;
1304                     }
1305                     if (flags & EV_EXIT)
1306                               exitshell(exitstatus);
1307                     break;
1308 
1309           case CMDSPLBLTIN:
1310                     VTRACE(DBG_EVAL, ("special "));
1311           case CMDBUILTIN:
1312                     VXTRACE(DBG_EVAL, ("builtin command [%d]%s:  ", argc,
1313                         vforked ? " VF" : ""), trargs(argv));
1314 
1315                     if (cmdentry.u.bltin == execcmd) {
1316                               char **ap;
1317 
1318                               /*
1319                                * Work out how we should process redirections
1320                                * on the "exec" command.   We need REDIR_KEEP
1321                                * if we must not set close-on-exec, and REDIR_PUSH
1322                                * if we need to be able to undo them (in the
1323                                * exec command, only on  some kind of error).
1324                                *
1325                                * Skip "exec" (argv[0]) then examine args.
1326                                *
1327                                * This must be done manually, as nextopt()
1328                                * hasn't been init'd for this command yet.
1329                                * And it won't be until after redirections are done.
1330                                *
1331                                * "exec" currently takes no options (except "--"),
1332                                * but might one day, and this needs to keep working,
1333                                * so do it, kind of, properly.
1334                                *
1335                                * Note in the common cases argv[1] will be NULL
1336                                * (for exec just setting up redirectons) or will
1337                                * not start with a '-' ("exec cmd") so normally
1338                                * this loop will either never start or will break
1339                                * at the first test of the first iteration.
1340                                */
1341                               for (ap = argv + 1; *ap != NULL; ap++) {
1342 
1343                                         if (ap[0][0] != '-')
1344                                                   break;
1345 
1346                                         if (ap[0][1] == '\0')         /* "exec -" */
1347                                                   break;              /* or continue ?? */
1348 
1349                                         if (ap[0][1] == '-' && ap[0][2] == '\0') {
1350                                                   ap++;
1351                                                   break;
1352                                         }
1353 
1354 #if defined(DUMMY_EXAMPLE_CODE) && 0
1355                                         /*
1356                                          * if options are added to "exec" then
1357                                          * any which take an arg (like the common
1358                                          * in other shells "-a cmdname") need to
1359                                          * be recognised here, lest "cmdname" be
1360                                          * thought to be the cmd to exec
1361                                          */
1362 
1363                                         for (char *op = ap[0] + 1; *op; op++) {
1364                                                   switch (*op) {
1365                                                   case 'a':
1366                                                   case any others similar:
1367                                                             /* options needing an optarg */
1368                                                             if (op[1] == '\0' && ap[1])
1369                                                                       ap++;
1370                                                             break;
1371 
1372                                                   default:
1373                                                             /* options with no optarg */
1374                                                             continue;
1375                                                   }
1376                                                   break;
1377                                         }
1378 #endif /* DUMMY EXAMPLE CODE */
1379                               }
1380 
1381                               if (*ap != NULL)
1382                                         mode = REDIR_KEEP;  /* exec cmd <... */
1383                               else
1384                                         mode = 0;           /* exec < .... */
1385 
1386                               /*
1387                                * always save old fd setup in case of error()
1388                                * execcmd() will undo this if no error occurs
1389                                * (that is, in the case the shell has not vanished)
1390                                */
1391                               mode |= REDIR_PUSH;
1392                     } else                        /* any builtin execpt "exec" */
1393                               mode = REDIR_PUSH | REDIR_KEEP;
1394 
1395                     if (flags == EV_BACKCMD) {
1396                               memout.nleft = 0;
1397                               memout.nextc = memout.buf;
1398                               memout.bufsize = 64;
1399                               mode |= REDIR_BACKQ;
1400                     }
1401 
1402                     e = -1;
1403                     savecmdname = commandname;
1404                     savetopfile = getcurrentfile();
1405                     savehandler = handler;
1406                     temp_path = 0;
1407 
1408                     if (!setjmp(jmploc.loc)) {
1409                               handler = &jmploc;
1410 
1411                               /*
1412                                * We need to ensure the command hash table isn't
1413                                * corrupted by temporary PATH assignments.
1414                                * However we must ensure the 'local' command works!
1415                                */
1416                               if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
1417                                   cmdentry.u.bltin == typecmd)) {
1418                                         savelocalvars = localvars;
1419                                         localvars = 0;
1420                                         temp_path = 1;
1421                                         mklocal(path - 5 /* PATH= */, 0);
1422                               }
1423                               redirect(cmd->ncmd.redirect, mode);
1424 
1425                               /*
1426                                * the empty command is regarded as a normal
1427                                * builtin for the purposes of redirects, but
1428                                * is a special builtin for var assigns.
1429                                * (unless we are the "command" command.)
1430                                */
1431                               if (argc == 0 && !(cmd_flags & DO_NOFUNC))
1432                                         cmdentry.cmdtype = CMDSPLBLTIN;
1433 
1434                               /* exec is a special builtin, but needs this list... */
1435                               cmdenviron = varlist.list;
1436                               /* we must check 'readonly' flag for all builtins */
1437                               listsetvar(varlist.list,
1438                                         cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
1439                               commandname = argv[0];
1440                               /* initialize nextopt */
1441                               argptr = argv + 1;
1442                               optptr = NULL;
1443                               /* and getopt */
1444                               optreset = 1;
1445                               optind = 1;
1446                               builtin_flags = flags;
1447                               clr_err(out1);      /* discard previous I/O errors */
1448                               exitstatus = cmdentry.u.bltin(argc, argv);
1449                     } else {
1450                               e = exception;
1451                               if (e == EXINT)
1452                                         exitstatus = SIGINT + 128;
1453                               else if (e == EXEXEC)
1454                                         exitstatus = exerrno;
1455                               else if (e != EXEXIT)
1456                                         exitstatus = 2;
1457                     }
1458                     handler = savehandler;
1459                     flushall();
1460                     out1 = &output;
1461                     out2 = &errout;
1462                     freestdout();
1463                     if (temp_path) {
1464                               poplocalvars();
1465                               localvars = savelocalvars;
1466                     }
1467                     cmdenviron = NULL;
1468                     if (e != EXSHELLPROC) {
1469                               commandname = savecmdname;
1470                               if (flags & EV_EXIT)
1471                                         exitshell(exitstatus);
1472                     }
1473                     if (e != -1) {
1474                               if ((e != EXERROR && e != EXEXEC)
1475                                   || cmdentry.cmdtype == CMDSPLBLTIN)
1476                                         exraise(e);
1477                               popfilesupto(savetopfile);
1478                               FORCEINTON;
1479                     }
1480 
1481                     if (cmdentry.u.bltin != execcmd)
1482                               popredir(POPREDIR_UNDO);
1483 
1484                     if (flags == EV_BACKCMD) {
1485                               backcmd->buf = memout.buf;
1486                               backcmd->nleft = memout.nextc - memout.buf;
1487                               memout.buf = NULL;
1488                     }
1489                     break;
1490 
1491           default:
1492                     VXTRACE(DBG_EVAL, ("normal command%s:  ", vforked?" VF":""),
1493                         trargs(argv));
1494                     redirect(cmd->ncmd.redirect,
1495                         (vforked ? REDIR_VFORK : 0) | REDIR_KEEP);
1496                     if (!vforked)
1497                               for (sp = varlist.list ; sp ; sp = sp->next)
1498                                         setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK);
1499                     envp = environment();
1500                     shellexec(argv, envp, path, cmdentry.u.index, vforked);
1501                     break;
1502           }
1503           goto out;
1504 
1505  parent:                      /* parent process gets here (if we forked) */
1506 
1507           exitstatus = 0;               /* if not altered just below */
1508           if (mode == FORK_FG) {        /* argument to fork */
1509                     exitstatus = waitforjob(jp);
1510           } else if (mode == FORK_NOJOB) {
1511                     backcmd->fd = pip[0];
1512                     close(pip[1]);
1513                     backcmd->jp = jp;
1514           } else
1515                     jobstarted(jp);
1516 
1517           FORCEINTON;
1518 
1519  out:
1520           if (lastarg)
1521                     /* implement $_ for whatever use that really is */
1522                     (void) setvarsafe("_", lastarg, VNOERROR);
1523           popstackmark(&smark);
1524 }
1525 
1526 
1527 /*
1528  * Search for a command.  This is called before we fork so that the
1529  * location of the command will be available in the parent as well as
1530  * the child.  The check for "goodname" is an overly conservative
1531  * check that the name will not be subject to expansion.
1532  */
1533 
1534 STATIC void
prehash(union node * n)1535 prehash(union node *n)
1536 {
1537           struct cmdentry entry;
1538 
1539           if (n && n->type == NCMD && n->ncmd.args)
1540                     if (goodname(n->ncmd.args->narg.text))
1541                               find_command(n->ncmd.args->narg.text, &entry, 0,
1542                                              pathval());
1543 }
1544 
1545 int
in_function(void)1546 in_function(void)
1547 {
1548           return funcnest;
1549 }
1550 
1551 enum skipstate
current_skipstate(void)1552 current_skipstate(void)
1553 {
1554           return evalskip;
1555 }
1556 
1557 void
save_skipstate(struct skipsave * p)1558 save_skipstate(struct skipsave *p)
1559 {
1560           *p = s_k_i_p;
1561 }
1562 
1563 void
restore_skipstate(const struct skipsave * p)1564 restore_skipstate(const struct skipsave *p)
1565 {
1566           s_k_i_p = *p;
1567 }
1568 
1569 void
stop_skipping(void)1570 stop_skipping(void)
1571 {
1572           evalskip = SKIPNONE;
1573           skipcount = 0;
1574 }
1575 
1576 /*
1577  * Builtin commands.  Builtin commands whose functions are closely
1578  * tied to evaluation are implemented here.
1579  */
1580 
1581 /*
1582  * No command given.
1583  */
1584 
1585 int
bltincmd(int argc,char ** argv)1586 bltincmd(int argc, char **argv)
1587 {
1588           /*
1589            * Preserve exitstatus of a previous possible redirection
1590            * as POSIX mandates
1591            */
1592           return back_exitstatus;
1593 }
1594 
1595 
1596 /*
1597  * Handle break and continue commands.  Break, continue, and return are
1598  * all handled by setting the evalskip flag.  The evaluation routines
1599  * above all check this flag, and if it is set they start skipping
1600  * commands rather than executing them.  The variable skipcount is
1601  * the number of loops to break/continue, or the number of function
1602  * levels to return.  (The latter is always 1.)  It should probably
1603  * be an error to break out of more loops than exist, but it isn't
1604  * in the standard shell so we don't make it one here.
1605  */
1606 
1607 int
breakcmd(int argc,char ** argv)1608 breakcmd(int argc, char **argv)
1609 {
1610           int n = argc > 1 ? number(argv[1]) : 1;
1611 
1612           if (n <= 0)
1613                     error("invalid count: %d", n);
1614           if (n > loopnest)
1615                     n = loopnest;
1616           if (n > 0) {
1617                     evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1618                     skipcount = n;
1619           }
1620           return 0;
1621 }
1622 
1623 int
dotcmd(int argc,char ** argv)1624 dotcmd(int argc, char **argv)
1625 {
1626           exitstatus = 0;
1627 
1628           (void) nextopt(NULL);                   /* ignore a leading "--" */
1629 
1630           if (*argptr != NULL) {                  /* That's what SVR2 does */
1631                     char *fullname;
1632                     /*
1633                      * dot_funcnest needs to be 0 when not in a dotcmd, so it
1634                      * cannot be restored with (funcnest + 1).
1635                      */
1636                     int dot_funcnest_old;
1637                     struct stackmark smark;
1638 
1639                     setstackmark(&smark);
1640                     fullname = find_dot_file(*argptr);
1641                     setinputfile(fullname, 1);
1642                     commandname = fullname;
1643                     dot_funcnest_old = dot_funcnest;
1644                     dot_funcnest = funcnest + 1;
1645                     cmdloop(0);
1646                     dot_funcnest = dot_funcnest_old;
1647                     popfile();
1648                     popstackmark(&smark);
1649           }
1650           return exitstatus;
1651 }
1652 
1653 /*
1654  * allow dotfile function nesting to be manipulated
1655  * (for read_profile).  This allows profile files to
1656  * be treated as if they were used as '.' commands,
1657  * (approximately) and in particular, for "return" to work.
1658  */
1659 int
set_dot_funcnest(int new)1660 set_dot_funcnest(int new)
1661 {
1662           int rv = dot_funcnest;
1663 
1664           if (new >= 0)
1665                     dot_funcnest = new;
1666 
1667           return rv;
1668 }
1669 
1670 /*
1671  * Take commands from a file.  To be compatible we should do a path
1672  * search for the file, which is necessary to find sub-commands.
1673  */
1674 
1675 STATIC char *
find_dot_file(char * basename)1676 find_dot_file(char *basename)
1677 {
1678           char *fullname;
1679           const char *path = pathval();
1680           struct stat statb;
1681 
1682           /* don't try this for absolute or relative paths */
1683           if (strchr(basename, '/')) {
1684                     if (stat(basename, &statb) == 0) {
1685                               if (S_ISDIR(statb.st_mode))
1686                                         error("%s: is a directory", basename);
1687                               if (S_ISBLK(statb.st_mode))
1688                                         error("%s: is a block device", basename);
1689                               return basename;
1690                     }
1691           } else while ((fullname = padvance(&path, basename, 1)) != NULL) {
1692                     if ((stat(fullname, &statb) == 0)) {
1693                               /* weird format is to ease future code... */
1694                               if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode))
1695                                         ;
1696 #if notyet
1697                               else if (unreadable()) {
1698                                         /*
1699                                          * testing this via st_mode is ugly to get
1700                                          * correct (and would ignore ACLs).
1701                                          * better way is just to open the file.
1702                                          * But doing that here would (currently)
1703                                          * mean opening the file twice, which
1704                                          * might not be safe.  So, defer this
1705                                          * test until code is restructures so
1706                                          * we can return a fd.   Then we also
1707                                          * get to fix the mem leak just below...
1708                                          */
1709                               }
1710 #endif
1711                               else {
1712                                         /*
1713                                          * Don't bother freeing here, since
1714                                          * it will be freed by the caller.
1715                                          * XXX no it won't - a bug for later.
1716                                          */
1717                                         return fullname;
1718                               }
1719                     }
1720                     stunalloc(fullname);
1721           }
1722 
1723           /* not found in the PATH */
1724           error("%s: not found", basename);
1725           /* NOTREACHED */
1726 }
1727 
1728 
1729 
1730 /*
1731  * The return command.
1732  *
1733  * Quoth the POSIX standard:
1734  *   The return utility shall cause the shell to stop executing the current
1735  *   function or dot script. If the shell is not currently executing
1736  *   a function or dot script, the results are unspecified.
1737  *
1738  * As for the unspecified part, there seems to be no de-facto standard: bash
1739  * ignores the return with a warning, zsh ignores the return in interactive
1740  * mode but seems to liken it to exit in a script.  (checked May 2014)
1741  *
1742  * We choose to silently ignore the return.  Older versions of this shell
1743  * set evalskip to SKIPFILE causing the shell to (indirectly) exit.  This
1744  * had at least the problem of circumventing the check for stopped jobs,
1745  * which would occur for exit or ^D.
1746  */
1747 
1748 int
returncmd(int argc,char ** argv)1749 returncmd(int argc, char **argv)
1750 {
1751           int ret = argc > 1 ? number(argv[1]) : exitstatus;
1752 
1753           if ((dot_funcnest == 0 && funcnest)
1754               || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) {
1755                     evalskip = SKIPFUNC;
1756                     skipcount = 1;
1757           } else if (dot_funcnest > 0) {
1758                     evalskip = SKIPFILE;
1759                     skipcount = 1;
1760           } else {
1761                     /* XXX: should a warning be issued? */
1762                     ret = 0;
1763           }
1764 
1765           return ret;
1766 }
1767 
1768 
1769 int
falsecmd(int argc,char ** argv)1770 falsecmd(int argc, char **argv)
1771 {
1772           return 1;
1773 }
1774 
1775 
1776 int
truecmd(int argc,char ** argv)1777 truecmd(int argc, char **argv)
1778 {
1779           return 0;
1780 }
1781 
1782 
1783 int
execcmd(int argc,char ** argv)1784 execcmd(int argc, char **argv)
1785 {
1786           /*
1787            * BEWARE: if any options are added here, they must
1788            * also be added in evalcommand(), look for "DUMMY_EXAMPLE_CODE"
1789            * for example code for there.   Here the options would be
1790            * processed completely normally.
1791            */
1792           (void) nextopt("");           /* ignore a leading "--" */
1793 
1794           if (*argptr) {
1795                     struct strlist *sp;
1796 
1797                     iflag = 0;                    /* exit on error */
1798                     mflag = 0;
1799                     optschanged();
1800                     for (sp = cmdenviron; sp; sp = sp->next)
1801                               setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK);
1802                     shellexec(argptr, environment(), pathval(), 0, 0);
1803                     /* NOTREACHED */
1804           }
1805           popredir(POPREDIR_PERMANENT); /* make any redirections permanent */
1806           return 0;
1807 }
1808 
1809 static int
conv_time(clock_t ticks,char * seconds,size_t l)1810 conv_time(clock_t ticks, char *seconds, size_t l)
1811 {
1812           static clock_t tpm = 0;
1813           clock_t mins;
1814           int i;
1815 
1816           if (!tpm)
1817                     tpm = sysconf(_SC_CLK_TCK) * 60;
1818 
1819           mins = ticks / tpm;
1820           snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1821 
1822           if (seconds[0] == '6' && seconds[1] == '0') {
1823                     /* 59.99995 got rounded up... */
1824                     mins++;
1825                     strlcpy(seconds, "0.0", l);
1826                     return mins;
1827           }
1828 
1829           /* suppress trailing zeros */
1830           i = strlen(seconds) - 1;
1831           for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1832                     seconds[i] = 0;
1833           return mins;
1834 }
1835 
1836 int
timescmd(int argc,char ** argv)1837 timescmd(int argc, char **argv)
1838 {
1839           struct tms tms;
1840           int u, s, cu, cs;
1841           char us[8], ss[8], cus[8], css[8];
1842 
1843           nextopt("");
1844 
1845           times(&tms);
1846 
1847           u = conv_time(tms.tms_utime, us, sizeof(us));
1848           s = conv_time(tms.tms_stime, ss, sizeof(ss));
1849           cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1850           cs = conv_time(tms.tms_cstime, css, sizeof(css));
1851 
1852           outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1853                     u, us, s, ss, cu, cus, cs, css);
1854 
1855           return 0;
1856 }
1857