Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/sh/eval.c
39475 views
1
/*-
2
* Copyright (c) 1993
3
* The Regents of the University of California. All rights reserved.
4
*
5
* This code is derived from software contributed to Berkeley by
6
* Kenneth Almquist.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
* 3. Neither the name of the University nor the names of its contributors
17
* may be used to endorse or promote products derived from this software
18
* without specific prior written permission.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*/
32
33
#include <paths.h>
34
#include <signal.h>
35
#include <stdlib.h>
36
#include <unistd.h>
37
#include <sys/resource.h>
38
#include <errno.h>
39
40
/*
41
* Evaluate a command.
42
*/
43
44
#include "shell.h"
45
#include "nodes.h"
46
#include "syntax.h"
47
#include "expand.h"
48
#include "parser.h"
49
#include "jobs.h"
50
#include "eval.h"
51
#include "builtins.h"
52
#include "options.h"
53
#include "exec.h"
54
#include "redir.h"
55
#include "input.h"
56
#include "output.h"
57
#include "trap.h"
58
#include "var.h"
59
#include "memalloc.h"
60
#include "error.h"
61
#include "show.h"
62
#include "mystring.h"
63
#ifndef NO_HISTORY
64
#include "myhistedit.h"
65
#endif
66
67
68
int evalskip; /* set if we are skipping commands */
69
int skipcount; /* number of levels to skip */
70
static int loopnest; /* current loop nesting level */
71
int funcnest; /* depth of function calls */
72
static int builtin_flags; /* evalcommand flags for builtins */
73
74
75
char *commandname;
76
struct arglist *cmdenviron;
77
int exitstatus; /* exit status of last command */
78
int oexitstatus; /* saved exit status */
79
80
81
static void evalloop(union node *, int);
82
static void evalfor(union node *, int);
83
static union node *evalcase(union node *);
84
static void evalsubshell(union node *, int);
85
static void evalredir(union node *, int);
86
static void exphere(union node *, struct arglist *);
87
static void expredir(union node *);
88
static void evalpipe(union node *);
89
static int is_valid_fast_cmdsubst(union node *n);
90
static void evalcommand(union node *, int, struct backcmd *);
91
static void prehash(union node *);
92
93
94
/*
95
* Called to reset things after an exception.
96
*/
97
98
void
99
reseteval(void)
100
{
101
evalskip = 0;
102
loopnest = 0;
103
}
104
105
106
/*
107
* The eval command.
108
*/
109
110
int
111
evalcmd(int argc, char **argv)
112
{
113
char *p;
114
char *concat;
115
char **ap;
116
117
if (argc > 1) {
118
p = argv[1];
119
if (argc > 2) {
120
STARTSTACKSTR(concat);
121
ap = argv + 2;
122
for (;;) {
123
STPUTS(p, concat);
124
if ((p = *ap++) == NULL)
125
break;
126
STPUTC(' ', concat);
127
}
128
STPUTC('\0', concat);
129
p = grabstackstr(concat);
130
}
131
evalstring(p, builtin_flags);
132
} else
133
exitstatus = 0;
134
return exitstatus;
135
}
136
137
138
/*
139
* Execute a command or commands contained in a string.
140
*/
141
142
void
143
evalstring(const char *s, int flags)
144
{
145
union node *n;
146
struct stackmark smark;
147
int flags_exit;
148
int any;
149
150
flags_exit = flags & EV_EXIT;
151
flags &= ~EV_EXIT;
152
any = 0;
153
setstackmark(&smark);
154
setinputstring(s, 1);
155
while ((n = parsecmd(0)) != NEOF) {
156
if (n != NULL && !nflag) {
157
if (flags_exit && preadateof())
158
evaltree(n, flags | EV_EXIT);
159
else
160
evaltree(n, flags);
161
any = 1;
162
if (evalskip)
163
break;
164
}
165
popstackmark(&smark);
166
setstackmark(&smark);
167
}
168
popfile();
169
popstackmark(&smark);
170
if (!any)
171
exitstatus = 0;
172
if (flags_exit)
173
exraise(EXEXIT);
174
}
175
176
177
/*
178
* Evaluate a parse tree. The value is left in the global variable
179
* exitstatus.
180
*/
181
182
void
183
evaltree(union node *n, int flags)
184
{
185
int do_etest;
186
union node *next;
187
struct stackmark smark;
188
189
setstackmark(&smark);
190
do_etest = 0;
191
if (n == NULL) {
192
TRACE(("evaltree(NULL) called\n"));
193
exitstatus = 0;
194
goto out;
195
}
196
do {
197
next = NULL;
198
#ifndef NO_HISTORY
199
displayhist = 1; /* show history substitutions done with fc */
200
#endif
201
TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
202
switch (n->type) {
203
case NSEMI:
204
evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
205
if (evalskip)
206
goto out;
207
next = n->nbinary.ch2;
208
break;
209
case NAND:
210
evaltree(n->nbinary.ch1, EV_TESTED);
211
if (evalskip || exitstatus != 0) {
212
goto out;
213
}
214
next = n->nbinary.ch2;
215
break;
216
case NOR:
217
evaltree(n->nbinary.ch1, EV_TESTED);
218
if (evalskip || exitstatus == 0)
219
goto out;
220
next = n->nbinary.ch2;
221
break;
222
case NREDIR:
223
evalredir(n, flags);
224
break;
225
case NSUBSHELL:
226
evalsubshell(n, flags);
227
do_etest = !(flags & EV_TESTED);
228
break;
229
case NBACKGND:
230
evalsubshell(n, flags);
231
break;
232
case NIF: {
233
evaltree(n->nif.test, EV_TESTED);
234
if (evalskip)
235
goto out;
236
if (exitstatus == 0)
237
next = n->nif.ifpart;
238
else if (n->nif.elsepart)
239
next = n->nif.elsepart;
240
else
241
exitstatus = 0;
242
break;
243
}
244
case NWHILE:
245
case NUNTIL:
246
evalloop(n, flags & ~EV_EXIT);
247
break;
248
case NFOR:
249
evalfor(n, flags & ~EV_EXIT);
250
break;
251
case NCASE:
252
next = evalcase(n);
253
break;
254
case NCLIST:
255
next = n->nclist.body;
256
break;
257
case NCLISTFALLTHRU:
258
if (n->nclist.body) {
259
evaltree(n->nclist.body, flags & ~EV_EXIT);
260
if (evalskip)
261
goto out;
262
}
263
next = n->nclist.next;
264
break;
265
case NDEFUN:
266
defun(n->narg.text, n->narg.next);
267
exitstatus = 0;
268
break;
269
case NNOT:
270
evaltree(n->nnot.com, EV_TESTED);
271
if (evalskip)
272
goto out;
273
exitstatus = !exitstatus;
274
break;
275
276
case NPIPE:
277
evalpipe(n);
278
do_etest = !(flags & EV_TESTED);
279
break;
280
case NCMD:
281
evalcommand(n, flags, (struct backcmd *)NULL);
282
do_etest = !(flags & EV_TESTED);
283
break;
284
default:
285
out1fmt("Node type = %d\n", n->type);
286
flushout(&output);
287
break;
288
}
289
n = next;
290
popstackmark(&smark);
291
setstackmark(&smark);
292
} while (n != NULL);
293
out:
294
popstackmark(&smark);
295
if (pendingsig)
296
dotrap();
297
if (eflag && exitstatus != 0 && do_etest)
298
exitshell(exitstatus);
299
if (flags & EV_EXIT)
300
exraise(EXEXIT);
301
}
302
303
304
static void
305
evalloop(union node *n, int flags)
306
{
307
int status;
308
309
loopnest++;
310
status = 0;
311
for (;;) {
312
if (!evalskip)
313
evaltree(n->nbinary.ch1, EV_TESTED);
314
if (evalskip) {
315
if (evalskip == SKIPCONT && --skipcount <= 0) {
316
evalskip = 0;
317
continue;
318
}
319
if (evalskip == SKIPBREAK && --skipcount <= 0)
320
evalskip = 0;
321
if (evalskip == SKIPRETURN)
322
status = exitstatus;
323
break;
324
}
325
if (n->type == NWHILE) {
326
if (exitstatus != 0)
327
break;
328
} else {
329
if (exitstatus == 0)
330
break;
331
}
332
evaltree(n->nbinary.ch2, flags);
333
status = exitstatus;
334
}
335
loopnest--;
336
exitstatus = status;
337
}
338
339
340
341
static void
342
evalfor(union node *n, int flags)
343
{
344
struct arglist arglist;
345
union node *argp;
346
int i;
347
int status;
348
349
emptyarglist(&arglist);
350
for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
351
oexitstatus = exitstatus;
352
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
353
}
354
355
loopnest++;
356
status = 0;
357
for (i = 0; i < arglist.count; i++) {
358
setvar(n->nfor.var, arglist.args[i], 0);
359
evaltree(n->nfor.body, flags);
360
status = exitstatus;
361
if (evalskip) {
362
if (evalskip == SKIPCONT && --skipcount <= 0) {
363
evalskip = 0;
364
continue;
365
}
366
if (evalskip == SKIPBREAK && --skipcount <= 0)
367
evalskip = 0;
368
break;
369
}
370
}
371
loopnest--;
372
exitstatus = status;
373
}
374
375
376
/*
377
* Evaluate a case statement, returning the selected tree.
378
*
379
* The exit status needs care to get right.
380
*/
381
382
static union node *
383
evalcase(union node *n)
384
{
385
union node *cp;
386
union node *patp;
387
struct arglist arglist;
388
389
emptyarglist(&arglist);
390
oexitstatus = exitstatus;
391
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
392
for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
393
for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
394
if (casematch(patp, arglist.args[0])) {
395
while (cp->nclist.next &&
396
cp->type == NCLISTFALLTHRU &&
397
cp->nclist.body == NULL)
398
cp = cp->nclist.next;
399
if (cp->nclist.next &&
400
cp->type == NCLISTFALLTHRU)
401
return (cp);
402
if (cp->nclist.body == NULL)
403
exitstatus = 0;
404
return (cp->nclist.body);
405
}
406
}
407
}
408
exitstatus = 0;
409
return (NULL);
410
}
411
412
413
414
/*
415
* Kick off a subshell to evaluate a tree.
416
*/
417
418
static void
419
evalsubshell(union node *n, int flags)
420
{
421
struct job *jp;
422
int backgnd = (n->type == NBACKGND);
423
424
oexitstatus = exitstatus;
425
expredir(n->nredir.redirect);
426
if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
427
forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
428
if (backgnd)
429
flags &=~ EV_TESTED;
430
redirect(n->nredir.redirect, 0);
431
evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
432
} else if (! backgnd) {
433
INTOFF;
434
exitstatus = waitforjob(jp, (int *)NULL);
435
INTON;
436
} else
437
exitstatus = 0;
438
}
439
440
441
/*
442
* Evaluate a redirected compound command.
443
*/
444
445
static void
446
evalredir(union node *n, int flags)
447
{
448
struct jmploc jmploc;
449
struct jmploc *savehandler;
450
volatile int in_redirect = 1;
451
452
oexitstatus = exitstatus;
453
expredir(n->nredir.redirect);
454
savehandler = handler;
455
if (setjmp(jmploc.loc)) {
456
int e;
457
458
handler = savehandler;
459
e = exception;
460
popredir();
461
if (e == EXERROR && in_redirect) {
462
FORCEINTON;
463
return;
464
}
465
longjmp(handler->loc, 1);
466
} else {
467
INTOFF;
468
handler = &jmploc;
469
redirect(n->nredir.redirect, REDIR_PUSH);
470
in_redirect = 0;
471
INTON;
472
evaltree(n->nredir.n, flags);
473
}
474
INTOFF;
475
handler = savehandler;
476
popredir();
477
INTON;
478
}
479
480
481
static void
482
exphere(union node *redir, struct arglist *fn)
483
{
484
struct jmploc jmploc;
485
struct jmploc *savehandler;
486
struct localvar *savelocalvars;
487
int need_longjmp = 0;
488
unsigned char saveoptreset;
489
490
redir->nhere.expdoc = "";
491
savelocalvars = localvars;
492
localvars = NULL;
493
saveoptreset = shellparam.reset;
494
forcelocal++;
495
savehandler = handler;
496
if (setjmp(jmploc.loc))
497
need_longjmp = exception != EXERROR;
498
else {
499
handler = &jmploc;
500
expandarg(redir->nhere.doc, fn, 0);
501
redir->nhere.expdoc = fn->args[0];
502
INTOFF;
503
}
504
handler = savehandler;
505
forcelocal--;
506
poplocalvars();
507
localvars = savelocalvars;
508
shellparam.reset = saveoptreset;
509
if (need_longjmp)
510
longjmp(handler->loc, 1);
511
INTON;
512
}
513
514
515
/*
516
* Compute the names of the files in a redirection list.
517
*/
518
519
static void
520
expredir(union node *n)
521
{
522
union node *redir;
523
524
for (redir = n ; redir ; redir = redir->nfile.next) {
525
struct arglist fn;
526
emptyarglist(&fn);
527
switch (redir->type) {
528
case NFROM:
529
case NTO:
530
case NFROMTO:
531
case NAPPEND:
532
case NCLOBBER:
533
expandarg(redir->nfile.fname, &fn, EXP_TILDE);
534
redir->nfile.expfname = fn.args[0];
535
break;
536
case NFROMFD:
537
case NTOFD:
538
if (redir->ndup.vname) {
539
expandarg(redir->ndup.vname, &fn, EXP_TILDE);
540
fixredir(redir, fn.args[0], 1);
541
}
542
break;
543
case NXHERE:
544
exphere(redir, &fn);
545
break;
546
}
547
}
548
}
549
550
551
552
/*
553
* Evaluate a pipeline. All the processes in the pipeline are children
554
* of the process creating the pipeline. (This differs from some versions
555
* of the shell, which make the last process in a pipeline the parent
556
* of all the rest.)
557
*/
558
559
static void
560
evalpipe(union node *n)
561
{
562
struct job *jp;
563
struct nodelist *lp;
564
int pipelen;
565
int prevfd;
566
int pip[2];
567
568
TRACE(("evalpipe(%p) called\n", (void *)n));
569
pipelen = 0;
570
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
571
pipelen++;
572
INTOFF;
573
jp = makejob(n, pipelen);
574
prevfd = -1;
575
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
576
prehash(lp->n);
577
pip[1] = -1;
578
if (lp->next) {
579
if (pipe(pip) < 0) {
580
if (prevfd >= 0)
581
close(prevfd);
582
error("Pipe call failed: %s", strerror(errno));
583
}
584
}
585
if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
586
INTON;
587
if (prevfd > 0) {
588
dup2(prevfd, 0);
589
close(prevfd);
590
}
591
if (pip[1] >= 0) {
592
if (!(prevfd >= 0 && pip[0] == 0))
593
close(pip[0]);
594
if (pip[1] != 1) {
595
dup2(pip[1], 1);
596
close(pip[1]);
597
}
598
}
599
evaltree(lp->n, EV_EXIT);
600
}
601
if (prevfd >= 0)
602
close(prevfd);
603
prevfd = pip[0];
604
if (pip[1] != -1)
605
close(pip[1]);
606
}
607
INTON;
608
if (n->npipe.backgnd == 0) {
609
INTOFF;
610
exitstatus = waitforjob(jp, (int *)NULL);
611
TRACE(("evalpipe: job done exit status %d\n", exitstatus));
612
INTON;
613
} else
614
exitstatus = 0;
615
}
616
617
618
619
static int
620
is_valid_fast_cmdsubst(union node *n)
621
{
622
623
return (n->type == NCMD);
624
}
625
626
/*
627
* Execute a command inside back quotes. If it's a builtin command, we
628
* want to save its output in a block obtained from malloc. Otherwise
629
* we fork off a subprocess and get the output of the command via a pipe.
630
* Should be called with interrupts off.
631
*/
632
633
void
634
evalbackcmd(union node *n, struct backcmd *result)
635
{
636
int pip[2];
637
struct job *jp;
638
struct stackmark smark;
639
struct jmploc jmploc;
640
struct jmploc *savehandler;
641
struct localvar *savelocalvars;
642
unsigned char saveoptreset;
643
644
result->fd = -1;
645
result->buf = NULL;
646
result->nleft = 0;
647
result->jp = NULL;
648
if (n == NULL) {
649
exitstatus = 0;
650
return;
651
}
652
setstackmark(&smark);
653
exitstatus = oexitstatus;
654
if (is_valid_fast_cmdsubst(n)) {
655
savelocalvars = localvars;
656
localvars = NULL;
657
saveoptreset = shellparam.reset;
658
forcelocal++;
659
savehandler = handler;
660
if (setjmp(jmploc.loc)) {
661
if (exception == EXERROR)
662
/* nothing */;
663
else if (exception != 0) {
664
handler = savehandler;
665
forcelocal--;
666
poplocalvars();
667
localvars = savelocalvars;
668
shellparam.reset = saveoptreset;
669
longjmp(handler->loc, 1);
670
}
671
} else {
672
handler = &jmploc;
673
evalcommand(n, EV_BACKCMD, result);
674
}
675
handler = savehandler;
676
forcelocal--;
677
poplocalvars();
678
localvars = savelocalvars;
679
shellparam.reset = saveoptreset;
680
} else {
681
if (pipe(pip) < 0)
682
error("Pipe call failed: %s", strerror(errno));
683
jp = makejob(n, 1);
684
if (forkshell(jp, n, FORK_NOJOB) == 0) {
685
FORCEINTON;
686
close(pip[0]);
687
if (pip[1] != 1) {
688
dup2(pip[1], 1);
689
close(pip[1]);
690
}
691
evaltree(n, EV_EXIT);
692
}
693
close(pip[1]);
694
result->fd = pip[0];
695
result->jp = jp;
696
}
697
popstackmark(&smark);
698
TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
699
result->fd, result->buf, result->nleft, result->jp));
700
}
701
702
static int
703
mustexpandto(const char *argtext, const char *mask)
704
{
705
for (;;) {
706
if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
707
argtext++;
708
continue;
709
}
710
if (*argtext == CTLESC)
711
argtext++;
712
else if (BASESYNTAX[(int)*argtext] == CCTL)
713
return (0);
714
if (*argtext != *mask)
715
return (0);
716
if (*argtext == '\0')
717
return (1);
718
argtext++;
719
mask++;
720
}
721
}
722
723
static int
724
isdeclarationcmd(struct narg *arg)
725
{
726
int have_command = 0;
727
728
if (arg == NULL)
729
return (0);
730
while (mustexpandto(arg->text, "command")) {
731
have_command = 1;
732
arg = &arg->next->narg;
733
if (arg == NULL)
734
return (0);
735
/*
736
* To also allow "command -p" and "command --" as part of
737
* a declaration command, add code here.
738
* We do not do this, as ksh does not do it either and it
739
* is not required by POSIX.
740
*/
741
}
742
return (mustexpandto(arg->text, "export") ||
743
mustexpandto(arg->text, "readonly") ||
744
(mustexpandto(arg->text, "local") &&
745
(have_command || !isfunc("local"))));
746
}
747
748
static void
749
xtracecommand(struct arglist *varlist, int argc, char **argv)
750
{
751
char sep = 0;
752
const char *text, *p, *ps4;
753
int i;
754
755
ps4 = expandstr(ps4val());
756
out2str(ps4 != NULL ? ps4 : ps4val());
757
for (i = 0; i < varlist->count; i++) {
758
text = varlist->args[i];
759
if (sep != 0)
760
out2c(' ');
761
p = strchr(text, '=');
762
if (p != NULL) {
763
p++;
764
outbin(text, p - text, out2);
765
out2qstr(p);
766
} else
767
out2qstr(text);
768
sep = ' ';
769
}
770
for (i = 0; i < argc; i++) {
771
text = argv[i];
772
if (sep != 0)
773
out2c(' ');
774
out2qstr(text);
775
sep = ' ';
776
}
777
out2c('\n');
778
flushout(&errout);
779
}
780
781
/*
782
* Check if a builtin can safely be executed in the same process,
783
* even though it should be in a subshell (command substitution).
784
* Note that jobid, jobs, times and trap can show information not
785
* available in a child process; this is deliberate.
786
* The arguments should already have been expanded.
787
*/
788
static int
789
safe_builtin(int idx, int argc, char **argv)
790
{
791
/* Generated from builtins.def. */
792
if (safe_builtin_always(idx))
793
return (1);
794
if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
795
idx == UMASKCMD)
796
return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
797
if (idx == SETCMD)
798
return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
799
argv[1][0] == '+') && argv[1][1] == 'o' &&
800
argv[1][2] == '\0'));
801
return (0);
802
}
803
804
/*
805
* Execute a simple command.
806
* Note: This may or may not return if (flags & EV_EXIT).
807
*/
808
809
static void
810
evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
811
{
812
union node *argp;
813
struct arglist arglist;
814
struct arglist varlist;
815
char **argv;
816
int argc;
817
char **envp;
818
int varflag;
819
int mode;
820
int pip[2];
821
struct cmdentry cmdentry;
822
struct job *jp;
823
struct jmploc jmploc;
824
struct jmploc *savehandler;
825
char *savecmdname;
826
struct shparam saveparam;
827
struct localvar *savelocalvars;
828
struct parsefile *savetopfile;
829
volatile int e;
830
char *lastarg;
831
int signaled;
832
int do_clearcmdentry;
833
const char *path = pathval();
834
int i;
835
836
/* First expand the arguments. */
837
TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
838
emptyarglist(&arglist);
839
emptyarglist(&varlist);
840
varflag = 1;
841
jp = NULL;
842
do_clearcmdentry = 0;
843
oexitstatus = exitstatus;
844
exitstatus = 0;
845
/* Add one slot at the beginning for tryexec(). */
846
appendarglist(&arglist, nullstr);
847
for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
848
if (varflag && isassignment(argp->narg.text)) {
849
expandarg(argp, varflag == 1 ? &varlist : &arglist,
850
EXP_VARTILDE);
851
continue;
852
} else if (varflag == 1)
853
varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
854
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
855
}
856
appendarglist(&arglist, nullstr);
857
expredir(cmd->ncmd.redirect);
858
argc = arglist.count - 2;
859
argv = &arglist.args[1];
860
861
argv[argc] = NULL;
862
lastarg = NULL;
863
if (iflag && funcnest == 0 && argc > 0)
864
lastarg = argv[argc - 1];
865
866
/* Print the command if xflag is set. */
867
if (xflag)
868
xtracecommand(&varlist, argc, argv);
869
870
/* Now locate the command. */
871
if (argc == 0) {
872
/* Variable assignment(s) without command */
873
cmdentry.cmdtype = CMDBUILTIN;
874
cmdentry.u.index = BLTINCMD;
875
cmdentry.special = 0;
876
} else {
877
static const char PATH[] = "PATH=";
878
int cmd_flags = 0, bltinonly = 0;
879
880
/*
881
* Modify the command lookup path, if a PATH= assignment
882
* is present
883
*/
884
for (i = 0; i < varlist.count; i++)
885
if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) {
886
path = varlist.args[i] + sizeof(PATH) - 1;
887
/*
888
* On `PATH=... command`, we need to make
889
* sure that the command isn't using the
890
* non-updated hash table of the outer PATH
891
* setting and we need to make sure that
892
* the hash table isn't filled with items
893
* from the temporary setting.
894
*
895
* It would be better to forbid using and
896
* updating the table while this command
897
* runs, by the command finding mechanism
898
* is heavily integrated with hash handling,
899
* so we just delete the hash before and after
900
* the command runs. Partly deleting like
901
* changepatch() does doesn't seem worth the
902
* booking effort, since most such runs add
903
* directories in front of the new PATH.
904
*/
905
clearcmdentry();
906
do_clearcmdentry = 1;
907
}
908
909
for (;;) {
910
if (bltinonly) {
911
cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
912
if (cmdentry.u.index < 0) {
913
cmdentry.u.index = BLTINCMD;
914
argv--;
915
argc++;
916
break;
917
}
918
} else
919
find_command(argv[0], &cmdentry, cmd_flags, path);
920
/* implement the bltin and command builtins here */
921
if (cmdentry.cmdtype != CMDBUILTIN)
922
break;
923
if (cmdentry.u.index == BLTINCMD) {
924
if (argc == 1)
925
break;
926
argv++;
927
argc--;
928
bltinonly = 1;
929
} else if (cmdentry.u.index == COMMANDCMD) {
930
if (argc == 1)
931
break;
932
if (!strcmp(argv[1], "-p")) {
933
if (argc == 2)
934
break;
935
if (argv[2][0] == '-') {
936
if (strcmp(argv[2], "--"))
937
break;
938
if (argc == 3)
939
break;
940
argv += 3;
941
argc -= 3;
942
} else {
943
argv += 2;
944
argc -= 2;
945
}
946
path = _PATH_STDPATH;
947
clearcmdentry();
948
do_clearcmdentry = 1;
949
} else if (!strcmp(argv[1], "--")) {
950
if (argc == 2)
951
break;
952
argv += 2;
953
argc -= 2;
954
} else if (argv[1][0] == '-')
955
break;
956
else {
957
argv++;
958
argc--;
959
}
960
cmd_flags |= DO_NOFUNC;
961
bltinonly = 0;
962
} else
963
break;
964
}
965
/*
966
* Special builtins lose their special properties when
967
* called via 'command'.
968
*/
969
if (cmd_flags & DO_NOFUNC)
970
cmdentry.special = 0;
971
}
972
973
/* Fork off a child process if necessary. */
974
if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
975
&& ((flags & EV_EXIT) == 0 || have_traps()))
976
|| ((flags & EV_BACKCMD) != 0
977
&& (cmdentry.cmdtype != CMDBUILTIN ||
978
!safe_builtin(cmdentry.u.index, argc, argv)))) {
979
jp = makejob(cmd, 1);
980
mode = FORK_FG;
981
if (flags & EV_BACKCMD) {
982
mode = FORK_NOJOB;
983
if (pipe(pip) < 0)
984
error("Pipe call failed: %s", strerror(errno));
985
}
986
if (cmdentry.cmdtype == CMDNORMAL &&
987
cmd->ncmd.redirect == NULL &&
988
varlist.count == 0 &&
989
(mode == FORK_FG || mode == FORK_NOJOB) &&
990
!disvforkset() && !iflag && !mflag) {
991
vforkexecshell(jp, argv, environment(), path,
992
cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL);
993
goto parent;
994
}
995
if (forkshell(jp, cmd, mode) != 0)
996
goto parent; /* at end of routine */
997
if (flags & EV_BACKCMD) {
998
FORCEINTON;
999
close(pip[0]);
1000
if (pip[1] != 1) {
1001
dup2(pip[1], 1);
1002
close(pip[1]);
1003
}
1004
flags &= ~EV_BACKCMD;
1005
}
1006
flags |= EV_EXIT;
1007
}
1008
1009
/* This is the child process if a fork occurred. */
1010
/* Execute the command. */
1011
if (cmdentry.cmdtype == CMDFUNCTION) {
1012
#ifdef DEBUG
1013
trputs("Shell function: "); trargs(argv);
1014
#endif
1015
saveparam = shellparam;
1016
shellparam.malloc = 0;
1017
shellparam.reset = 1;
1018
shellparam.nparam = argc - 1;
1019
shellparam.p = argv + 1;
1020
shellparam.optp = NULL;
1021
shellparam.optnext = NULL;
1022
INTOFF;
1023
savelocalvars = localvars;
1024
localvars = NULL;
1025
reffunc(cmdentry.u.func);
1026
savehandler = handler;
1027
if (setjmp(jmploc.loc)) {
1028
popredir();
1029
unreffunc(cmdentry.u.func);
1030
poplocalvars();
1031
localvars = savelocalvars;
1032
freeparam(&shellparam);
1033
shellparam = saveparam;
1034
funcnest--;
1035
handler = savehandler;
1036
longjmp(handler->loc, 1);
1037
}
1038
handler = &jmploc;
1039
funcnest++;
1040
redirect(cmd->ncmd.redirect, REDIR_PUSH);
1041
INTON;
1042
for (i = 0; i < varlist.count; i++)
1043
mklocal(varlist.args[i]);
1044
exitstatus = oexitstatus;
1045
evaltree(getfuncnode(cmdentry.u.func),
1046
flags & (EV_TESTED | EV_EXIT));
1047
INTOFF;
1048
unreffunc(cmdentry.u.func);
1049
poplocalvars();
1050
localvars = savelocalvars;
1051
freeparam(&shellparam);
1052
shellparam = saveparam;
1053
handler = savehandler;
1054
funcnest--;
1055
popredir();
1056
INTON;
1057
if (evalskip == SKIPRETURN) {
1058
evalskip = 0;
1059
skipcount = 0;
1060
}
1061
if (jp)
1062
exitshell(exitstatus);
1063
} else if (cmdentry.cmdtype == CMDBUILTIN) {
1064
#ifdef DEBUG
1065
trputs("builtin command: "); trargs(argv);
1066
#endif
1067
mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
1068
if (flags == EV_BACKCMD) {
1069
memout.nextc = memout.buf;
1070
mode |= REDIR_BACKQ;
1071
}
1072
savecmdname = commandname;
1073
savetopfile = getcurrentfile();
1074
cmdenviron = &varlist;
1075
e = -1;
1076
savehandler = handler;
1077
if (setjmp(jmploc.loc)) {
1078
e = exception;
1079
if (e == EXINT)
1080
exitstatus = SIGINT+128;
1081
goto cmddone;
1082
}
1083
handler = &jmploc;
1084
redirect(cmd->ncmd.redirect, mode);
1085
outclearerror(out1);
1086
/*
1087
* If there is no command word, redirection errors should
1088
* not be fatal but assignment errors should.
1089
*/
1090
if (argc == 0)
1091
cmdentry.special = 1;
1092
listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1093
if (argc > 0)
1094
bltinsetlocale();
1095
commandname = argv[0];
1096
argptr = argv + 1;
1097
nextopt_optptr = NULL; /* initialize nextopt */
1098
builtin_flags = flags;
1099
exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
1100
flushall();
1101
if (outiserror(out1)) {
1102
warning("write error on stdout");
1103
if (exitstatus == 0 || exitstatus == 1)
1104
exitstatus = 2;
1105
}
1106
cmddone:
1107
if (argc > 0)
1108
bltinunsetlocale();
1109
cmdenviron = NULL;
1110
out1 = &output;
1111
out2 = &errout;
1112
freestdout();
1113
handler = savehandler;
1114
commandname = savecmdname;
1115
if (jp)
1116
exitshell(exitstatus);
1117
if (flags == EV_BACKCMD) {
1118
backcmd->buf = memout.buf;
1119
backcmd->nleft = memout.buf != NULL ?
1120
memout.nextc - memout.buf : 0;
1121
memout.buf = NULL;
1122
memout.nextc = NULL;
1123
memout.bufend = NULL;
1124
memout.bufsize = 64;
1125
}
1126
if (cmdentry.u.index != EXECCMD)
1127
popredir();
1128
if (e != -1) {
1129
if (e != EXERROR || cmdentry.special)
1130
exraise(e);
1131
popfilesupto(savetopfile);
1132
if (flags != EV_BACKCMD)
1133
FORCEINTON;
1134
}
1135
} else {
1136
#ifdef DEBUG
1137
trputs("normal command: "); trargs(argv);
1138
#endif
1139
redirect(cmd->ncmd.redirect, 0);
1140
for (i = 0; i < varlist.count; i++)
1141
setvareq(varlist.args[i], VEXPORT|VSTACK);
1142
envp = environment();
1143
shellexec(argv, envp, path, cmdentry.u.index);
1144
/*NOTREACHED*/
1145
}
1146
goto out;
1147
1148
parent: /* parent process gets here (if we forked) */
1149
if (mode == FORK_FG) { /* argument to fork */
1150
INTOFF;
1151
exitstatus = waitforjob(jp, &signaled);
1152
INTON;
1153
if (iflag && loopnest > 0 && signaled) {
1154
evalskip = SKIPBREAK;
1155
skipcount = loopnest;
1156
}
1157
} else if (mode == FORK_NOJOB) {
1158
backcmd->fd = pip[0];
1159
close(pip[1]);
1160
backcmd->jp = jp;
1161
}
1162
1163
out:
1164
if (lastarg)
1165
setvar("_", lastarg, 0);
1166
if (do_clearcmdentry)
1167
clearcmdentry();
1168
}
1169
1170
1171
1172
/*
1173
* Search for a command. This is called before we fork so that the
1174
* location of the command will be available in the parent as well as
1175
* the child. The check for "goodname" is an overly conservative
1176
* check that the name will not be subject to expansion.
1177
*/
1178
1179
static void
1180
prehash(union node *n)
1181
{
1182
struct cmdentry entry;
1183
1184
if (n && n->type == NCMD && n->ncmd.args)
1185
if (goodname(n->ncmd.args->narg.text))
1186
find_command(n->ncmd.args->narg.text, &entry, 0,
1187
pathval());
1188
}
1189
1190
1191
1192
/*
1193
* Builtin commands. Builtin commands whose functions are closely
1194
* tied to evaluation are implemented here.
1195
*/
1196
1197
/*
1198
* No command given, a bltin command with no arguments, or a bltin command
1199
* with an invalid name.
1200
*/
1201
1202
int
1203
bltincmd(int argc, char **argv)
1204
{
1205
if (argc > 1) {
1206
out2fmt_flush("%s: not found\n", argv[1]);
1207
return 127;
1208
}
1209
/*
1210
* Preserve exitstatus of a previous possible command substitution
1211
* as POSIX mandates
1212
*/
1213
return exitstatus;
1214
}
1215
1216
1217
/*
1218
* Handle break and continue commands. Break, continue, and return are
1219
* all handled by setting the evalskip flag. The evaluation routines
1220
* above all check this flag, and if it is set they start skipping
1221
* commands rather than executing them. The variable skipcount is
1222
* the number of loops to break/continue, or the number of function
1223
* levels to return. (The latter is always 1.) It should probably
1224
* be an error to break out of more loops than exist, but it isn't
1225
* in the standard shell so we don't make it one here.
1226
*/
1227
1228
int
1229
breakcmd(int argc, char **argv)
1230
{
1231
long n;
1232
char *end;
1233
1234
if (argc > 1) {
1235
/* Allow arbitrarily large numbers. */
1236
n = strtol(argv[1], &end, 10);
1237
if (!is_digit(argv[1][0]) || *end != '\0')
1238
error("Illegal number: %s", argv[1]);
1239
} else
1240
n = 1;
1241
if (n > loopnest)
1242
n = loopnest;
1243
if (n > 0) {
1244
evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1245
skipcount = n;
1246
}
1247
return 0;
1248
}
1249
1250
/*
1251
* The `command' command.
1252
*/
1253
int
1254
commandcmd(int argc __unused, char **argv __unused)
1255
{
1256
const char *path;
1257
int ch;
1258
int cmd = -1;
1259
1260
path = bltinlookup("PATH", 1);
1261
1262
while ((ch = nextopt("pvV")) != '\0') {
1263
switch (ch) {
1264
case 'p':
1265
path = _PATH_STDPATH;
1266
break;
1267
case 'v':
1268
cmd = TYPECMD_SMALLV;
1269
break;
1270
case 'V':
1271
cmd = TYPECMD_BIGV;
1272
break;
1273
}
1274
}
1275
1276
if (cmd != -1) {
1277
if (*argptr == NULL || argptr[1] != NULL)
1278
error("wrong number of arguments");
1279
return typecmd_impl(2, argptr - 1, cmd, path);
1280
}
1281
if (*argptr != NULL)
1282
error("commandcmd bad call");
1283
1284
/*
1285
* Do nothing successfully if no command was specified;
1286
* ksh also does this.
1287
*/
1288
return 0;
1289
}
1290
1291
1292
/*
1293
* The return command.
1294
*/
1295
1296
int
1297
returncmd(int argc, char **argv)
1298
{
1299
int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1300
1301
evalskip = SKIPRETURN;
1302
skipcount = 1;
1303
return ret;
1304
}
1305
1306
1307
int
1308
falsecmd(int argc __unused, char **argv __unused)
1309
{
1310
return 1;
1311
}
1312
1313
1314
int
1315
truecmd(int argc __unused, char **argv __unused)
1316
{
1317
return 0;
1318
}
1319
1320
1321
int
1322
execcmd(int argc, char **argv)
1323
{
1324
int i;
1325
1326
/*
1327
* Because we have historically not supported any options,
1328
* only treat "--" specially.
1329
*/
1330
if (argc > 1 && strcmp(argv[1], "--") == 0)
1331
argc--, argv++;
1332
if (argc > 1) {
1333
iflag = 0; /* exit on error */
1334
mflag = 0;
1335
optschanged();
1336
for (i = 0; i < cmdenviron->count; i++)
1337
setvareq(cmdenviron->args[i], VEXPORT|VSTACK);
1338
shellexec(argv + 1, environment(), pathval(), 0);
1339
1340
}
1341
return 0;
1342
}
1343
1344
1345
int
1346
timescmd(int argc __unused, char **argv __unused)
1347
{
1348
struct rusage ru;
1349
long shumins, shsmins, chumins, chsmins;
1350
double shusecs, shssecs, chusecs, chssecs;
1351
1352
if (getrusage(RUSAGE_SELF, &ru) < 0)
1353
return 1;
1354
shumins = ru.ru_utime.tv_sec / 60;
1355
shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1356
shsmins = ru.ru_stime.tv_sec / 60;
1357
shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1358
if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1359
return 1;
1360
chumins = ru.ru_utime.tv_sec / 60;
1361
chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1362
chsmins = ru.ru_stime.tv_sec / 60;
1363
chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1364
out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
1365
shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
1366
return 0;
1367
}
1368
1369