VirtualBox

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

Last change on this file since 1203 was 1203, checked in by bird, 17 years ago

converted a few more files.

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