source: trunk/src/ash/eval.c@ 665

Last change on this file since 665 was 629, checked in by bird, 19 years ago

porting in progress.

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