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