eval.c revision 1.71 1 /* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal 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. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
43 #else
44 __RCSID("$NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $");
45 #endif
46 #endif /* not lint */
47
48 #include <stdlib.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <unistd.h>
52 #include <sys/times.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/wait.h>
56 #include <sys/sysctl.h>
57
58 /*
59 * Evaluate a command.
60 */
61
62 #include "shell.h"
63 #include "nodes.h"
64 #include "syntax.h"
65 #include "expand.h"
66 #include "parser.h"
67 #include "jobs.h"
68 #include "eval.h"
69 #include "builtins.h"
70 #include "options.h"
71 #include "exec.h"
72 #include "redir.h"
73 #include "input.h"
74 #include "output.h"
75 #include "trap.h"
76 #include "var.h"
77 #include "memalloc.h"
78 #include "error.h"
79 #include "show.h"
80 #include "mystring.h"
81 #include "main.h"
82 #ifndef SMALL
83 #include "myhistedit.h"
84 #endif
85
86
87 /* flags in argument to evaltree */
88 #define EV_EXIT 01 /* exit after evaluating tree */
89 #define EV_TESTED 02 /* exit status is checked; ignore -e flag */
90 #define EV_BACKCMD 04 /* command executing within back quotes */
91
92 int evalskip; /* set if we are skipping commands */
93 STATIC int skipcount; /* number of levels to skip */
94 MKINIT int loopnest; /* current loop nesting level */
95 int funcnest; /* depth of function calls */
96
97
98 char *commandname;
99 struct strlist *cmdenviron;
100 int exitstatus; /* exit status of last command */
101 int back_exitstatus; /* exit status of backquoted command */
102
103
104 STATIC void evalloop(union node *, int);
105 STATIC void evalfor(union node *, int);
106 STATIC void evalcase(union node *, int);
107 STATIC void evalsubshell(union node *, int);
108 STATIC void expredir(union node *);
109 STATIC void evalpipe(union node *);
110 STATIC void evalcommand(union node *, int, struct backcmd *);
111 STATIC void prehash(union node *);
112
113
114 /*
115 * Called to reset things after an exception.
116 */
117
118 #ifdef mkinit
119 INCLUDE "eval.h"
120
121 RESET {
122 evalskip = 0;
123 loopnest = 0;
124 funcnest = 0;
125 }
126
127 SHELLPROC {
128 exitstatus = 0;
129 }
130 #endif
131
132
133
134 /*
135 * The eval commmand.
136 */
137
138 int
139 evalcmd(int argc, char **argv)
140 {
141 char *p;
142 char *concat;
143 char **ap;
144
145 if (argc > 1) {
146 p = argv[1];
147 if (argc > 2) {
148 STARTSTACKSTR(concat);
149 ap = argv + 2;
150 for (;;) {
151 while (*p)
152 STPUTC(*p++, concat);
153 if ((p = *ap++) == NULL)
154 break;
155 STPUTC(' ', concat);
156 }
157 STPUTC('\0', concat);
158 p = grabstackstr(concat);
159 }
160 evalstring(p, EV_TESTED);
161 }
162 return exitstatus;
163 }
164
165
166 /*
167 * Execute a command or commands contained in a string.
168 */
169
170 void
171 evalstring(char *s, int flag)
172 {
173 union node *n;
174 struct stackmark smark;
175
176 setstackmark(&smark);
177 setinputstring(s, 1);
178
179 while ((n = parsecmd(0)) != NEOF) {
180 evaltree(n, flag);
181 popstackmark(&smark);
182 }
183 popfile();
184 popstackmark(&smark);
185 }
186
187
188
189 /*
190 * Evaluate a parse tree. The value is left in the global variable
191 * exitstatus.
192 */
193
194 void
195 evaltree(union node *n, int flags)
196 {
197 if (n == NULL) {
198 TRACE(("evaltree(NULL) called\n"));
199 exitstatus = 0;
200 goto out;
201 }
202 #ifndef SMALL
203 displayhist = 1; /* show history substitutions done with fc */
204 #endif
205 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
206 getpid(), n, n->type, flags));
207 switch (n->type) {
208 case NSEMI:
209 evaltree(n->nbinary.ch1, flags & EV_TESTED);
210 if (evalskip)
211 goto out;
212 evaltree(n->nbinary.ch2, flags);
213 break;
214 case NAND:
215 evaltree(n->nbinary.ch1, EV_TESTED);
216 if (evalskip || exitstatus != 0)
217 goto out;
218 evaltree(n->nbinary.ch2, flags | EV_TESTED);
219 break;
220 case NOR:
221 evaltree(n->nbinary.ch1, EV_TESTED);
222 if (evalskip || exitstatus == 0)
223 goto out;
224 evaltree(n->nbinary.ch2, flags | EV_TESTED);
225 break;
226 case NREDIR:
227 expredir(n->nredir.redirect);
228 redirect(n->nredir.redirect, REDIR_PUSH);
229 evaltree(n->nredir.n, flags);
230 popredir();
231 break;
232 case NSUBSHELL:
233 evalsubshell(n, flags);
234 break;
235 case NBACKGND:
236 evalsubshell(n, flags);
237 break;
238 case NIF: {
239 evaltree(n->nif.test, EV_TESTED);
240 if (evalskip)
241 goto out;
242 if (exitstatus == 0)
243 evaltree(n->nif.ifpart, flags);
244 else if (n->nif.elsepart)
245 evaltree(n->nif.elsepart, flags);
246 else
247 exitstatus = 0;
248 break;
249 }
250 case NWHILE:
251 case NUNTIL:
252 evalloop(n, flags);
253 break;
254 case NFOR:
255 evalfor(n, flags);
256 break;
257 case NCASE:
258 evalcase(n, flags);
259 break;
260 case NDEFUN:
261 defun(n->narg.text, n->narg.next);
262 exitstatus = 0;
263 break;
264 case NNOT:
265 evaltree(n->nnot.com, EV_TESTED);
266 exitstatus = !exitstatus;
267 break;
268 case NPIPE:
269 evalpipe(n);
270 break;
271 case NCMD:
272 evalcommand(n, flags, (struct backcmd *)NULL);
273 break;
274 default:
275 out1fmt("Node type = %d\n", n->type);
276 flushout(&output);
277 break;
278 }
279 out:
280 if (pendingsigs)
281 dotrap();
282 if ((flags & EV_EXIT) != 0)
283 exitshell(exitstatus);
284 }
285
286
287 STATIC void
288 evalloop(union node *n, int flags)
289 {
290 int status;
291
292 loopnest++;
293 status = 0;
294 for (;;) {
295 evaltree(n->nbinary.ch1, EV_TESTED);
296 if (evalskip) {
297 skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
298 evalskip = 0;
299 continue;
300 }
301 if (evalskip == SKIPBREAK && --skipcount <= 0)
302 evalskip = 0;
303 break;
304 }
305 if (n->type == NWHILE) {
306 if (exitstatus != 0)
307 break;
308 } else {
309 if (exitstatus == 0)
310 break;
311 }
312 evaltree(n->nbinary.ch2, flags & EV_TESTED);
313 status = exitstatus;
314 if (evalskip)
315 goto skipping;
316 }
317 loopnest--;
318 exitstatus = status;
319 }
320
321
322
323 STATIC void
324 evalfor(union node *n, int flags)
325 {
326 struct arglist arglist;
327 union node *argp;
328 struct strlist *sp;
329 struct stackmark smark;
330 int status = 0;
331
332 setstackmark(&smark);
333 arglist.lastp = &arglist.list;
334 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
335 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
336 if (evalskip)
337 goto out;
338 }
339 *arglist.lastp = NULL;
340
341 loopnest++;
342 for (sp = arglist.list ; sp ; sp = sp->next) {
343 setvar(n->nfor.var, sp->text, 0);
344 evaltree(n->nfor.body, flags & EV_TESTED);
345 status = exitstatus;
346 if (evalskip) {
347 if (evalskip == SKIPCONT && --skipcount <= 0) {
348 evalskip = 0;
349 continue;
350 }
351 if (evalskip == SKIPBREAK && --skipcount <= 0)
352 evalskip = 0;
353 break;
354 }
355 }
356 loopnest--;
357 exitstatus = status;
358 out:
359 popstackmark(&smark);
360 }
361
362
363
364 STATIC void
365 evalcase(union node *n, int flags)
366 {
367 union node *cp;
368 union node *patp;
369 struct arglist arglist;
370 struct stackmark smark;
371 int status = 0;
372
373 setstackmark(&smark);
374 arglist.lastp = &arglist.list;
375 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
376 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
377 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
378 if (casematch(patp, arglist.list->text)) {
379 if (evalskip == 0) {
380 evaltree(cp->nclist.body, flags);
381 status = exitstatus;
382 }
383 goto out;
384 }
385 }
386 }
387 out:
388 exitstatus = status;
389 popstackmark(&smark);
390 }
391
392
393
394 /*
395 * Kick off a subshell to evaluate a tree.
396 */
397
398 STATIC void
399 evalsubshell(union node *n, int flags)
400 {
401 struct job *jp;
402 int backgnd = (n->type == NBACKGND);
403
404 expredir(n->nredir.redirect);
405 INTOFF;
406 jp = makejob(n, 1);
407 if (forkshell(jp, n, backgnd) == 0) {
408 INTON;
409 if (backgnd)
410 flags &=~ EV_TESTED;
411 redirect(n->nredir.redirect, 0);
412 /* never returns */
413 evaltree(n->nredir.n, flags | EV_EXIT);
414 }
415 if (! backgnd)
416 exitstatus = waitforjob(jp);
417 INTON;
418 }
419
420
421
422 /*
423 * Compute the names of the files in a redirection list.
424 */
425
426 STATIC void
427 expredir(union node *n)
428 {
429 union node *redir;
430
431 for (redir = n ; redir ; redir = redir->nfile.next) {
432 struct arglist fn;
433 fn.lastp = &fn.list;
434 switch (redir->type) {
435 case NFROMTO:
436 case NFROM:
437 case NTO:
438 case NCLOBBER:
439 case NAPPEND:
440 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
441 redir->nfile.expfname = fn.list->text;
442 break;
443 case NFROMFD:
444 case NTOFD:
445 if (redir->ndup.vname) {
446 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
447 fixredir(redir, fn.list->text, 1);
448 }
449 break;
450 }
451 }
452 }
453
454
455
456 /*
457 * Evaluate a pipeline. All the processes in the pipeline are children
458 * of the process creating the pipeline. (This differs from some versions
459 * of the shell, which make the last process in a pipeline the parent
460 * of all the rest.)
461 */
462
463 STATIC void
464 evalpipe(union node *n)
465 {
466 struct job *jp;
467 struct nodelist *lp;
468 int pipelen;
469 int prevfd;
470 int pip[2];
471
472 TRACE(("evalpipe(0x%lx) called\n", (long)n));
473 pipelen = 0;
474 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
475 pipelen++;
476 INTOFF;
477 jp = makejob(n, pipelen);
478 prevfd = -1;
479 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
480 prehash(lp->n);
481 pip[1] = -1;
482 if (lp->next) {
483 if (pipe(pip) < 0) {
484 close(prevfd);
485 error("Pipe call failed");
486 }
487 }
488 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
489 INTON;
490 if (prevfd > 0) {
491 close(0);
492 copyfd(prevfd, 0);
493 close(prevfd);
494 }
495 if (pip[1] >= 0) {
496 close(pip[0]);
497 if (pip[1] != 1) {
498 close(1);
499 copyfd(pip[1], 1);
500 close(pip[1]);
501 }
502 }
503 evaltree(lp->n, EV_EXIT);
504 }
505 if (prevfd >= 0)
506 close(prevfd);
507 prevfd = pip[0];
508 close(pip[1]);
509 }
510 if (n->npipe.backgnd == 0) {
511 exitstatus = waitforjob(jp);
512 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
513 }
514 INTON;
515 }
516
517
518
519 /*
520 * Execute a command inside back quotes. If it's a builtin command, we
521 * want to save its output in a block obtained from malloc. Otherwise
522 * we fork off a subprocess and get the output of the command via a pipe.
523 * Should be called with interrupts off.
524 */
525
526 void
527 evalbackcmd(union node *n, struct backcmd *result)
528 {
529 int pip[2];
530 struct job *jp;
531 struct stackmark smark; /* unnecessary */
532
533 setstackmark(&smark);
534 result->fd = -1;
535 result->buf = NULL;
536 result->nleft = 0;
537 result->jp = NULL;
538 if (n == NULL) {
539 goto out;
540 }
541 #ifdef notyet
542 /*
543 * For now we disable executing builtins in the same
544 * context as the shell, because we are not keeping
545 * enough state to recover from changes that are
546 * supposed only to affect subshells. eg. echo "`cd /`"
547 */
548 if (n->type == NCMD) {
549 exitstatus = oexitstatus;
550 evalcommand(n, EV_BACKCMD, result);
551 } else
552 #endif
553 {
554 INTOFF;
555 if (pipe(pip) < 0)
556 error("Pipe call failed");
557 jp = makejob(n, 1);
558 if (forkshell(jp, n, FORK_NOJOB) == 0) {
559 FORCEINTON;
560 close(pip[0]);
561 if (pip[1] != 1) {
562 close(1);
563 copyfd(pip[1], 1);
564 close(pip[1]);
565 }
566 eflag = 0;
567 evaltree(n, EV_EXIT);
568 /* NOTREACHED */
569 }
570 close(pip[1]);
571 result->fd = pip[0];
572 result->jp = jp;
573 INTON;
574 }
575 out:
576 popstackmark(&smark);
577 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
578 result->fd, result->buf, result->nleft, result->jp));
579 }
580
581 static const char *
582 syspath(void)
583 {
584 static char *sys_path = NULL;
585 static int mib[] = {CTL_USER, USER_CS_PATH};
586 int len;
587
588 if (sys_path == NULL) {
589 if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
590 (sys_path = ckmalloc(len + 5)) != NULL &&
591 sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
592 memcpy(sys_path, "PATH=", 5);
593 } else {
594 ckfree(sys_path);
595 /* something to keep things happy */
596 sys_path = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
597 }
598 }
599 return sys_path;
600 }
601
602 static int
603 parse_command_args(int argc, char **argv, int *use_syspath)
604 {
605 int sv_argc = argc;
606 char *cp, c;
607
608 *use_syspath = 0;
609
610 for (;;) {
611 argv++;
612 if (--argc == 0)
613 break;
614 cp = *argv;
615 if (*cp++ != '-')
616 break;
617 if (*cp == '-' && cp[1] == 0) {
618 argv++;
619 argc--;
620 break;
621 }
622 while ((c = *cp++)) {
623 switch (c) {
624 case 'p':
625 *use_syspath = 1;
626 break;
627 default:
628 /* run 'typecmd' for other options */
629 return 0;
630 }
631 }
632 }
633 return sv_argc - argc;
634 }
635
636 int vforked = 0;
637
638 /*
639 * Execute a simple command.
640 */
641
642 STATIC void
643 evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
644 {
645 struct stackmark smark;
646 union node *argp;
647 struct arglist arglist;
648 struct arglist varlist;
649 char **argv;
650 int argc;
651 char **envp;
652 int varflag;
653 struct strlist *sp;
654 int mode;
655 int pip[2];
656 struct cmdentry cmdentry;
657 struct job *jp;
658 struct jmploc jmploc;
659 struct jmploc *volatile savehandler;
660 char *volatile savecmdname;
661 volatile struct shparam saveparam;
662 struct localvar *volatile savelocalvars;
663 volatile int e;
664 char *lastarg;
665 const char *path = pathval();
666 int temp_path;
667 #if __GNUC__
668 /* Avoid longjmp clobbering */
669 (void) &argv;
670 (void) &argc;
671 (void) &lastarg;
672 (void) &flags;
673 #endif
674
675 vforked = 0;
676 /* First expand the arguments. */
677 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
678 setstackmark(&smark);
679 back_exitstatus = 0;
680
681 arglist.lastp = &arglist.list;
682 varflag = 1;
683 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
684 char *p = argp->narg.text;
685 if (varflag && is_name(*p)) {
686 do {
687 p++;
688 } while (is_in_name(*p));
689 if (*p == '=')
690 continue;
691 }
692 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
693 varflag = 0;
694 }
695 *arglist.lastp = NULL;
696
697 expredir(cmd->ncmd.redirect);
698
699 varlist.lastp = &varlist.list;
700 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
701 char *p = argp->narg.text;
702 if (!is_name(*p))
703 break;
704 do
705 p++;
706 while (is_in_name(*p));
707 if (*p != '=')
708 break;
709 expandarg(argp, &varlist, EXP_VARTILDE);
710 }
711 *varlist.lastp = NULL;
712
713 argc = 0;
714 for (sp = arglist.list ; sp ; sp = sp->next)
715 argc++;
716 argv = stalloc(sizeof (char *) * (argc + 1));
717
718 for (sp = arglist.list ; sp ; sp = sp->next) {
719 TRACE(("evalcommand arg: %s\n", sp->text));
720 *argv++ = sp->text;
721 }
722 *argv = NULL;
723 lastarg = NULL;
724 if (iflag && funcnest == 0 && argc > 0)
725 lastarg = argv[-1];
726 argv -= argc;
727
728 /* Print the command if xflag is set. */
729 if (xflag) {
730 char sep = 0;
731 out2str(ps4val());
732 for (sp = varlist.list ; sp ; sp = sp->next) {
733 if (sep != 0)
734 outc(sep, &errout);
735 out2str(sp->text);
736 sep = ' ';
737 }
738 for (sp = arglist.list ; sp ; sp = sp->next) {
739 if (sep != 0)
740 outc(sep, &errout);
741 out2str(sp->text);
742 sep = ' ';
743 }
744 outc('\n', &errout);
745 flushout(&errout);
746 }
747
748 /* Now locate the command. */
749 if (argc == 0) {
750 cmdentry.cmdtype = CMDSPLBLTIN;
751 cmdentry.u.bltin = bltincmd;
752 } else {
753 static const char PATH[] = "PATH=";
754 int cmd_flags = DO_ERR;
755
756 /*
757 * Modify the command lookup path, if a PATH= assignment
758 * is present
759 */
760 for (sp = varlist.list; sp; sp = sp->next)
761 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
762 path = sp->text + sizeof(PATH) - 1;
763
764 do {
765 int argsused, use_syspath;
766 find_command(argv[0], &cmdentry, cmd_flags, path);
767 if (cmdentry.cmdtype == CMDUNKNOWN) {
768 exitstatus = 127;
769 flushout(&errout);
770 goto out;
771 }
772
773 /* implement the 'command' builtin here */
774 if (cmdentry.cmdtype != CMDBUILTIN ||
775 cmdentry.u.bltin != bltincmd)
776 break;
777 cmd_flags |= DO_NOFUNC;
778 argsused = parse_command_args(argc, argv, &use_syspath);
779 if (argsused == 0) {
780 /* use 'type' builting to display info */
781 cmdentry.u.bltin = typecmd;
782 break;
783 }
784 argc -= argsused;
785 argv += argsused;
786 if (use_syspath)
787 path = syspath() + 5;
788 } while (argc != 0);
789 if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
790 /* posix mandates that 'command <splbltin>' act as if
791 <splbltin> was a normal builtin */
792 cmdentry.cmdtype = CMDBUILTIN;
793 }
794
795 /* Fork off a child process if necessary. */
796 if (cmd->ncmd.backgnd
797 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
798 || ((flags & EV_BACKCMD) != 0
799 && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
800 || cmdentry.u.bltin == dotcmd
801 || cmdentry.u.bltin == evalcmd))) {
802 INTOFF;
803 jp = makejob(cmd, 1);
804 mode = cmd->ncmd.backgnd;
805 if (flags & EV_BACKCMD) {
806 mode = FORK_NOJOB;
807 if (pipe(pip) < 0)
808 error("Pipe call failed");
809 }
810 #ifdef DO_SHAREDVFORK
811 /* It is essential that if DO_SHAREDVFORK is defined that the
812 * child's address space is actually shared with the parent as
813 * we rely on this.
814 */
815 if (cmdentry.cmdtype == CMDNORMAL) {
816 pid_t pid;
817
818 savelocalvars = localvars;
819 localvars = NULL;
820 vforked = 1;
821 switch (pid = vfork()) {
822 case -1:
823 TRACE(("Vfork failed, errno=%d", errno));
824 INTON;
825 error("Cannot vfork");
826 break;
827 case 0:
828 /* Make sure that exceptions only unwind to
829 * after the vfork(2)
830 */
831 if (setjmp(jmploc.loc)) {
832 if (exception == EXSHELLPROC) {
833 /* We can't progress with the vfork,
834 * so, set vforked = 2 so the parent
835 * knows, and _exit();
836 */
837 vforked = 2;
838 _exit(0);
839 } else {
840 _exit(exerrno);
841 }
842 }
843 savehandler = handler;
844 handler = &jmploc;
845 listmklocal(varlist.list, VEXPORT | VNOFUNC);
846 forkchild(jp, cmd, mode, vforked);
847 break;
848 default:
849 handler = savehandler; /* restore from vfork(2) */
850 poplocalvars();
851 localvars = savelocalvars;
852 if (vforked == 2) {
853 vforked = 0;
854
855 (void)waitpid(pid, NULL, 0);
856 /* We need to progress in a normal fork fashion */
857 goto normal_fork;
858 }
859 vforked = 0;
860 forkparent(jp, cmd, mode, pid);
861 goto parent;
862 }
863 } else {
864 normal_fork:
865 #endif
866 if (forkshell(jp, cmd, mode) != 0)
867 goto parent; /* at end of routine */
868 FORCEINTON;
869 #ifdef DO_SHAREDVFORK
870 }
871 #endif
872 if (flags & EV_BACKCMD) {
873 if (!vforked) {
874 FORCEINTON;
875 }
876 close(pip[0]);
877 if (pip[1] != 1) {
878 close(1);
879 copyfd(pip[1], 1);
880 close(pip[1]);
881 }
882 }
883 flags |= EV_EXIT;
884 }
885
886 /* This is the child process if a fork occurred. */
887 /* Execute the command. */
888 switch (cmdentry.cmdtype) {
889 case CMDFUNCTION:
890 #ifdef DEBUG
891 trputs("Shell function: "); trargs(argv);
892 #endif
893 redirect(cmd->ncmd.redirect, REDIR_PUSH);
894 saveparam = shellparam;
895 shellparam.malloc = 0;
896 shellparam.reset = 1;
897 shellparam.nparam = argc - 1;
898 shellparam.p = argv + 1;
899 shellparam.optnext = NULL;
900 INTOFF;
901 savelocalvars = localvars;
902 localvars = NULL;
903 INTON;
904 if (setjmp(jmploc.loc)) {
905 if (exception == EXSHELLPROC) {
906 freeparam((volatile struct shparam *)
907 &saveparam);
908 } else {
909 freeparam(&shellparam);
910 shellparam = saveparam;
911 }
912 poplocalvars();
913 localvars = savelocalvars;
914 handler = savehandler;
915 longjmp(handler->loc, 1);
916 }
917 savehandler = handler;
918 handler = &jmploc;
919 listmklocal(varlist.list, 0);
920 /* stop shell blowing its stack */
921 if (++funcnest > 1000)
922 error("too many nested function calls");
923 evaltree(cmdentry.u.func, flags & EV_TESTED);
924 funcnest--;
925 INTOFF;
926 poplocalvars();
927 localvars = savelocalvars;
928 freeparam(&shellparam);
929 shellparam = saveparam;
930 handler = savehandler;
931 popredir();
932 INTON;
933 if (evalskip == SKIPFUNC) {
934 evalskip = 0;
935 skipcount = 0;
936 }
937 if (flags & EV_EXIT)
938 exitshell(exitstatus);
939 break;
940
941 case CMDBUILTIN:
942 case CMDSPLBLTIN:
943 #ifdef DEBUG
944 trputs("builtin command: "); trargs(argv);
945 #endif
946 mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
947 if (flags == EV_BACKCMD) {
948 memout.nleft = 0;
949 memout.nextc = memout.buf;
950 memout.bufsize = 64;
951 mode |= REDIR_BACKQ;
952 }
953 e = -1;
954 savehandler = handler;
955 handler = &jmploc;
956 if (!setjmp(jmploc.loc)) {
957 /* We need to ensure the command hash table isn't
958 * corruped by temporary PATH assignments.
959 * However we must ensure the 'local' command works!
960 */
961 if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
962 cmdentry.u.bltin == typecmd)) {
963 savelocalvars = localvars;
964 localvars = 0;
965 mklocal(path - 5 /* PATH= */, 0);
966 temp_path = 1;
967 } else
968 temp_path = 0;
969 redirect(cmd->ncmd.redirect, mode);
970
971 savecmdname = commandname;
972 /* exec is a special builtin, but needs this list... */
973 cmdenviron = varlist.list;
974 /* we must check 'readonly' flag for all builtins */
975 listsetvar(varlist.list,
976 cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
977 commandname = argv[0];
978 /* initialize nextopt */
979 argptr = argv + 1;
980 optptr = NULL;
981 /* and getopt */
982 optreset = 1;
983 optind = 1;
984 exitstatus = cmdentry.u.bltin(argc, argv);
985 } else {
986 e = exception;
987 exitstatus = e == EXINT ? SIGINT + 128 :
988 e == EXEXEC ? exerrno : 2;
989 }
990 handler = savehandler;
991 flushall();
992 out1 = &output;
993 out2 = &errout;
994 freestdout();
995 if (temp_path) {
996 poplocalvars();
997 localvars = savelocalvars;
998 }
999 cmdenviron = NULL;
1000 if (e != EXSHELLPROC) {
1001 commandname = savecmdname;
1002 if (flags & EV_EXIT)
1003 exitshell(exitstatus);
1004 }
1005 if (e != -1) {
1006 if ((e != EXERROR && e != EXEXEC)
1007 || cmdentry.cmdtype == CMDSPLBLTIN)
1008 exraise(e);
1009 FORCEINTON;
1010 }
1011 if (cmdentry.u.bltin != execcmd)
1012 popredir();
1013 if (flags == EV_BACKCMD) {
1014 backcmd->buf = memout.buf;
1015 backcmd->nleft = memout.nextc - memout.buf;
1016 memout.buf = NULL;
1017 }
1018 break;
1019
1020 default:
1021 #ifdef DEBUG
1022 trputs("normal command: "); trargs(argv);
1023 #endif
1024 clearredir(vforked);
1025 redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
1026 if (!vforked)
1027 for (sp = varlist.list ; sp ; sp = sp->next)
1028 setvareq(sp->text, VEXPORT|VSTACK);
1029 envp = environment();
1030 shellexec(argv, envp, path, cmdentry.u.index, vforked);
1031 break;
1032 }
1033 goto out;
1034
1035 parent: /* parent process gets here (if we forked) */
1036 if (mode == FORK_FG) { /* argument to fork */
1037 exitstatus = waitforjob(jp);
1038 } else if (mode == FORK_NOJOB) {
1039 backcmd->fd = pip[0];
1040 close(pip[1]);
1041 backcmd->jp = jp;
1042 }
1043 FORCEINTON;
1044
1045 out:
1046 if (lastarg)
1047 /* dsl: I think this is intended to be used to support
1048 * '_' in 'vi' command mode during line editing...
1049 * However I implemented that within libedit itself.
1050 */
1051 setvar("_", lastarg, 0);
1052 popstackmark(&smark);
1053
1054 if (eflag && exitstatus && !(flags & EV_TESTED))
1055 exitshell(exitstatus);
1056 }
1057
1058
1059 /*
1060 * Search for a command. This is called before we fork so that the
1061 * location of the command will be available in the parent as well as
1062 * the child. The check for "goodname" is an overly conservative
1063 * check that the name will not be subject to expansion.
1064 */
1065
1066 STATIC void
1067 prehash(union node *n)
1068 {
1069 struct cmdentry entry;
1070
1071 if (n->type == NCMD && n->ncmd.args)
1072 if (goodname(n->ncmd.args->narg.text))
1073 find_command(n->ncmd.args->narg.text, &entry, 0,
1074 pathval());
1075 }
1076
1077
1078
1079 /*
1080 * Builtin commands. Builtin commands whose functions are closely
1081 * tied to evaluation are implemented here.
1082 */
1083
1084 /*
1085 * No command given.
1086 */
1087
1088 int
1089 bltincmd(int argc, char **argv)
1090 {
1091 /*
1092 * Preserve exitstatus of a previous possible redirection
1093 * as POSIX mandates
1094 */
1095 return back_exitstatus;
1096 }
1097
1098
1099 /*
1100 * Handle break and continue commands. Break, continue, and return are
1101 * all handled by setting the evalskip flag. The evaluation routines
1102 * above all check this flag, and if it is set they start skipping
1103 * commands rather than executing them. The variable skipcount is
1104 * the number of loops to break/continue, or the number of function
1105 * levels to return. (The latter is always 1.) It should probably
1106 * be an error to break out of more loops than exist, but it isn't
1107 * in the standard shell so we don't make it one here.
1108 */
1109
1110 int
1111 breakcmd(int argc, char **argv)
1112 {
1113 int n = argc > 1 ? number(argv[1]) : 1;
1114
1115 if (n > loopnest)
1116 n = loopnest;
1117 if (n > 0) {
1118 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1119 skipcount = n;
1120 }
1121 return 0;
1122 }
1123
1124
1125 /*
1126 * The return command.
1127 */
1128
1129 int
1130 returncmd(int argc, char **argv)
1131 {
1132 int ret = argc > 1 ? number(argv[1]) : exitstatus;
1133
1134 if (funcnest) {
1135 evalskip = SKIPFUNC;
1136 skipcount = 1;
1137 return ret;
1138 }
1139 else {
1140 /* Do what ksh does; skip the rest of the file */
1141 evalskip = SKIPFILE;
1142 skipcount = 1;
1143 return ret;
1144 }
1145 }
1146
1147
1148 int
1149 falsecmd(int argc, char **argv)
1150 {
1151 return 1;
1152 }
1153
1154
1155 int
1156 truecmd(int argc, char **argv)
1157 {
1158 return 0;
1159 }
1160
1161
1162 int
1163 execcmd(int argc, char **argv)
1164 {
1165 if (argc > 1) {
1166 struct strlist *sp;
1167
1168 iflag = 0; /* exit on error */
1169 mflag = 0;
1170 optschanged();
1171 for (sp = cmdenviron; sp; sp = sp->next)
1172 setvareq(sp->text, VEXPORT|VSTACK);
1173 shellexec(argv + 1, environment(), pathval(), 0, 0);
1174 }
1175 return 0;
1176 }
1177
1178 static int
1179 conv_time(clock_t ticks, char *seconds)
1180 {
1181 static clock_t tpm = 0;
1182 clock_t mins;
1183 int i;
1184
1185 if (!tpm)
1186 tpm = sysconf(_SC_CLK_TCK) * 60;
1187
1188 mins = ticks / tpm;
1189 sprintf(seconds, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1190
1191 if (seconds[0] == '6' && seconds[1] == '0') {
1192 /* 59.99995 got rounded up... */
1193 mins++;
1194 strcpy(seconds, "0.0");
1195 return mins;
1196 }
1197
1198 /* suppress trailing zeros */
1199 i = strlen(seconds) - 1;
1200 for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1201 seconds[i] = 0;
1202 return mins;
1203 }
1204
1205 int
1206 timescmd(int argc, char **argv)
1207 {
1208 struct tms tms;
1209 int u, s, cu, cs;
1210 char us[8], ss[8], cus[8], css[8];
1211
1212 nextopt("");
1213
1214 times(&tms);
1215
1216 u = conv_time( tms.tms_utime, us );
1217 s = conv_time( tms.tms_stime, ss );
1218 cu = conv_time( tms.tms_cutime, cus );
1219 cs = conv_time( tms.tms_cstime, css );
1220
1221 outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1222 u, us, s, ss, cu, cus, cs, css);
1223
1224 return 0;
1225 }
1226