source: mainline/uspace/app/ash/eval.c@ c28a023

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c28a023 was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

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