VirtualBox

source: kBuild/trunk/src/kash/eval.c@ 2291

Last change on this file since 2291 was 2290, checked in by bird, 16 years ago

kash: malloc/free/friends gets a psh.

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