Subversion Repositories HelenOS

Rev

Rev 2714 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2714 cejka 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
42
static 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
 
86
MKINIT int evalskip;        /* set if we are skipping commands */
87
STATIC int skipcount;       /* number of levels to skip */
88
MKINIT int loopnest;        /* current loop nesting level */
89
int funcnest;           /* depth of function calls */
90
 
91
 
92
char *commandname;
93
struct strlist *cmdenviron;
94
int exitstatus;         /* exit status of last command */
95
int oexitstatus;        /* saved exit status */
96
 
97
 
98
STATIC void evalloop (union node *, int);
99
STATIC void evalfor (union node *, int);
100
STATIC void evalcase (union node *, int);
101
STATIC void evalsubshell (union node *, int);
102
STATIC void expredir (union node *);
103
STATIC void evalpipe (union node *);
104
STATIC void evalcommand (union node *, int, struct backcmd *);
105
STATIC void prehash (union node *);
106
STATIC int is_assignment_builtin (const char *);
107
STATIC const char *get_standard_path (void);
108
 
109
 
110
/*
111
 * Called to reset things after an exception.
112
 */
113
 
114
#ifdef mkinit
115
INCLUDE "eval.h"
116
 
117
RESET {
118
    evalskip = 0;
119
    loopnest = 0;
120
    funcnest = 0;
121
}
122
 
123
SHELLPROC {
124
    exitstatus = 0;
125
}
126
#endif
127
 
128
 
129
 
130
/*
131
 * The eval commmand.
132
 */
133
 
134
int
135
evalcmd(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
 
168
void
169
evalstring(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
 
193
void
194
evaltree(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
    }
288
out:
289
    if (pendingsigs)
290
        dotrap();
291
    if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
292
        exitshell(exitstatus);
293
}
294
 
295
 
296
STATIC void
297
evalloop(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) {
308
skipping:     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
 
334
STATIC void
335
evalfor(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--;
370
out:
371
    popstackmark(&smark);
372
}
373
 
374
 
375
 
376
STATIC void
377
evalcase(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
    }
400
out:
401
    popstackmark(&smark);
402
}
403
 
404
 
405
 
406
/*
407
 * Kick off a subshell to evaluate a tree.
408
 */
409
 
410
STATIC void
411
evalsubshell(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
 
439
STATIC void
440
expredir(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
 
478
STATIC void
479
evalpipe(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
 
549
void
550
evalbackcmd(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
    }
599
out:
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
 
611
STATIC void
612
evalcommand(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();
907
cmddone:
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
 
960
parent: /* 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
 
971
out:
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
 
989
STATIC void
990
prehash(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
 
1013
int
1014
bltincmd(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
 
1038
int
1039
breakcmd(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
 
1059
int
1060
returncmd(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
 
1080
int
1081
falsecmd(argc, argv)
1082
    int argc;
1083
    char **argv;
1084
{
1085
    return 1;
1086
}
1087
 
1088
 
1089
int
1090
truecmd(argc, argv)
1091
    int argc;
1092
    char **argv;
1093
{
1094
    return 0;
1095
}
1096
 
1097
 
1098
int
1099
execcmd(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
 
1116
STATIC int
1117
is_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
 
1131
int
1132
is_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
 
1148
STATIC const char *
1149
get_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
}