VirtualBox

source: kBuild/trunk/src/ash/eval.c@ 2017

Last change on this file since 2017 was 809, checked in by bird, 18 years ago

Solaris + cleanup.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette