1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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 <errno.h>
36#include <signal.h>
37#include <stdlib.h>
38#include <sys/resource.h>
39#include <unistd.h>
40
41/*
42 * Evaluate a command.
43 */
44
45#include "../../../shared/paths.h"
46#include "../../../shared/wexec.h"
47#include "builtins.h"
48#include "error.h"
49#include "eval.h"
50#include "exec.h"
51#include "expand.h"
52#include "input.h"
53#include "jobs.h"
54#include "memalloc.h"
55#include "mystring.h"
56#include "nodes.h"
57#include "options.h"
58#include "output.h"
59#include "parser.h"
60#include "redir.h"
61#include "shell.h"
62#include "show.h"
63#include "syntax.h"
64#include "trap.h"
65#include "var.h"
66#ifndef NO_HISTORY
67#include "lineedit.h"
68#endif
69
70int evalskip; /* set if we are skipping commands */
71int skipcount; /* number of levels to skip */
72static int loopnest; /* current loop nesting level */
73int funcnest; /* depth of function calls */
74static int builtin_flags; /* evalcommand flags for builtins */
75
76char *commandname;
77struct arglist *cmdenviron;
78int exitstatus; /* exit status of last command */
79int oexitstatus; /* saved exit status */
80
81static void evalloop(union node *, int);
82static void evalfor(union node *, int);
83static union node *evalcase(union node *);
84static void evalsubshell(union node *, int);
85static void evalredir(union node *, int);
86static void exphere(union node *, struct arglist *);
87static void expredir(union node *);
88static void evalpipe(union node *);
89static int is_valid_fast_cmdsubst(union node *n);
90static void evalcommand(union node *, int, struct backcmd *);
91static void prehash(union node *);
92
93/*
94 * Called to reset things after an exception.
95 */
96
97void
98reseteval(void)
99{
100 evalskip = 0;
101 loopnest = 0;
102}
103
104/*
105 * The eval command.
106 */
107
108int
109evalcmd(int argc, char **argv)
110{
111 char *p;
112 char *concat;
113 char **ap;
114
115 if (argc > 1) {
116 p = argv[1];
117 if (argc > 2) {
118 STARTSTACKSTR(concat);
119 ap = argv + 2;
120 for (;;) {
121 STPUTS(p, concat);
122 if ((p = *ap++) == NULL)
123 break;
124 STPUTC(' ', concat);
125 }
126 STPUTC('\0', concat);
127 p = grabstackstr(concat);
128 }
129 evalstring(p, builtin_flags);
130 } else
131 exitstatus = 0;
132 return exitstatus;
133}
134
135/*
136 * Execute a command or commands contained in a string.
137 */
138
139void
140evalstring(const char *s, int flags)
141{
142 union node *n;
143 struct stackmark smark;
144 int flags_exit;
145 int any;
146
147 flags_exit = flags & EV_EXIT;
148 flags &= ~EV_EXIT;
149 any = 0;
150 setstackmark(&smark);
151 setinputstring(s);
152 while ((n = parsecmd(0)) != NEOF) {
153 if (n != NULL && !nflag) {
154 if (flags_exit && preadateof())
155 evaltree(n, flags | EV_EXIT);
156 else
157 evaltree(n, flags);
158 any = 1;
159 if (evalskip)
160 break;
161 }
162 popstackmark(&smark);
163 setstackmark(&smark);
164 }
165 popfile();
166 popstackmark(&smark);
167 if (!any)
168 exitstatus = 0;
169 if (flags_exit)
170 exraise(EXEXIT);
171}
172
173/*
174 * Evaluate a parse tree. The value is left in the global variable
175 * exitstatus.
176 */
177
178void
179evaltree(union node *n, int flags)
180{
181 int do_etest;
182 union node *next;
183 struct stackmark smark;
184
185 setstackmark(&smark);
186 do_etest = 0;
187 if (n == NULL) {
188 TRACE(("evaltree(NULL) called\n"));
189 exitstatus = 0;
190 goto out;
191 }
192 do {
193 next = NULL;
194#ifndef NO_HISTORY
195 displayhist = 1; /* show history substitutions done with fc */
196#endif
197 TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
198 switch (n->type) {
199 case NSEMI:
200 evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
201 if (evalskip)
202 goto out;
203 next = n->nbinary.ch2;
204 break;
205 case NAND:
206 evaltree(n->nbinary.ch1, EV_TESTED);
207 if (evalskip || exitstatus != 0) {
208 goto out;
209 }
210 next = n->nbinary.ch2;
211 break;
212 case NOR:
213 evaltree(n->nbinary.ch1, EV_TESTED);
214 if (evalskip || exitstatus == 0)
215 goto out;
216 next = n->nbinary.ch2;
217 break;
218 case NREDIR:
219 evalredir(n, flags);
220 break;
221 case NSUBSHELL:
222 evalsubshell(n, flags);
223 do_etest = !(flags & EV_TESTED);
224 break;
225 case NBACKGND:
226 evalsubshell(n, flags);
227 break;
228 case NIF: {
229 evaltree(n->nif.test, EV_TESTED);
230 if (evalskip)
231 goto out;
232 if (exitstatus == 0)
233 next = n->nif.ifpart;
234 else if (n->nif.elsepart)
235 next = n->nif.elsepart;
236 else
237 exitstatus = 0;
238 break;
239 }
240 case NWHILE:
241 case NUNTIL:
242 evalloop(n, flags & ~EV_EXIT);
243 break;
244 case NFOR:
245 evalfor(n, flags & ~EV_EXIT);
246 break;
247 case NCASE:
248 next = evalcase(n);
249 break;
250 case NCLIST:
251 next = n->nclist.body;
252 break;
253 case NCLISTFALLTHRU:
254 if (n->nclist.body) {
255 evaltree(n->nclist.body, flags & ~EV_EXIT);
256 if (evalskip)
257 goto out;
258 }
259 next = n->nclist.next;
260 break;
261 case NDEFUN:
262 defun(n->narg.text, n->narg.next);
263 exitstatus = 0;
264 break;
265 case NNOT:
266 evaltree(n->nnot.com, EV_TESTED);
267 if (evalskip)
268 goto out;
269 exitstatus = !exitstatus;
270 break;
271
272 case NPIPE:
273 evalpipe(n);
274 do_etest = !(flags & EV_TESTED);
275 break;
276 case NCMD:
277 evalcommand(n, flags, (struct backcmd *)NULL);
278 do_etest = !(flags & EV_TESTED);
279 break;
280 default:
281 out1fmt("Node type = %d\n", n->type);
282 flushout(&output);
283 break;
284 }
285 n = next;
286 popstackmark(&smark);
287 setstackmark(&smark);
288 } while (n != NULL);
289out:
290 popstackmark(&smark);
291 if (pendingsig)
292 dotrap();
293 if (eflag && exitstatus != 0 && do_etest)
294 exitshell(exitstatus);
295 if (flags & EV_EXIT)
296 exraise(EXEXIT);
297}
298
299static void
300evalloop(union node *n, int flags)
301{
302 int status;
303
304 loopnest++;
305 status = 0;
306 for (;;) {
307 if (!evalskip)
308 evaltree(n->nbinary.ch1, EV_TESTED);
309 if (evalskip) {
310 if (evalskip == SKIPCONT && --skipcount <= 0) {
311 evalskip = 0;
312 continue;
313 }
314 if (evalskip == SKIPBREAK && --skipcount <= 0)
315 evalskip = 0;
316 if (evalskip == SKIPRETURN)
317 status = exitstatus;
318 break;
319 }
320 if (n->type == NWHILE) {
321 if (exitstatus != 0)
322 break;
323 } else {
324 if (exitstatus == 0)
325 break;
326 }
327 evaltree(n->nbinary.ch2, flags);
328 status = exitstatus;
329 }
330 loopnest--;
331 exitstatus = status;
332}
333
334static void
335evalfor(union node *n, int flags)
336{
337 struct arglist arglist;
338 union node *argp;
339 int i;
340 int status;
341
342 emptyarglist(&arglist);
343 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
344 oexitstatus = exitstatus;
345 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
346 }
347
348 loopnest++;
349 status = 0;
350 for (i = 0; i < arglist.count; i++) {
351 setvar(n->nfor.var, arglist.args[i], 0);
352 evaltree(n->nfor.body, flags);
353 status = exitstatus;
354 if (evalskip) {
355 if (evalskip == SKIPCONT && --skipcount <= 0) {
356 evalskip = 0;
357 continue;
358 }
359 if (evalskip == SKIPBREAK && --skipcount <= 0)
360 evalskip = 0;
361 break;
362 }
363 }
364 loopnest--;
365 exitstatus = status;
366}
367
368/*
369 * Evaluate a case statement, returning the selected tree.
370 *
371 * The exit status needs care to get right.
372 */
373
374static union node *
375evalcase(union node *n)
376{
377 union node *cp;
378 union node *patp;
379 struct arglist arglist;
380
381 emptyarglist(&arglist);
382 oexitstatus = exitstatus;
383 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
384 for (cp = n->ncase.cases; cp; cp = cp->nclist.next) {
385 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
386 if (casematch(patp, arglist.args[0])) {
387 while (cp->nclist.next && cp->type == NCLISTFALLTHRU && cp->nclist.body == NULL)
388 cp = cp->nclist.next;
389 if (cp->nclist.next && cp->type == NCLISTFALLTHRU)
390 return (cp);
391 if (cp->nclist.body == NULL)
392 exitstatus = 0;
393 return (cp->nclist.body);
394 }
395 }
396 }
397 exitstatus = 0;
398 return (NULL);
399}
400
401/*
402 * Kick off a subshell to evaluate a tree.
403 */
404
405static void
406evalsubshell(union node *n, int flags)
407{
408 struct job *jp;
409 int backgnd = (n->type == NBACKGND);
410
411 oexitstatus = exitstatus;
412 expredir(n->nredir.redirect);
413 if ((!backgnd && flags & EV_EXIT && !have_traps())
414 || forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
415 if (backgnd)
416 flags &= ~EV_TESTED;
417 redirect(n->nredir.redirect, 0);
418 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
419 } else if (!backgnd) {
420 INTOFF;
421 exitstatus = waitforjob(jp, (int *)NULL);
422 INTON;
423 } else
424 exitstatus = 0;
425}
426
427/*
428 * Evaluate a redirected compound command.
429 */
430
431static void
432evalredir(union node *n, int flags)
433{
434 struct jmploc jmploc;
435 struct jmploc *savehandler;
436 volatile int in_redirect = 1;
437
438 oexitstatus = exitstatus;
439 expredir(n->nredir.redirect);
440 savehandler = handler;
441 if (setjmp(jmploc.loc)) {
442 int e;
443
444 handler = savehandler;
445 e = exception;
446 popredir();
447 if (e == EXERROR && in_redirect) {
448 FORCEINTON;
449 return;
450 }
451 longjmp(handler->loc, 1);
452 } else {
453 INTOFF;
454 handler = &jmploc;
455 redirect(n->nredir.redirect, REDIR_PUSH);
456 in_redirect = 0;
457 INTON;
458 evaltree(n->nredir.n, flags);
459 }
460 INTOFF;
461 handler = savehandler;
462 popredir();
463 INTON;
464}
465
466static void
467exphere(union node *redir, struct arglist *fn)
468{
469 struct jmploc jmploc;
470 struct jmploc *savehandler;
471 struct localvar *savelocalvars;
472 int need_longjmp = 0;
473 unsigned char saveoptreset;
474
475 redir->nhere.expdoc = "";
476 savelocalvars = localvars;
477 localvars = NULL;
478 saveoptreset = shellparam.reset;
479 forcelocal++;
480 savehandler = handler;
481 if (setjmp(jmploc.loc))
482 need_longjmp = exception != EXERROR;
483 else {
484 handler = &jmploc;
485 expandarg(redir->nhere.doc, fn, 0);
486 redir->nhere.expdoc = fn->args[0];
487 INTOFF;
488 }
489 handler = savehandler;
490 forcelocal--;
491 poplocalvars();
492 localvars = savelocalvars;
493 shellparam.reset = saveoptreset;
494 if (need_longjmp)
495 longjmp(handler->loc, 1);
496 INTON;
497}
498
499/*
500 * Compute the names of the files in a redirection list.
501 */
502
503static void
504expredir(union node *n)
505{
506 union node *redir;
507
508 for (redir = n; redir; redir = redir->nfile.next) {
509 struct arglist fn;
510 emptyarglist(&fn);
511 switch (redir->type) {
512 case NFROM:
513 case NTO:
514 case NFROMTO:
515 case NAPPEND:
516 case NCLOBBER:
517 expandarg(redir->nfile.fname, &fn, EXP_TILDE);
518 redir->nfile.expfname = fn.args[0];
519 break;
520 case NFROMFD:
521 case NTOFD:
522 if (redir->ndup.vname) {
523 expandarg(redir->ndup.vname, &fn, EXP_TILDE);
524 fixredir(redir, fn.args[0], 1);
525 }
526 break;
527 case NXHERE:
528 exphere(redir, &fn);
529 break;
530 }
531 }
532}
533
534/*
535 * Evaluate a pipeline. All the processes in the pipeline are children
536 * of the process creating the pipeline. (This differs from some versions
537 * of the shell, which make the last process in a pipeline the parent
538 * of all the rest.)
539 */
540
541static void
542evalpipe(union node *n)
543{
544 struct job *jp;
545 struct nodelist *lp;
546 int pipelen;
547 int prevfd;
548 int pip[2];
549
550 TRACE(("evalpipe(%p) called\n", (void *)n));
551 pipelen = 0;
552 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
553 pipelen++;
554 INTOFF;
555 jp = makejob(n, pipelen);
556 prevfd = -1;
557 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
558 prehash(lp->n);
559 pip[1] = -1;
560 if (lp->next) {
561 if (pipe(pip) < 0) {
562 if (prevfd >= 0)
563 close(prevfd);
564 error("Pipe call failed: %s", strerror(errno));
565 }
566 }
567 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
568 INTON;
569 if (prevfd > 0) {
570 dup2(prevfd, 0);
571 close(prevfd);
572 }
573 if (pip[1] >= 0) {
574 if (!(prevfd >= 0 && pip[0] == 0))
575 close(pip[0]);
576 if (pip[1] != 1) {
577 dup2(pip[1], 1);
578 close(pip[1]);
579 }
580 }
581 evaltree(lp->n, EV_EXIT);
582 }
583 if (prevfd >= 0)
584 close(prevfd);
585 prevfd = pip[0];
586 if (pip[1] != -1)
587 close(pip[1]);
588 }
589 INTON;
590 if (n->npipe.backgnd == 0) {
591 INTOFF;
592 exitstatus = waitforjob(jp, (int *)NULL);
593 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
594 INTON;
595 } else
596 exitstatus = 0;
597}
598
599static int
600is_valid_fast_cmdsubst(union node *n)
601{
602 return (n->type == NCMD);
603}
604
605/*
606 * Execute a command inside back quotes. If it's a builtin command, we
607 * want to save its output in a block obtained from malloc. Otherwise
608 * we fork off a subprocess and get the output of the command via a pipe.
609 * Should be called with interrupts off.
610 */
611
612void
613evalbackcmd(union node *n, struct backcmd *result)
614{
615 int pip[2];
616 struct job *jp;
617 struct stackmark smark;
618 struct jmploc jmploc;
619 struct jmploc *savehandler;
620 struct localvar *savelocalvars;
621 unsigned char saveoptreset;
622
623 result->fd = -1;
624 result->buf = NULL;
625 result->nleft = 0;
626 result->jp = NULL;
627 if (n == NULL) {
628 exitstatus = 0;
629 return;
630 }
631 setstackmark(&smark);
632 exitstatus = oexitstatus;
633 if (is_valid_fast_cmdsubst(n)) {
634 savelocalvars = localvars;
635 localvars = NULL;
636 saveoptreset = shellparam.reset;
637 forcelocal++;
638 savehandler = handler;
639 if (setjmp(jmploc.loc)) {
640 if (exception == EXERROR)
641 /* nothing */;
642 else if (exception != 0) {
643 handler = savehandler;
644 forcelocal--;
645 poplocalvars();
646 localvars = savelocalvars;
647 shellparam.reset = saveoptreset;
648 longjmp(handler->loc, 1);
649 }
650 } else {
651 handler = &jmploc;
652 evalcommand(n, EV_BACKCMD, result);
653 }
654 handler = savehandler;
655 forcelocal--;
656 poplocalvars();
657 localvars = savelocalvars;
658 shellparam.reset = saveoptreset;
659 } else {
660 if (pipe(pip) < 0)
661 error("Pipe call failed: %s", strerror(errno));
662 jp = makejob(n, 1);
663 if (forkshell(jp, n, FORK_NOJOB) == 0) {
664 FORCEINTON;
665 close(pip[0]);
666 if (pip[1] != 1) {
667 dup2(pip[1], 1);
668 close(pip[1]);
669 }
670 evaltree(n, EV_EXIT);
671 }
672 close(pip[1]);
673 result->fd = pip[0];
674 result->jp = jp;
675 }
676 popstackmark(&smark);
677 TRACE(
678 ("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
679 result->fd,
680 result->buf,
681 result->nleft,
682 result->jp)
683 );
684}
685
686static int
687mustexpandto(const char *argtext, const char *mask)
688{
689 for (;;) {
690 if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
691 argtext++;
692 continue;
693 }
694 if (*argtext == CTLESC)
695 argtext++;
696 else if (BASESYNTAX[(int)*argtext] == CCTL)
697 return (0);
698 if (*argtext != *mask)
699 return (0);
700 if (*argtext == '\0')
701 return (1);
702 argtext++;
703 mask++;
704 }
705}
706
707static int
708isdeclarationcmd(struct narg *arg)
709{
710 int have_command = 0;
711
712 if (arg == NULL)
713 return (0);
714 while (mustexpandto(arg->text, "command")) {
715 have_command = 1;
716 arg = &arg->next->narg;
717 if (arg == NULL)
718 return (0);
719 /*
720 * To also allow "command -p" and "command --" as part of
721 * a declaration command, add code here.
722 * We do not do this, as ksh does not do it either and it
723 * is not required by POSIX.
724 */
725 }
726 return (
727 mustexpandto(arg->text, "export") || mustexpandto(arg->text, "readonly")
728 || (mustexpandto(arg->text, "local") && (have_command || !isfunc("local")))
729 );
730}
731
732static void
733xtracecommand(struct arglist *varlist, int argc, char **argv)
734{
735 char sep = 0;
736 const char *text, *p, *ps4;
737 int i;
738
739 ps4 = expandstr(ps4val());
740 out2str(ps4 != NULL ? ps4 : ps4val());
741 for (i = 0; i < varlist->count; i++) {
742 text = varlist->args[i];
743 if (sep != 0)
744 out2c(' ');
745 p = strchr(text, '=');
746 if (p != NULL) {
747 p++;
748 outbin(text, p - text, out2);
749 out2qstr(p);
750 } else
751 out2qstr(text);
752 sep = ' ';
753 }
754 for (i = 0; i < argc; i++) {
755 text = argv[i];
756 if (sep != 0)
757 out2c(' ');
758 out2qstr(text);
759 sep = ' ';
760 }
761 out2c('\n');
762 flushout(&errout);
763}
764
765/*
766 * Check if a builtin can safely be executed in the same process,
767 * even though it should be in a subshell (command substitution).
768 * Note that jobid, jobs, times and trap can show information not
769 * available in a child process; this is deliberate.
770 * The arguments should already have been expanded.
771 */
772static int
773safe_builtin(int idx, int argc, char **argv)
774{
775 /* Generated from builtins.def. */
776 if (safe_builtin_always(idx))
777 return (1);
778 if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || idx == UMASKCMD)
779 return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
780 if (idx == SETCMD)
781 return (
782 argc <= 1
783 || (argc == 2 && (argv[1][0] == '-' || argv[1][0] == '+') && argv[1][1] == 'o'
784 && argv[1][2] == '\0')
785 );
786 return (0);
787}
788
789/*
790 * Perform redirections, then execute a simple command with vfork.
791 * This cannot be used for command substitutions for two reasons:
792 * - Redirections might cause the error message for later redirections or for
793 * an unknown command to be sent to the pipe (to be substituted), and this
794 * might cause a deadlock if the message is too long.
795 * - The assignment of the pipe needs to come before instead of after the
796 * redirections.
797 */
798static int
799redirected_vforkexecshell(
800 struct job *jp, union node *redir, char **argv, char **envp, const char *path, int idx
801)
802{
803 struct jmploc jmploc;
804 struct jmploc *savehandler;
805 volatile int in_redirect = 1;
806
807 savehandler = handler;
808 if (setjmp(jmploc.loc)) {
809 int e;
810
811 handler = savehandler;
812 e = exception;
813 popredir();
814 if (e == EXERROR && in_redirect) {
815 FORCEINTON;
816 return 0;
817 }
818 longjmp(handler->loc, 1);
819 } else {
820 INTOFF;
821 handler = &jmploc;
822 redirect(redir, REDIR_PUSH);
823 in_redirect = 0;
824 INTON;
825 vforkexecshell(jp, argv, envp, path, idx, NULL);
826 }
827 INTOFF;
828 handler = savehandler;
829 popredir();
830 INTON;
831 return 1;
832}
833
834/*
835 * Execute a simple command.
836 * Note: This may or may not return if (flags & EV_EXIT).
837 */
838
839static void
840evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
841{
842 union node *argp;
843 struct arglist arglist;
844 struct arglist varlist;
845 char **argv;
846 int argc;
847 char **envp;
848 int varflag;
849 int mode;
850 int pip[2];
851 struct cmdentry cmdentry;
852 struct job *jp;
853 struct jmploc jmploc;
854 struct jmploc *savehandler;
855 char *savecmdname;
856 struct shparam saveparam;
857 struct localvar *savelocalvars;
858 struct parsefile *savetopfile;
859 volatile int e;
860 char *lastarg;
861 int signaled;
862 int do_clearcmdentry;
863 const char *path = pathval();
864 int i;
865
866 /* First expand the arguments. */
867 TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
868 emptyarglist(&arglist);
869 emptyarglist(&varlist);
870 varflag = 1;
871 jp = NULL;
872 do_clearcmdentry = 0;
873 oexitstatus = exitstatus;
874 exitstatus = 0;
875 /* Add one slot at the beginning for tryexec(). */
876 appendarglist(&arglist, nullstr);
877 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
878 if (varflag && isassignment(argp->narg.text)) {
879 expandarg(argp, varflag == 1 ? &varlist : &arglist, EXP_VARTILDE);
880 continue;
881 } else if (varflag == 1)
882 varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
883 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
884 }
885 appendarglist(&arglist, nullstr);
886 expredir(cmd->ncmd.redirect);
887 argc = arglist.count - 2;
888 argv = &arglist.args[1];
889
890 argv[argc] = NULL;
891 lastarg = NULL;
892 if (iflag && funcnest == 0 && argc > 0)
893 lastarg = argv[argc - 1];
894
895 /* Print the command if xflag is set. */
896 if (xflag)
897 xtracecommand(&varlist, argc, argv);
898
899 /* Now locate the command. */
900 if (argc == 0) {
901 /* Variable assignment(s) without command */
902 cmdentry.cmdtype = CMDBUILTIN;
903 cmdentry.u.index = BLTINCMD;
904 cmdentry.special = 0;
905 } else {
906 static const char PATH[] = "PATH=";
907 int cmd_flags = 0, bltinonly = 0;
908
909 /*
910 * Modify the command lookup path, if a PATH= assignment
911 * is present
912 */
913 for (i = 0; i < varlist.count; i++)
914 if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) {
915 path = varlist.args[i] + sizeof(PATH) - 1;
916 /*
917 * On `PATH=... command`, we need to make
918 * sure that the command isn't using the
919 * non-updated hash table of the outer PATH
920 * setting and we need to make sure that
921 * the hash table isn't filled with items
922 * from the temporary setting.
923 *
924 * It would be better to forbid using and
925 * updating the table while this command
926 * runs, by the command finding mechanism
927 * is heavily integrated with hash handling,
928 * so we just delete the hash before and after
929 * the command runs. Partly deleting like
930 * changepatch() does doesn't seem worth the
931 * booking effort, since most such runs add
932 * directories in front of the new PATH.
933 */
934 clearcmdentry();
935 do_clearcmdentry = 1;
936 }
937
938 for (;;) {
939 if (bltinonly) {
940 cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
941 if (cmdentry.u.index < 0) {
942 cmdentry.u.index = BLTINCMD;
943 argv--;
944 argc++;
945 break;
946 }
947 } else
948 find_command(argv[0], &cmdentry, cmd_flags, path);
949 /* implement the bltin and command builtins here */
950 if (cmdentry.cmdtype != CMDBUILTIN)
951 break;
952 if (cmdentry.u.index == BLTINCMD) {
953 if (argc == 1)
954 break;
955 argv++;
956 argc--;
957 bltinonly = 1;
958 } else if (cmdentry.u.index == COMMANDCMD) {
959 if (argc == 1)
960 break;
961 if (!strcmp(argv[1], "-p")) {
962 if (argc == 2)
963 break;
964 if (argv[2][0] == '-') {
965 if (strcmp(argv[2], "--"))
966 break;
967 if (argc == 3)
968 break;
969 argv += 3;
970 argc -= 3;
971 } else {
972 argv += 2;
973 argc -= 2;
974 }
975 path = ARUU_PATH_STDPATH;
976 clearcmdentry();
977 do_clearcmdentry = 1;
978 } else if (!strcmp(argv[1], "--")) {
979 if (argc == 2)
980 break;
981 argv += 2;
982 argc -= 2;
983 } else if (argv[1][0] == '-')
984 break;
985 else {
986 argv++;
987 argc--;
988 }
989 cmd_flags |= DO_NOFUNC;
990 bltinonly = 0;
991 } else
992 break;
993 }
994 /*
995 * Special builtins lose their special properties when
996 * called via 'command'.
997 */
998 if (cmd_flags & DO_NOFUNC)
999 cmdentry.special = 0;
1000 }
1001
1002 /* Fork off a child process if necessary. */
1003 if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN
1004 || (cmdentry.cmdtype == CMDWEXEC && !(wexec_get_nofork() && wexec_is_nofork(argv[0]))))
1005 && ((flags & EV_EXIT) == 0 || have_traps()))
1006 || ((flags & EV_BACKCMD) != 0
1007 && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) {
1008 jp = makejob(cmd, 1);
1009 mode = FORK_FG;
1010 if (flags & EV_BACKCMD) {
1011 mode = FORK_NOJOB;
1012 if (pipe(pip) < 0)
1013 error("Pipe call failed: %s", strerror(errno));
1014 }
1015 if (cmdentry.cmdtype == CMDNORMAL && (cmd->ncmd.redirect == NULL || (flags & EV_BACKCMD) == 0)
1016 && varlist.count == 0 && (mode == FORK_FG || mode == FORK_NOJOB) && !disvforkset() && !iflag
1017 && !mflag) {
1018 if (cmd->ncmd.redirect != NULL) {
1019 if (redirected_vforkexecshell(
1020 jp, cmd->ncmd.redirect, argv, environment(), path, cmdentry.u.index
1021 ))
1022 goto parent;
1023 else
1024 goto out;
1025 } else
1026 vforkexecshell(
1027 jp, argv, environment(), path, cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL
1028 );
1029 goto parent;
1030 }
1031 if (forkshell(jp, cmd, mode) != 0)
1032 goto parent; /* at end of routine */
1033 if (flags & EV_BACKCMD) {
1034 FORCEINTON;
1035 close(pip[0]);
1036 if (pip[1] != 1) {
1037 dup2(pip[1], 1);
1038 close(pip[1]);
1039 }
1040 flags &= ~EV_BACKCMD;
1041 }
1042 flags |= EV_EXIT;
1043 }
1044
1045 /* This is the child process if a fork occurred. */
1046 /* Execute the command. */
1047 if (cmdentry.cmdtype == CMDFUNCTION) {
1048#ifdef DEBUG
1049 trputs("Shell function: ");
1050 trargs(argv);
1051#endif
1052 saveparam = shellparam;
1053 shellparam.malloc = 0;
1054 shellparam.reset = 1;
1055 shellparam.nparam = argc - 1;
1056 shellparam.p = argv + 1;
1057 shellparam.optp = NULL;
1058 shellparam.optnext = NULL;
1059 INTOFF;
1060 savelocalvars = localvars;
1061 localvars = NULL;
1062 reffunc(cmdentry.u.func);
1063 savehandler = handler;
1064 if (setjmp(jmploc.loc)) {
1065 popredir();
1066 unreffunc(cmdentry.u.func);
1067 poplocalvars();
1068 localvars = savelocalvars;
1069 freeparam(&shellparam);
1070 shellparam = saveparam;
1071 funcnest--;
1072 handler = savehandler;
1073 longjmp(handler->loc, 1);
1074 }
1075 handler = &jmploc;
1076 funcnest++;
1077 redirect(cmd->ncmd.redirect, REDIR_PUSH);
1078 INTON;
1079 for (i = 0; i < varlist.count; i++)
1080 mklocal(varlist.args[i]);
1081 exitstatus = oexitstatus;
1082 evaltree(getfuncnode(cmdentry.u.func), flags & (EV_TESTED | EV_EXIT));
1083 INTOFF;
1084 unreffunc(cmdentry.u.func);
1085 poplocalvars();
1086 localvars = savelocalvars;
1087 freeparam(&shellparam);
1088 shellparam = saveparam;
1089 handler = savehandler;
1090 funcnest--;
1091 popredir();
1092 INTON;
1093 if (evalskip == SKIPRETURN) {
1094 evalskip = 0;
1095 skipcount = 0;
1096 }
1097 if (jp)
1098 exitshell(exitstatus);
1099 } else if (cmdentry.cmdtype == CMDWEXEC) {
1100 savecmdname = commandname;
1101 savetopfile = getcurrentfile();
1102 cmdenviron = &varlist;
1103 e = -1;
1104 savehandler = handler;
1105 if (setjmp(jmploc.loc)) {
1106 e = exception;
1107 if (e == EXINT)
1108 exitstatus = SIGINT + 128;
1109 goto cmddone_wexec;
1110 }
1111 handler = &jmploc;
1112 mode = REDIR_PUSH;
1113 if (flags == EV_BACKCMD) {
1114 memout.nextc = memout.buf;
1115 mode |= REDIR_BACKQ;
1116 }
1117 redirect(cmd->ncmd.redirect, mode);
1118 outclearerror(out1);
1119 listsetvar(cmdenviron, VNOSET);
1120 commandname = argv[0];
1121 exitstatus = wexecvp(argv[0], argv);
1122 flushall();
1123 if (outiserror(out1)) {
1124 warning("write error on stdout");
1125 if (exitstatus == 0 || exitstatus == 1)
1126 exitstatus = 2;
1127 }
1128 cmddone_wexec:
1129 cmdenviron = NULL;
1130 out1 = &output;
1131 out2 = &errout;
1132 freestdout();
1133 handler = savehandler;
1134 commandname = savecmdname;
1135 if (jp)
1136 exitshell(exitstatus);
1137 if (flags == EV_BACKCMD) {
1138 backcmd->buf = memout.buf;
1139 backcmd->nleft = memout.buf != NULL ? memout.nextc - memout.buf : 0;
1140 memout.buf = NULL;
1141 memout.nextc = NULL;
1142 memout.bufend = NULL;
1143 memout.bufsize = 64;
1144 }
1145 } else if (cmdentry.cmdtype == CMDBUILTIN) {
1146#ifdef DEBUG
1147 trputs("builtin command: ");
1148 trargs(argv);
1149#endif
1150 mode = (cmdentry.u.index == EXECCMD) ? 0 : REDIR_PUSH;
1151 if (flags == EV_BACKCMD) {
1152 memout.nextc = memout.buf;
1153 mode |= REDIR_BACKQ;
1154 }
1155 savecmdname = commandname;
1156 savetopfile = getcurrentfile();
1157 cmdenviron = &varlist;
1158 e = -1;
1159 savehandler = handler;
1160 if (setjmp(jmploc.loc)) {
1161 e = exception;
1162 if (e == EXINT)
1163 exitstatus = SIGINT + 128;
1164 goto cmddone;
1165 }
1166 handler = &jmploc;
1167 redirect(cmd->ncmd.redirect, mode);
1168 outclearerror(out1);
1169 /*
1170 * If there is no command word, redirection errors should
1171 * not be fatal but assignment errors should.
1172 */
1173 if (argc == 0)
1174 cmdentry.special = 1;
1175 listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1176 if (argc > 0)
1177 bltinsetlocale();
1178 commandname = argv[0];
1179 argptr = argv + 1;
1180 nextopt_optptr = NULL; /* initialize nextopt */
1181 builtin_flags = flags;
1182 exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
1183 flushall();
1184 if (outiserror(out1)) {
1185 warning("write error on stdout");
1186 if (exitstatus == 0 || exitstatus == 1)
1187 exitstatus = 2;
1188 }
1189 cmddone:
1190 if (argc > 0)
1191 bltinunsetlocale();
1192 cmdenviron = NULL;
1193 out1 = &output;
1194 out2 = &errout;
1195 freestdout();
1196 handler = savehandler;
1197 commandname = savecmdname;
1198 if (jp)
1199 exitshell(exitstatus);
1200 if (flags == EV_BACKCMD) {
1201 backcmd->buf = memout.buf;
1202 backcmd->nleft = memout.buf != NULL ? memout.nextc - memout.buf : 0;
1203 memout.buf = NULL;
1204 memout.nextc = NULL;
1205 memout.bufend = NULL;
1206 memout.bufsize = 64;
1207 }
1208 if (cmdentry.u.index != EXECCMD)
1209 popredir();
1210 if (e != -1) {
1211 if (e != EXERROR || cmdentry.special)
1212 exraise(e);
1213 popfilesupto(savetopfile);
1214 if (flags != EV_BACKCMD)
1215 FORCEINTON;
1216 }
1217 } else {
1218#ifdef DEBUG
1219 trputs("normal command: ");
1220 trargs(argv);
1221#endif
1222 redirect(cmd->ncmd.redirect, 0);
1223 for (i = 0; i < varlist.count; i++)
1224 setvareq(varlist.args[i], VEXPORT | VSTACK);
1225 envp = environment();
1226 shellexec(argv, envp, path, cmdentry.u.index);
1227 /*NOTREACHED*/
1228 }
1229 goto out;
1230
1231parent: /* parent process gets here (if we forked) */
1232 if (mode == FORK_FG) { /* argument to fork */
1233 INTOFF;
1234 exitstatus = waitforjob(jp, &signaled);
1235 INTON;
1236 if (iflag && loopnest > 0 && signaled) {
1237 evalskip = SKIPBREAK;
1238 skipcount = loopnest;
1239 }
1240 } else if (mode == FORK_NOJOB) {
1241 backcmd->fd = pip[0];
1242 close(pip[1]);
1243 backcmd->jp = jp;
1244 }
1245
1246out:
1247 if (lastarg)
1248 setvar("_", lastarg, 0);
1249 if (do_clearcmdentry)
1250 clearcmdentry();
1251}
1252
1253/*
1254 * Search for a command. This is called before we fork so that the
1255 * location of the command will be available in the parent as well as
1256 * the child. The check for "goodname" is an overly conservative
1257 * check that the name will not be subject to expansion.
1258 */
1259
1260static void
1261prehash(union node *n)
1262{
1263 struct cmdentry entry;
1264
1265 if (n && n->type == NCMD && n->ncmd.args)
1266 if (goodname(n->ncmd.args->narg.text))
1267 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
1268}
1269
1270/*
1271 * Builtin commands. Builtin commands whose functions are closely
1272 * tied to evaluation are implemented here.
1273 */
1274
1275/*
1276 * No command given, a bltin command with no arguments, or a bltin command
1277 * with an invalid name.
1278 */
1279
1280int
1281bltincmd(int argc, char **argv)
1282{
1283 if (argc > 1) {
1284 out2fmt_flush("%s: not found\n", argv[1]);
1285 return 127;
1286 }
1287 /*
1288 * Preserve exitstatus of a previous possible command substitution
1289 * as POSIX mandates
1290 */
1291 return exitstatus;
1292}
1293
1294/*
1295 * Handle break and continue commands. Break, continue, and return are
1296 * all handled by setting the evalskip flag. The evaluation routines
1297 * above all check this flag, and if it is set they start skipping
1298 * commands rather than executing them. The variable skipcount is
1299 * the number of loops to break/continue, or the number of function
1300 * levels to return. (The latter is always 1.) It should probably
1301 * be an error to break out of more loops than exist, but it isn't
1302 * in the standard shell so we don't make it one here.
1303 */
1304
1305int
1306breakcmd(int argc, char **argv)
1307{
1308 long n;
1309 char *end;
1310
1311 if (argc > 1) {
1312 /* Allow arbitrarily large numbers. */
1313 n = strtol(argv[1], &end, 10);
1314 if (!is_digit(argv[1][0]) || *end != '\0')
1315 error("Illegal number: %s", argv[1]);
1316 } else
1317 n = 1;
1318 if (n > loopnest)
1319 n = loopnest;
1320 if (n > 0) {
1321 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
1322 skipcount = n;
1323 }
1324 return 0;
1325}
1326
1327/*
1328 * The `command' command.
1329 */
1330int
1331commandcmd(int argc __unused, char **argv __unused)
1332{
1333 const char *path;
1334 int ch;
1335 int cmd = -1;
1336
1337 path = bltinlookup("PATH", 1);
1338
1339 while ((ch = nextopt("pvV")) != '\0') {
1340 switch (ch) {
1341 case 'p':
1342 path = ARUU_PATH_STDPATH;
1343 break;
1344 case 'v':
1345 cmd = TYPECMD_SMALLV;
1346 break;
1347 case 'V':
1348 cmd = TYPECMD_BIGV;
1349 break;
1350 }
1351 }
1352
1353 if (cmd != -1) {
1354 if (*argptr == NULL || argptr[1] != NULL)
1355 error("wrong number of arguments");
1356 return typecmd_impl(2, argptr - 1, cmd, path);
1357 }
1358 if (*argptr != NULL)
1359 error("commandcmd bad call");
1360
1361 /*
1362 * Do nothing successfully if no command was specified;
1363 * ksh also does this.
1364 */
1365 return 0;
1366}
1367
1368/*
1369 * The return command.
1370 */
1371
1372int
1373returncmd(int argc, char **argv)
1374{
1375 int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1376
1377 evalskip = SKIPRETURN;
1378 skipcount = 1;
1379 return ret;
1380}
1381
1382int
1383falsecmd(int argc __unused, char **argv __unused)
1384{
1385 return 1;
1386}
1387
1388int
1389truecmd(int argc __unused, char **argv __unused)
1390{
1391 return 0;
1392}
1393
1394int
1395execcmd(int argc, char **argv)
1396{
1397 int i;
1398
1399 /*
1400 * Because we have historically not supported any options,
1401 * only treat "--" specially.
1402 */
1403 if (argc > 1 && strcmp(argv[1], "--") == 0)
1404 argc--, argv++;
1405 if (argc > 1) {
1406 iflag = 0; /* exit on error */
1407 mflag = 0;
1408 optschanged();
1409 for (i = 0; i < cmdenviron->count; i++)
1410 setvareq(cmdenviron->args[i], VEXPORT | VSTACK);
1411 shellexec(argv + 1, environment(), pathval(), 0);
1412 }
1413 return 0;
1414}
1415
1416int
1417timescmd(int argc __unused, char **argv __unused)
1418{
1419 struct rusage ru;
1420 long shumins, shsmins, chumins, chsmins;
1421 double shusecs, shssecs, chusecs, chssecs;
1422
1423 if (getrusage(RUSAGE_SELF, &ru) < 0)
1424 return 1;
1425 shumins = ru.ru_utime.tv_sec / 60;
1426 shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1427 shsmins = ru.ru_stime.tv_sec / 60;
1428 shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1429 if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1430 return 1;
1431 chumins = ru.ru_utime.tv_sec / 60;
1432 chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1433 chsmins = ru.ru_stime.tv_sec / 60;
1434 chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1435 out1fmt(
1436 "%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n",
1437 shumins,
1438 shusecs,
1439 shsmins,
1440 shssecs,
1441 chumins,
1442 chusecs,
1443 chsmins,
1444 chssecs
1445 );
1446 return 0;
1447}