1*0a6a1f1dSLionel Sambuc /* $NetBSD: eval.c,v 1.110 2015/01/02 19:56:20 christos Exp $ */
2d90bee97SLionel Sambuc
3d90bee97SLionel Sambuc /*-
4d90bee97SLionel Sambuc * Copyright (c) 1993
5d90bee97SLionel Sambuc * The Regents of the University of California. All rights reserved.
6d90bee97SLionel Sambuc *
7d90bee97SLionel Sambuc * This code is derived from software contributed to Berkeley by
8d90bee97SLionel Sambuc * Kenneth Almquist.
9d90bee97SLionel Sambuc *
10d90bee97SLionel Sambuc * Redistribution and use in source and binary forms, with or without
11d90bee97SLionel Sambuc * modification, are permitted provided that the following conditions
12d90bee97SLionel Sambuc * are met:
13d90bee97SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
14d90bee97SLionel Sambuc * notice, this list of conditions and the following disclaimer.
15d90bee97SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
16d90bee97SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
17d90bee97SLionel Sambuc * documentation and/or other materials provided with the distribution.
18d90bee97SLionel Sambuc * 3. Neither the name of the University nor the names of its contributors
19d90bee97SLionel Sambuc * may be used to endorse or promote products derived from this software
20d90bee97SLionel Sambuc * without specific prior written permission.
21d90bee97SLionel Sambuc *
22d90bee97SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23d90bee97SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24d90bee97SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25d90bee97SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26d90bee97SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27d90bee97SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28d90bee97SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29d90bee97SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30d90bee97SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31d90bee97SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32d90bee97SLionel Sambuc * SUCH DAMAGE.
33d90bee97SLionel Sambuc */
34d90bee97SLionel Sambuc
35d90bee97SLionel Sambuc #include <sys/cdefs.h>
36d90bee97SLionel Sambuc #ifndef lint
37d90bee97SLionel Sambuc #if 0
38d90bee97SLionel Sambuc static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
39d90bee97SLionel Sambuc #else
40*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: eval.c,v 1.110 2015/01/02 19:56:20 christos Exp $");
41d90bee97SLionel Sambuc #endif
42d90bee97SLionel Sambuc #endif /* not lint */
43d90bee97SLionel Sambuc
44d90bee97SLionel Sambuc #include <stdbool.h>
45d90bee97SLionel Sambuc #include <stdlib.h>
46d90bee97SLionel Sambuc #include <signal.h>
47d90bee97SLionel Sambuc #include <stdio.h>
48d90bee97SLionel Sambuc #include <errno.h>
49d90bee97SLionel Sambuc #include <limits.h>
50d90bee97SLionel Sambuc #include <unistd.h>
51d90bee97SLionel Sambuc #include <sys/fcntl.h>
52d90bee97SLionel Sambuc #include <sys/times.h>
53d90bee97SLionel Sambuc #include <sys/param.h>
54d90bee97SLionel Sambuc #include <sys/types.h>
55d90bee97SLionel Sambuc #include <sys/wait.h>
56d90bee97SLionel Sambuc #include <sys/sysctl.h>
57d90bee97SLionel Sambuc
58d90bee97SLionel Sambuc /*
59d90bee97SLionel Sambuc * Evaluate a command.
60d90bee97SLionel Sambuc */
61d90bee97SLionel Sambuc
62d90bee97SLionel Sambuc #include "shell.h"
63d90bee97SLionel Sambuc #include "nodes.h"
64d90bee97SLionel Sambuc #include "syntax.h"
65d90bee97SLionel Sambuc #include "expand.h"
66d90bee97SLionel Sambuc #include "parser.h"
67d90bee97SLionel Sambuc #include "jobs.h"
68d90bee97SLionel Sambuc #include "eval.h"
69d90bee97SLionel Sambuc #include "builtins.h"
70d90bee97SLionel Sambuc #include "options.h"
71d90bee97SLionel Sambuc #include "exec.h"
72d90bee97SLionel Sambuc #include "redir.h"
73d90bee97SLionel Sambuc #include "input.h"
74d90bee97SLionel Sambuc #include "output.h"
75d90bee97SLionel Sambuc #include "trap.h"
76d90bee97SLionel Sambuc #include "var.h"
77d90bee97SLionel Sambuc #include "memalloc.h"
78d90bee97SLionel Sambuc #include "error.h"
79d90bee97SLionel Sambuc #include "show.h"
80d90bee97SLionel Sambuc #include "mystring.h"
81d90bee97SLionel Sambuc #include "main.h"
82d90bee97SLionel Sambuc #ifndef SMALL
83d90bee97SLionel Sambuc #include "myhistedit.h"
84d90bee97SLionel Sambuc #endif
85d90bee97SLionel Sambuc
86d90bee97SLionel Sambuc
87d90bee97SLionel Sambuc /* flags in argument to evaltree */
88d90bee97SLionel Sambuc #define EV_EXIT 01 /* exit after evaluating tree */
89d90bee97SLionel Sambuc #define EV_TESTED 02 /* exit status is checked; ignore -e flag */
90d90bee97SLionel Sambuc #define EV_BACKCMD 04 /* command executing within back quotes */
91d90bee97SLionel Sambuc
92*0a6a1f1dSLionel Sambuc STATIC enum skipstate evalskip; /* != SKIPNONE if we are skipping commands */
93d90bee97SLionel Sambuc STATIC int skipcount; /* number of levels to skip */
94*0a6a1f1dSLionel Sambuc STATIC int loopnest; /* current loop nesting level */
95*0a6a1f1dSLionel Sambuc STATIC int funcnest; /* depth of function calls */
96d90bee97SLionel Sambuc STATIC int builtin_flags; /* evalcommand flags for builtins */
97*0a6a1f1dSLionel Sambuc /*
98*0a6a1f1dSLionel Sambuc * Base function nesting level inside a dot command. Set to 0 initially
99*0a6a1f1dSLionel Sambuc * and to (funcnest + 1) before every dot command to enable
100*0a6a1f1dSLionel Sambuc * 1) detection of being in a file sourced by a dot command and
101*0a6a1f1dSLionel Sambuc * 2) counting of function nesting in that file for the implementation
102*0a6a1f1dSLionel Sambuc * of the return command.
103*0a6a1f1dSLionel Sambuc * The value is reset to its previous value after the dot command.
104*0a6a1f1dSLionel Sambuc */
105*0a6a1f1dSLionel Sambuc STATIC int dot_funcnest;
106d90bee97SLionel Sambuc
107d90bee97SLionel Sambuc
108d90bee97SLionel Sambuc const char *commandname;
109d90bee97SLionel Sambuc struct strlist *cmdenviron;
110d90bee97SLionel Sambuc int exitstatus; /* exit status of last command */
111d90bee97SLionel Sambuc int back_exitstatus; /* exit status of backquoted command */
112d90bee97SLionel Sambuc
113d90bee97SLionel Sambuc
114d90bee97SLionel Sambuc STATIC void evalloop(union node *, int);
115d90bee97SLionel Sambuc STATIC void evalfor(union node *, int);
116d90bee97SLionel Sambuc STATIC void evalcase(union node *, int);
117d90bee97SLionel Sambuc STATIC void evalsubshell(union node *, int);
118d90bee97SLionel Sambuc STATIC void expredir(union node *);
119d90bee97SLionel Sambuc STATIC void evalpipe(union node *);
120d90bee97SLionel Sambuc STATIC void evalcommand(union node *, int, struct backcmd *);
121d90bee97SLionel Sambuc STATIC void prehash(union node *);
122d90bee97SLionel Sambuc
123*0a6a1f1dSLionel Sambuc STATIC char *find_dot_file(char *);
124d90bee97SLionel Sambuc
125d90bee97SLionel Sambuc /*
126d90bee97SLionel Sambuc * Called to reset things after an exception.
127d90bee97SLionel Sambuc */
128d90bee97SLionel Sambuc
129d90bee97SLionel Sambuc #ifdef mkinit
130d90bee97SLionel Sambuc INCLUDE "eval.h"
131d90bee97SLionel Sambuc
132d90bee97SLionel Sambuc RESET {
133*0a6a1f1dSLionel Sambuc reset_eval();
134d90bee97SLionel Sambuc }
135d90bee97SLionel Sambuc
136d90bee97SLionel Sambuc SHELLPROC {
137d90bee97SLionel Sambuc exitstatus = 0;
138d90bee97SLionel Sambuc }
139d90bee97SLionel Sambuc #endif
140d90bee97SLionel Sambuc
141*0a6a1f1dSLionel Sambuc void
reset_eval(void)142*0a6a1f1dSLionel Sambuc reset_eval(void)
143*0a6a1f1dSLionel Sambuc {
144*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
145*0a6a1f1dSLionel Sambuc dot_funcnest = 0;
146*0a6a1f1dSLionel Sambuc loopnest = 0;
147*0a6a1f1dSLionel Sambuc funcnest = 0;
148*0a6a1f1dSLionel Sambuc }
149*0a6a1f1dSLionel Sambuc
150d90bee97SLionel Sambuc static int
sh_pipe(int fds[2])151d90bee97SLionel Sambuc sh_pipe(int fds[2])
152d90bee97SLionel Sambuc {
153d90bee97SLionel Sambuc int nfd;
154d90bee97SLionel Sambuc
155d90bee97SLionel Sambuc if (pipe(fds))
156d90bee97SLionel Sambuc return -1;
157d90bee97SLionel Sambuc
158d90bee97SLionel Sambuc if (fds[0] < 3) {
159d90bee97SLionel Sambuc nfd = fcntl(fds[0], F_DUPFD, 3);
160d90bee97SLionel Sambuc if (nfd != -1) {
161d90bee97SLionel Sambuc close(fds[0]);
162d90bee97SLionel Sambuc fds[0] = nfd;
163d90bee97SLionel Sambuc }
164d90bee97SLionel Sambuc }
165d90bee97SLionel Sambuc
166d90bee97SLionel Sambuc if (fds[1] < 3) {
167d90bee97SLionel Sambuc nfd = fcntl(fds[1], F_DUPFD, 3);
168d90bee97SLionel Sambuc if (nfd != -1) {
169d90bee97SLionel Sambuc close(fds[1]);
170d90bee97SLionel Sambuc fds[1] = nfd;
171d90bee97SLionel Sambuc }
172d90bee97SLionel Sambuc }
173d90bee97SLionel Sambuc return 0;
174d90bee97SLionel Sambuc }
175d90bee97SLionel Sambuc
176d90bee97SLionel Sambuc
177d90bee97SLionel Sambuc /*
178d90bee97SLionel Sambuc * The eval commmand.
179d90bee97SLionel Sambuc */
180d90bee97SLionel Sambuc
181d90bee97SLionel Sambuc int
evalcmd(int argc,char ** argv)182d90bee97SLionel Sambuc evalcmd(int argc, char **argv)
183d90bee97SLionel Sambuc {
184d90bee97SLionel Sambuc char *p;
185d90bee97SLionel Sambuc char *concat;
186d90bee97SLionel Sambuc char **ap;
187d90bee97SLionel Sambuc
188d90bee97SLionel Sambuc if (argc > 1) {
189d90bee97SLionel Sambuc p = argv[1];
190d90bee97SLionel Sambuc if (argc > 2) {
191d90bee97SLionel Sambuc STARTSTACKSTR(concat);
192d90bee97SLionel Sambuc ap = argv + 2;
193d90bee97SLionel Sambuc for (;;) {
194d90bee97SLionel Sambuc while (*p)
195d90bee97SLionel Sambuc STPUTC(*p++, concat);
196d90bee97SLionel Sambuc if ((p = *ap++) == NULL)
197d90bee97SLionel Sambuc break;
198d90bee97SLionel Sambuc STPUTC(' ', concat);
199d90bee97SLionel Sambuc }
200d90bee97SLionel Sambuc STPUTC('\0', concat);
201d90bee97SLionel Sambuc p = grabstackstr(concat);
202d90bee97SLionel Sambuc }
203d90bee97SLionel Sambuc evalstring(p, builtin_flags & EV_TESTED);
204d90bee97SLionel Sambuc }
205d90bee97SLionel Sambuc return exitstatus;
206d90bee97SLionel Sambuc }
207d90bee97SLionel Sambuc
208d90bee97SLionel Sambuc
209d90bee97SLionel Sambuc /*
210d90bee97SLionel Sambuc * Execute a command or commands contained in a string.
211d90bee97SLionel Sambuc */
212d90bee97SLionel Sambuc
213d90bee97SLionel Sambuc void
evalstring(char * s,int flag)214d90bee97SLionel Sambuc evalstring(char *s, int flag)
215d90bee97SLionel Sambuc {
216d90bee97SLionel Sambuc union node *n;
217d90bee97SLionel Sambuc struct stackmark smark;
218d90bee97SLionel Sambuc
219d90bee97SLionel Sambuc setstackmark(&smark);
220d90bee97SLionel Sambuc setinputstring(s, 1);
221d90bee97SLionel Sambuc
222d90bee97SLionel Sambuc while ((n = parsecmd(0)) != NEOF) {
223d90bee97SLionel Sambuc evaltree(n, flag);
224d90bee97SLionel Sambuc popstackmark(&smark);
225d90bee97SLionel Sambuc }
226d90bee97SLionel Sambuc popfile();
227d90bee97SLionel Sambuc popstackmark(&smark);
228d90bee97SLionel Sambuc }
229d90bee97SLionel Sambuc
230d90bee97SLionel Sambuc
231d90bee97SLionel Sambuc
232d90bee97SLionel Sambuc /*
233d90bee97SLionel Sambuc * Evaluate a parse tree. The value is left in the global variable
234d90bee97SLionel Sambuc * exitstatus.
235d90bee97SLionel Sambuc */
236d90bee97SLionel Sambuc
237d90bee97SLionel Sambuc void
evaltree(union node * n,int flags)238d90bee97SLionel Sambuc evaltree(union node *n, int flags)
239d90bee97SLionel Sambuc {
240d90bee97SLionel Sambuc bool do_etest;
241d90bee97SLionel Sambuc
242d90bee97SLionel Sambuc do_etest = false;
243d90bee97SLionel Sambuc if (n == NULL) {
244d90bee97SLionel Sambuc TRACE(("evaltree(NULL) called\n"));
245d90bee97SLionel Sambuc exitstatus = 0;
246d90bee97SLionel Sambuc goto out;
247d90bee97SLionel Sambuc }
248d90bee97SLionel Sambuc #ifndef SMALL
249d90bee97SLionel Sambuc displayhist = 1; /* show history substitutions done with fc */
250d90bee97SLionel Sambuc #endif
251d90bee97SLionel Sambuc TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
252d90bee97SLionel Sambuc getpid(), n, n->type, flags));
253d90bee97SLionel Sambuc switch (n->type) {
254d90bee97SLionel Sambuc case NSEMI:
255d90bee97SLionel Sambuc evaltree(n->nbinary.ch1, flags & EV_TESTED);
256d90bee97SLionel Sambuc if (evalskip)
257d90bee97SLionel Sambuc goto out;
258d90bee97SLionel Sambuc evaltree(n->nbinary.ch2, flags);
259d90bee97SLionel Sambuc break;
260d90bee97SLionel Sambuc case NAND:
261d90bee97SLionel Sambuc evaltree(n->nbinary.ch1, EV_TESTED);
262d90bee97SLionel Sambuc if (evalskip || exitstatus != 0)
263d90bee97SLionel Sambuc goto out;
264d90bee97SLionel Sambuc evaltree(n->nbinary.ch2, flags);
265d90bee97SLionel Sambuc break;
266d90bee97SLionel Sambuc case NOR:
267d90bee97SLionel Sambuc evaltree(n->nbinary.ch1, EV_TESTED);
268d90bee97SLionel Sambuc if (evalskip || exitstatus == 0)
269d90bee97SLionel Sambuc goto out;
270d90bee97SLionel Sambuc evaltree(n->nbinary.ch2, flags);
271d90bee97SLionel Sambuc break;
272d90bee97SLionel Sambuc case NREDIR:
273d90bee97SLionel Sambuc expredir(n->nredir.redirect);
274d90bee97SLionel Sambuc redirect(n->nredir.redirect, REDIR_PUSH);
275d90bee97SLionel Sambuc evaltree(n->nredir.n, flags);
276d90bee97SLionel Sambuc popredir();
277d90bee97SLionel Sambuc break;
278d90bee97SLionel Sambuc case NSUBSHELL:
279d90bee97SLionel Sambuc evalsubshell(n, flags);
280d90bee97SLionel Sambuc do_etest = !(flags & EV_TESTED);
281d90bee97SLionel Sambuc break;
282d90bee97SLionel Sambuc case NBACKGND:
283d90bee97SLionel Sambuc evalsubshell(n, flags);
284d90bee97SLionel Sambuc break;
285d90bee97SLionel Sambuc case NIF: {
286d90bee97SLionel Sambuc evaltree(n->nif.test, EV_TESTED);
287d90bee97SLionel Sambuc if (evalskip)
288d90bee97SLionel Sambuc goto out;
289d90bee97SLionel Sambuc if (exitstatus == 0)
290d90bee97SLionel Sambuc evaltree(n->nif.ifpart, flags);
291d90bee97SLionel Sambuc else if (n->nif.elsepart)
292d90bee97SLionel Sambuc evaltree(n->nif.elsepart, flags);
293d90bee97SLionel Sambuc else
294d90bee97SLionel Sambuc exitstatus = 0;
295d90bee97SLionel Sambuc break;
296d90bee97SLionel Sambuc }
297d90bee97SLionel Sambuc case NWHILE:
298d90bee97SLionel Sambuc case NUNTIL:
299d90bee97SLionel Sambuc evalloop(n, flags);
300d90bee97SLionel Sambuc break;
301d90bee97SLionel Sambuc case NFOR:
302d90bee97SLionel Sambuc evalfor(n, flags);
303d90bee97SLionel Sambuc break;
304d90bee97SLionel Sambuc case NCASE:
305d90bee97SLionel Sambuc evalcase(n, flags);
306d90bee97SLionel Sambuc break;
307d90bee97SLionel Sambuc case NDEFUN:
308d90bee97SLionel Sambuc defun(n->narg.text, n->narg.next);
309d90bee97SLionel Sambuc exitstatus = 0;
310d90bee97SLionel Sambuc break;
311d90bee97SLionel Sambuc case NNOT:
312d90bee97SLionel Sambuc evaltree(n->nnot.com, EV_TESTED);
313d90bee97SLionel Sambuc exitstatus = !exitstatus;
314d90bee97SLionel Sambuc break;
315d90bee97SLionel Sambuc case NPIPE:
316d90bee97SLionel Sambuc evalpipe(n);
317d90bee97SLionel Sambuc do_etest = !(flags & EV_TESTED);
318d90bee97SLionel Sambuc break;
319d90bee97SLionel Sambuc case NCMD:
320d90bee97SLionel Sambuc evalcommand(n, flags, NULL);
321d90bee97SLionel Sambuc do_etest = !(flags & EV_TESTED);
322d90bee97SLionel Sambuc break;
323d90bee97SLionel Sambuc default:
324d90bee97SLionel Sambuc out1fmt("Node type = %d\n", n->type);
325d90bee97SLionel Sambuc flushout(&output);
326d90bee97SLionel Sambuc break;
327d90bee97SLionel Sambuc }
328d90bee97SLionel Sambuc out:
329d90bee97SLionel Sambuc if (pendingsigs)
330d90bee97SLionel Sambuc dotrap();
331d90bee97SLionel Sambuc if ((flags & EV_EXIT) != 0 || (eflag && exitstatus != 0 && do_etest))
332d90bee97SLionel Sambuc exitshell(exitstatus);
333d90bee97SLionel Sambuc }
334d90bee97SLionel Sambuc
335d90bee97SLionel Sambuc
336d90bee97SLionel Sambuc STATIC void
evalloop(union node * n,int flags)337d90bee97SLionel Sambuc evalloop(union node *n, int flags)
338d90bee97SLionel Sambuc {
339d90bee97SLionel Sambuc int status;
340d90bee97SLionel Sambuc
341d90bee97SLionel Sambuc loopnest++;
342d90bee97SLionel Sambuc status = 0;
343d90bee97SLionel Sambuc for (;;) {
344d90bee97SLionel Sambuc evaltree(n->nbinary.ch1, EV_TESTED);
345d90bee97SLionel Sambuc if (evalskip) {
346d90bee97SLionel Sambuc skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
347*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
348d90bee97SLionel Sambuc continue;
349d90bee97SLionel Sambuc }
350d90bee97SLionel Sambuc if (evalskip == SKIPBREAK && --skipcount <= 0)
351*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
352d90bee97SLionel Sambuc break;
353d90bee97SLionel Sambuc }
354d90bee97SLionel Sambuc if (n->type == NWHILE) {
355d90bee97SLionel Sambuc if (exitstatus != 0)
356d90bee97SLionel Sambuc break;
357d90bee97SLionel Sambuc } else {
358d90bee97SLionel Sambuc if (exitstatus == 0)
359d90bee97SLionel Sambuc break;
360d90bee97SLionel Sambuc }
361d90bee97SLionel Sambuc evaltree(n->nbinary.ch2, flags & EV_TESTED);
362d90bee97SLionel Sambuc status = exitstatus;
363d90bee97SLionel Sambuc if (evalskip)
364d90bee97SLionel Sambuc goto skipping;
365d90bee97SLionel Sambuc }
366d90bee97SLionel Sambuc loopnest--;
367d90bee97SLionel Sambuc exitstatus = status;
368d90bee97SLionel Sambuc }
369d90bee97SLionel Sambuc
370d90bee97SLionel Sambuc
371d90bee97SLionel Sambuc
372d90bee97SLionel Sambuc STATIC void
evalfor(union node * n,int flags)373d90bee97SLionel Sambuc evalfor(union node *n, int flags)
374d90bee97SLionel Sambuc {
375d90bee97SLionel Sambuc struct arglist arglist;
376d90bee97SLionel Sambuc union node *argp;
377d90bee97SLionel Sambuc struct strlist *sp;
378d90bee97SLionel Sambuc struct stackmark smark;
379d90bee97SLionel Sambuc int status = 0;
380d90bee97SLionel Sambuc
381d90bee97SLionel Sambuc setstackmark(&smark);
382d90bee97SLionel Sambuc arglist.lastp = &arglist.list;
383d90bee97SLionel Sambuc for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
384d90bee97SLionel Sambuc expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
385d90bee97SLionel Sambuc if (evalskip)
386d90bee97SLionel Sambuc goto out;
387d90bee97SLionel Sambuc }
388d90bee97SLionel Sambuc *arglist.lastp = NULL;
389d90bee97SLionel Sambuc
390d90bee97SLionel Sambuc loopnest++;
391d90bee97SLionel Sambuc for (sp = arglist.list ; sp ; sp = sp->next) {
392d90bee97SLionel Sambuc setvar(n->nfor.var, sp->text, 0);
393d90bee97SLionel Sambuc evaltree(n->nfor.body, flags & EV_TESTED);
394d90bee97SLionel Sambuc status = exitstatus;
395d90bee97SLionel Sambuc if (evalskip) {
396d90bee97SLionel Sambuc if (evalskip == SKIPCONT && --skipcount <= 0) {
397*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
398d90bee97SLionel Sambuc continue;
399d90bee97SLionel Sambuc }
400d90bee97SLionel Sambuc if (evalskip == SKIPBREAK && --skipcount <= 0)
401*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
402d90bee97SLionel Sambuc break;
403d90bee97SLionel Sambuc }
404d90bee97SLionel Sambuc }
405d90bee97SLionel Sambuc loopnest--;
406d90bee97SLionel Sambuc exitstatus = status;
407d90bee97SLionel Sambuc out:
408d90bee97SLionel Sambuc popstackmark(&smark);
409d90bee97SLionel Sambuc }
410d90bee97SLionel Sambuc
411d90bee97SLionel Sambuc
412d90bee97SLionel Sambuc
413d90bee97SLionel Sambuc STATIC void
evalcase(union node * n,int flags)414d90bee97SLionel Sambuc evalcase(union node *n, int flags)
415d90bee97SLionel Sambuc {
416d90bee97SLionel Sambuc union node *cp;
417d90bee97SLionel Sambuc union node *patp;
418d90bee97SLionel Sambuc struct arglist arglist;
419d90bee97SLionel Sambuc struct stackmark smark;
420d90bee97SLionel Sambuc int status = 0;
421d90bee97SLionel Sambuc
422d90bee97SLionel Sambuc setstackmark(&smark);
423d90bee97SLionel Sambuc arglist.lastp = &arglist.list;
424d90bee97SLionel Sambuc expandarg(n->ncase.expr, &arglist, EXP_TILDE);
425d90bee97SLionel Sambuc for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
426d90bee97SLionel Sambuc for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
427d90bee97SLionel Sambuc if (casematch(patp, arglist.list->text)) {
428d90bee97SLionel Sambuc if (evalskip == 0) {
429d90bee97SLionel Sambuc evaltree(cp->nclist.body, flags);
430d90bee97SLionel Sambuc status = exitstatus;
431d90bee97SLionel Sambuc }
432d90bee97SLionel Sambuc goto out;
433d90bee97SLionel Sambuc }
434d90bee97SLionel Sambuc }
435d90bee97SLionel Sambuc }
436d90bee97SLionel Sambuc out:
437d90bee97SLionel Sambuc exitstatus = status;
438d90bee97SLionel Sambuc popstackmark(&smark);
439d90bee97SLionel Sambuc }
440d90bee97SLionel Sambuc
441d90bee97SLionel Sambuc
442d90bee97SLionel Sambuc
443d90bee97SLionel Sambuc /*
444d90bee97SLionel Sambuc * Kick off a subshell to evaluate a tree.
445d90bee97SLionel Sambuc */
446d90bee97SLionel Sambuc
447d90bee97SLionel Sambuc STATIC void
evalsubshell(union node * n,int flags)448d90bee97SLionel Sambuc evalsubshell(union node *n, int flags)
449d90bee97SLionel Sambuc {
450d90bee97SLionel Sambuc struct job *jp;
451d90bee97SLionel Sambuc int backgnd = (n->type == NBACKGND);
452d90bee97SLionel Sambuc
453d90bee97SLionel Sambuc expredir(n->nredir.redirect);
454d90bee97SLionel Sambuc INTOFF;
455d90bee97SLionel Sambuc jp = makejob(n, 1);
456d90bee97SLionel Sambuc if (forkshell(jp, n, backgnd ? FORK_BG : FORK_FG) == 0) {
457d90bee97SLionel Sambuc INTON;
458d90bee97SLionel Sambuc if (backgnd)
459d90bee97SLionel Sambuc flags &=~ EV_TESTED;
460d90bee97SLionel Sambuc redirect(n->nredir.redirect, 0);
461d90bee97SLionel Sambuc /* never returns */
462d90bee97SLionel Sambuc evaltree(n->nredir.n, flags | EV_EXIT);
463d90bee97SLionel Sambuc }
464d90bee97SLionel Sambuc if (! backgnd)
465d90bee97SLionel Sambuc exitstatus = waitforjob(jp);
466d90bee97SLionel Sambuc INTON;
467d90bee97SLionel Sambuc }
468d90bee97SLionel Sambuc
469d90bee97SLionel Sambuc
470d90bee97SLionel Sambuc
471d90bee97SLionel Sambuc /*
472d90bee97SLionel Sambuc * Compute the names of the files in a redirection list.
473d90bee97SLionel Sambuc */
474d90bee97SLionel Sambuc
475d90bee97SLionel Sambuc STATIC void
expredir(union node * n)476d90bee97SLionel Sambuc expredir(union node *n)
477d90bee97SLionel Sambuc {
478d90bee97SLionel Sambuc union node *redir;
479d90bee97SLionel Sambuc
480d90bee97SLionel Sambuc for (redir = n ; redir ; redir = redir->nfile.next) {
481d90bee97SLionel Sambuc struct arglist fn;
482d90bee97SLionel Sambuc fn.lastp = &fn.list;
483d90bee97SLionel Sambuc switch (redir->type) {
484d90bee97SLionel Sambuc case NFROMTO:
485d90bee97SLionel Sambuc case NFROM:
486d90bee97SLionel Sambuc case NTO:
487d90bee97SLionel Sambuc case NCLOBBER:
488d90bee97SLionel Sambuc case NAPPEND:
489d90bee97SLionel Sambuc expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
490d90bee97SLionel Sambuc redir->nfile.expfname = fn.list->text;
491d90bee97SLionel Sambuc break;
492d90bee97SLionel Sambuc case NFROMFD:
493d90bee97SLionel Sambuc case NTOFD:
494d90bee97SLionel Sambuc if (redir->ndup.vname) {
495d90bee97SLionel Sambuc expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
496d90bee97SLionel Sambuc fixredir(redir, fn.list->text, 1);
497d90bee97SLionel Sambuc }
498d90bee97SLionel Sambuc break;
499d90bee97SLionel Sambuc }
500d90bee97SLionel Sambuc }
501d90bee97SLionel Sambuc }
502d90bee97SLionel Sambuc
503d90bee97SLionel Sambuc
504d90bee97SLionel Sambuc
505d90bee97SLionel Sambuc /*
506d90bee97SLionel Sambuc * Evaluate a pipeline. All the processes in the pipeline are children
507d90bee97SLionel Sambuc * of the process creating the pipeline. (This differs from some versions
508d90bee97SLionel Sambuc * of the shell, which make the last process in a pipeline the parent
509d90bee97SLionel Sambuc * of all the rest.)
510d90bee97SLionel Sambuc */
511d90bee97SLionel Sambuc
512d90bee97SLionel Sambuc STATIC void
evalpipe(union node * n)513d90bee97SLionel Sambuc evalpipe(union node *n)
514d90bee97SLionel Sambuc {
515d90bee97SLionel Sambuc struct job *jp;
516d90bee97SLionel Sambuc struct nodelist *lp;
517d90bee97SLionel Sambuc int pipelen;
518d90bee97SLionel Sambuc int prevfd;
519d90bee97SLionel Sambuc int pip[2];
520d90bee97SLionel Sambuc
521d90bee97SLionel Sambuc TRACE(("evalpipe(0x%lx) called\n", (long)n));
522d90bee97SLionel Sambuc pipelen = 0;
523d90bee97SLionel Sambuc for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
524d90bee97SLionel Sambuc pipelen++;
525d90bee97SLionel Sambuc INTOFF;
526d90bee97SLionel Sambuc jp = makejob(n, pipelen);
527d90bee97SLionel Sambuc prevfd = -1;
528d90bee97SLionel Sambuc for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
529d90bee97SLionel Sambuc prehash(lp->n);
530d90bee97SLionel Sambuc pip[1] = -1;
531d90bee97SLionel Sambuc if (lp->next) {
532d90bee97SLionel Sambuc if (sh_pipe(pip) < 0) {
533d90bee97SLionel Sambuc if (prevfd >= 0)
534d90bee97SLionel Sambuc close(prevfd);
535d90bee97SLionel Sambuc error("Pipe call failed");
536d90bee97SLionel Sambuc }
537d90bee97SLionel Sambuc }
538d90bee97SLionel Sambuc if (forkshell(jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
539d90bee97SLionel Sambuc INTON;
540d90bee97SLionel Sambuc if (prevfd > 0) {
541d90bee97SLionel Sambuc close(0);
542d90bee97SLionel Sambuc copyfd(prevfd, 0, 1);
543d90bee97SLionel Sambuc close(prevfd);
544d90bee97SLionel Sambuc }
545d90bee97SLionel Sambuc if (pip[1] >= 0) {
546d90bee97SLionel Sambuc close(pip[0]);
547d90bee97SLionel Sambuc if (pip[1] != 1) {
548d90bee97SLionel Sambuc close(1);
549d90bee97SLionel Sambuc copyfd(pip[1], 1, 1);
550d90bee97SLionel Sambuc close(pip[1]);
551d90bee97SLionel Sambuc }
552d90bee97SLionel Sambuc }
553d90bee97SLionel Sambuc evaltree(lp->n, EV_EXIT);
554d90bee97SLionel Sambuc }
555d90bee97SLionel Sambuc if (prevfd >= 0)
556d90bee97SLionel Sambuc close(prevfd);
557d90bee97SLionel Sambuc prevfd = pip[0];
558d90bee97SLionel Sambuc close(pip[1]);
559d90bee97SLionel Sambuc }
560d90bee97SLionel Sambuc if (n->npipe.backgnd == 0) {
561d90bee97SLionel Sambuc exitstatus = waitforjob(jp);
562d90bee97SLionel Sambuc TRACE(("evalpipe: job done exit status %d\n", exitstatus));
563d90bee97SLionel Sambuc }
564d90bee97SLionel Sambuc INTON;
565d90bee97SLionel Sambuc }
566d90bee97SLionel Sambuc
567d90bee97SLionel Sambuc
568d90bee97SLionel Sambuc
569d90bee97SLionel Sambuc /*
570d90bee97SLionel Sambuc * Execute a command inside back quotes. If it's a builtin command, we
571d90bee97SLionel Sambuc * want to save its output in a block obtained from malloc. Otherwise
572d90bee97SLionel Sambuc * we fork off a subprocess and get the output of the command via a pipe.
573d90bee97SLionel Sambuc * Should be called with interrupts off.
574d90bee97SLionel Sambuc */
575d90bee97SLionel Sambuc
576d90bee97SLionel Sambuc void
evalbackcmd(union node * n,struct backcmd * result)577d90bee97SLionel Sambuc evalbackcmd(union node *n, struct backcmd *result)
578d90bee97SLionel Sambuc {
579d90bee97SLionel Sambuc int pip[2];
580d90bee97SLionel Sambuc struct job *jp;
581d90bee97SLionel Sambuc struct stackmark smark; /* unnecessary */
582d90bee97SLionel Sambuc
583d90bee97SLionel Sambuc setstackmark(&smark);
584d90bee97SLionel Sambuc result->fd = -1;
585d90bee97SLionel Sambuc result->buf = NULL;
586d90bee97SLionel Sambuc result->nleft = 0;
587d90bee97SLionel Sambuc result->jp = NULL;
588d90bee97SLionel Sambuc if (n == NULL) {
589d90bee97SLionel Sambuc goto out;
590d90bee97SLionel Sambuc }
591d90bee97SLionel Sambuc #ifdef notyet
592d90bee97SLionel Sambuc /*
593d90bee97SLionel Sambuc * For now we disable executing builtins in the same
594d90bee97SLionel Sambuc * context as the shell, because we are not keeping
595d90bee97SLionel Sambuc * enough state to recover from changes that are
596d90bee97SLionel Sambuc * supposed only to affect subshells. eg. echo "`cd /`"
597d90bee97SLionel Sambuc */
598d90bee97SLionel Sambuc if (n->type == NCMD) {
599d90bee97SLionel Sambuc exitstatus = oexitstatus;
600d90bee97SLionel Sambuc evalcommand(n, EV_BACKCMD, result);
601d90bee97SLionel Sambuc } else
602d90bee97SLionel Sambuc #endif
603d90bee97SLionel Sambuc {
604d90bee97SLionel Sambuc INTOFF;
605d90bee97SLionel Sambuc if (sh_pipe(pip) < 0)
606d90bee97SLionel Sambuc error("Pipe call failed");
607d90bee97SLionel Sambuc jp = makejob(n, 1);
608d90bee97SLionel Sambuc if (forkshell(jp, n, FORK_NOJOB) == 0) {
609d90bee97SLionel Sambuc FORCEINTON;
610d90bee97SLionel Sambuc close(pip[0]);
611d90bee97SLionel Sambuc if (pip[1] != 1) {
612d90bee97SLionel Sambuc close(1);
613d90bee97SLionel Sambuc copyfd(pip[1], 1, 1);
614d90bee97SLionel Sambuc close(pip[1]);
615d90bee97SLionel Sambuc }
616d90bee97SLionel Sambuc eflag = 0;
617d90bee97SLionel Sambuc evaltree(n, EV_EXIT);
618d90bee97SLionel Sambuc /* NOTREACHED */
619d90bee97SLionel Sambuc }
620d90bee97SLionel Sambuc close(pip[1]);
621d90bee97SLionel Sambuc result->fd = pip[0];
622d90bee97SLionel Sambuc result->jp = jp;
623d90bee97SLionel Sambuc INTON;
624d90bee97SLionel Sambuc }
625d90bee97SLionel Sambuc out:
626d90bee97SLionel Sambuc popstackmark(&smark);
627d90bee97SLionel Sambuc TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
628d90bee97SLionel Sambuc result->fd, result->buf, result->nleft, result->jp));
629d90bee97SLionel Sambuc }
630d90bee97SLionel Sambuc
631d90bee97SLionel Sambuc static const char *
syspath(void)632d90bee97SLionel Sambuc syspath(void)
633d90bee97SLionel Sambuc {
634d90bee97SLionel Sambuc static char *sys_path = NULL;
635d90bee97SLionel Sambuc static int mib[] = {CTL_USER, USER_CS_PATH};
636d90bee97SLionel Sambuc static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
637d90bee97SLionel Sambuc size_t len;
638d90bee97SLionel Sambuc
639d90bee97SLionel Sambuc if (sys_path == NULL) {
640d90bee97SLionel Sambuc if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
641d90bee97SLionel Sambuc (sys_path = ckmalloc(len + 5)) != NULL &&
642d90bee97SLionel Sambuc sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
643d90bee97SLionel Sambuc memcpy(sys_path, "PATH=", 5);
644d90bee97SLionel Sambuc } else {
645d90bee97SLionel Sambuc ckfree(sys_path);
646d90bee97SLionel Sambuc /* something to keep things happy */
647d90bee97SLionel Sambuc sys_path = def_path;
648d90bee97SLionel Sambuc }
649d90bee97SLionel Sambuc }
650d90bee97SLionel Sambuc return sys_path;
651d90bee97SLionel Sambuc }
652d90bee97SLionel Sambuc
653d90bee97SLionel Sambuc static int
parse_command_args(int argc,char ** argv,int * use_syspath)654d90bee97SLionel Sambuc parse_command_args(int argc, char **argv, int *use_syspath)
655d90bee97SLionel Sambuc {
656d90bee97SLionel Sambuc int sv_argc = argc;
657d90bee97SLionel Sambuc char *cp, c;
658d90bee97SLionel Sambuc
659d90bee97SLionel Sambuc *use_syspath = 0;
660d90bee97SLionel Sambuc
661d90bee97SLionel Sambuc for (;;) {
662d90bee97SLionel Sambuc argv++;
663d90bee97SLionel Sambuc if (--argc == 0)
664d90bee97SLionel Sambuc break;
665d90bee97SLionel Sambuc cp = *argv;
666d90bee97SLionel Sambuc if (*cp++ != '-')
667d90bee97SLionel Sambuc break;
668d90bee97SLionel Sambuc if (*cp == '-' && cp[1] == 0) {
669d90bee97SLionel Sambuc argv++;
670d90bee97SLionel Sambuc argc--;
671d90bee97SLionel Sambuc break;
672d90bee97SLionel Sambuc }
673d90bee97SLionel Sambuc while ((c = *cp++)) {
674d90bee97SLionel Sambuc switch (c) {
675d90bee97SLionel Sambuc case 'p':
676d90bee97SLionel Sambuc *use_syspath = 1;
677d90bee97SLionel Sambuc break;
678d90bee97SLionel Sambuc default:
679d90bee97SLionel Sambuc /* run 'typecmd' for other options */
680d90bee97SLionel Sambuc return 0;
681d90bee97SLionel Sambuc }
682d90bee97SLionel Sambuc }
683d90bee97SLionel Sambuc }
684d90bee97SLionel Sambuc return sv_argc - argc;
685d90bee97SLionel Sambuc }
686d90bee97SLionel Sambuc
687d90bee97SLionel Sambuc int vforked = 0;
688d90bee97SLionel Sambuc extern char *trap[];
689d90bee97SLionel Sambuc
690d90bee97SLionel Sambuc /*
691d90bee97SLionel Sambuc * Execute a simple command.
692d90bee97SLionel Sambuc */
693d90bee97SLionel Sambuc
694d90bee97SLionel Sambuc STATIC void
evalcommand(union node * cmd,int flgs,struct backcmd * backcmd)695d90bee97SLionel Sambuc evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
696d90bee97SLionel Sambuc {
697d90bee97SLionel Sambuc struct stackmark smark;
698d90bee97SLionel Sambuc union node *argp;
699d90bee97SLionel Sambuc struct arglist arglist;
700d90bee97SLionel Sambuc struct arglist varlist;
701d90bee97SLionel Sambuc volatile int flags = flgs;
702d90bee97SLionel Sambuc char ** volatile argv;
703d90bee97SLionel Sambuc volatile int argc;
704d90bee97SLionel Sambuc char **envp;
705d90bee97SLionel Sambuc int varflag;
706d90bee97SLionel Sambuc struct strlist *sp;
707d90bee97SLionel Sambuc volatile int mode;
708d90bee97SLionel Sambuc int pip[2];
709d90bee97SLionel Sambuc struct cmdentry cmdentry;
710d90bee97SLionel Sambuc struct job * volatile jp;
711d90bee97SLionel Sambuc struct jmploc jmploc;
712d90bee97SLionel Sambuc struct jmploc *volatile savehandler = NULL;
713d90bee97SLionel Sambuc const char *volatile savecmdname;
714d90bee97SLionel Sambuc volatile struct shparam saveparam;
715d90bee97SLionel Sambuc struct localvar *volatile savelocalvars;
716d90bee97SLionel Sambuc volatile int e;
717d90bee97SLionel Sambuc char * volatile lastarg;
718d90bee97SLionel Sambuc const char * volatile path = pathval();
719d90bee97SLionel Sambuc volatile int temp_path;
720d90bee97SLionel Sambuc
721d90bee97SLionel Sambuc vforked = 0;
722d90bee97SLionel Sambuc /* First expand the arguments. */
723d90bee97SLionel Sambuc TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
724d90bee97SLionel Sambuc setstackmark(&smark);
725d90bee97SLionel Sambuc back_exitstatus = 0;
726d90bee97SLionel Sambuc
727d90bee97SLionel Sambuc arglist.lastp = &arglist.list;
728d90bee97SLionel Sambuc varflag = 1;
729d90bee97SLionel Sambuc /* Expand arguments, ignoring the initial 'name=value' ones */
730d90bee97SLionel Sambuc for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
731d90bee97SLionel Sambuc char *p = argp->narg.text;
732d90bee97SLionel Sambuc if (varflag && is_name(*p)) {
733d90bee97SLionel Sambuc do {
734d90bee97SLionel Sambuc p++;
735d90bee97SLionel Sambuc } while (is_in_name(*p));
736d90bee97SLionel Sambuc if (*p == '=')
737d90bee97SLionel Sambuc continue;
738d90bee97SLionel Sambuc }
739d90bee97SLionel Sambuc expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
740d90bee97SLionel Sambuc varflag = 0;
741d90bee97SLionel Sambuc }
742d90bee97SLionel Sambuc *arglist.lastp = NULL;
743d90bee97SLionel Sambuc
744d90bee97SLionel Sambuc expredir(cmd->ncmd.redirect);
745d90bee97SLionel Sambuc
746d90bee97SLionel Sambuc /* Now do the initial 'name=value' ones we skipped above */
747d90bee97SLionel Sambuc varlist.lastp = &varlist.list;
748d90bee97SLionel Sambuc for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
749d90bee97SLionel Sambuc char *p = argp->narg.text;
750d90bee97SLionel Sambuc if (!is_name(*p))
751d90bee97SLionel Sambuc break;
752d90bee97SLionel Sambuc do
753d90bee97SLionel Sambuc p++;
754d90bee97SLionel Sambuc while (is_in_name(*p));
755d90bee97SLionel Sambuc if (*p != '=')
756d90bee97SLionel Sambuc break;
757d90bee97SLionel Sambuc expandarg(argp, &varlist, EXP_VARTILDE);
758d90bee97SLionel Sambuc }
759d90bee97SLionel Sambuc *varlist.lastp = NULL;
760d90bee97SLionel Sambuc
761d90bee97SLionel Sambuc argc = 0;
762d90bee97SLionel Sambuc for (sp = arglist.list ; sp ; sp = sp->next)
763d90bee97SLionel Sambuc argc++;
764d90bee97SLionel Sambuc argv = stalloc(sizeof (char *) * (argc + 1));
765d90bee97SLionel Sambuc
766d90bee97SLionel Sambuc for (sp = arglist.list ; sp ; sp = sp->next) {
767d90bee97SLionel Sambuc TRACE(("evalcommand arg: %s\n", sp->text));
768d90bee97SLionel Sambuc *argv++ = sp->text;
769d90bee97SLionel Sambuc }
770d90bee97SLionel Sambuc *argv = NULL;
771d90bee97SLionel Sambuc lastarg = NULL;
772d90bee97SLionel Sambuc if (iflag && funcnest == 0 && argc > 0)
773d90bee97SLionel Sambuc lastarg = argv[-1];
774d90bee97SLionel Sambuc argv -= argc;
775d90bee97SLionel Sambuc
776d90bee97SLionel Sambuc /* Print the command if xflag is set. */
777d90bee97SLionel Sambuc if (xflag) {
778d90bee97SLionel Sambuc char sep = 0;
779d90bee97SLionel Sambuc out2str(ps4val());
780d90bee97SLionel Sambuc for (sp = varlist.list ; sp ; sp = sp->next) {
781d90bee97SLionel Sambuc if (sep != 0)
782d90bee97SLionel Sambuc outc(sep, &errout);
783d90bee97SLionel Sambuc out2shstr(sp->text);
784d90bee97SLionel Sambuc sep = ' ';
785d90bee97SLionel Sambuc }
786d90bee97SLionel Sambuc for (sp = arglist.list ; sp ; sp = sp->next) {
787d90bee97SLionel Sambuc if (sep != 0)
788d90bee97SLionel Sambuc outc(sep, &errout);
789d90bee97SLionel Sambuc out2shstr(sp->text);
790d90bee97SLionel Sambuc sep = ' ';
791d90bee97SLionel Sambuc }
792d90bee97SLionel Sambuc outc('\n', &errout);
793d90bee97SLionel Sambuc flushout(&errout);
794d90bee97SLionel Sambuc }
795d90bee97SLionel Sambuc
796d90bee97SLionel Sambuc /* Now locate the command. */
797d90bee97SLionel Sambuc if (argc == 0) {
798d90bee97SLionel Sambuc cmdentry.cmdtype = CMDSPLBLTIN;
799d90bee97SLionel Sambuc cmdentry.u.bltin = bltincmd;
800d90bee97SLionel Sambuc } else {
801d90bee97SLionel Sambuc static const char PATH[] = "PATH=";
802d90bee97SLionel Sambuc int cmd_flags = DO_ERR;
803d90bee97SLionel Sambuc
804d90bee97SLionel Sambuc /*
805d90bee97SLionel Sambuc * Modify the command lookup path, if a PATH= assignment
806d90bee97SLionel Sambuc * is present
807d90bee97SLionel Sambuc */
808d90bee97SLionel Sambuc for (sp = varlist.list; sp; sp = sp->next)
809d90bee97SLionel Sambuc if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
810d90bee97SLionel Sambuc path = sp->text + sizeof(PATH) - 1;
811d90bee97SLionel Sambuc
812d90bee97SLionel Sambuc do {
813d90bee97SLionel Sambuc int argsused, use_syspath;
814d90bee97SLionel Sambuc find_command(argv[0], &cmdentry, cmd_flags, path);
815d90bee97SLionel Sambuc if (cmdentry.cmdtype == CMDUNKNOWN) {
816d90bee97SLionel Sambuc exitstatus = 127;
817d90bee97SLionel Sambuc flushout(&errout);
818d90bee97SLionel Sambuc goto out;
819d90bee97SLionel Sambuc }
820d90bee97SLionel Sambuc
821d90bee97SLionel Sambuc /* implement the 'command' builtin here */
822d90bee97SLionel Sambuc if (cmdentry.cmdtype != CMDBUILTIN ||
823d90bee97SLionel Sambuc cmdentry.u.bltin != bltincmd)
824d90bee97SLionel Sambuc break;
825d90bee97SLionel Sambuc cmd_flags |= DO_NOFUNC;
826d90bee97SLionel Sambuc argsused = parse_command_args(argc, argv, &use_syspath);
827d90bee97SLionel Sambuc if (argsused == 0) {
828d90bee97SLionel Sambuc /* use 'type' builting to display info */
829d90bee97SLionel Sambuc cmdentry.u.bltin = typecmd;
830d90bee97SLionel Sambuc break;
831d90bee97SLionel Sambuc }
832d90bee97SLionel Sambuc argc -= argsused;
833d90bee97SLionel Sambuc argv += argsused;
834d90bee97SLionel Sambuc if (use_syspath)
835d90bee97SLionel Sambuc path = syspath() + 5;
836d90bee97SLionel Sambuc } while (argc != 0);
837d90bee97SLionel Sambuc if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
838d90bee97SLionel Sambuc /* posix mandates that 'command <splbltin>' act as if
839d90bee97SLionel Sambuc <splbltin> was a normal builtin */
840d90bee97SLionel Sambuc cmdentry.cmdtype = CMDBUILTIN;
841d90bee97SLionel Sambuc }
842d90bee97SLionel Sambuc
843d90bee97SLionel Sambuc /* Fork off a child process if necessary. */
844d90bee97SLionel Sambuc if (cmd->ncmd.backgnd || (trap[0] && (flags & EV_EXIT) != 0)
845d90bee97SLionel Sambuc || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
846d90bee97SLionel Sambuc || ((flags & EV_BACKCMD) != 0
847d90bee97SLionel Sambuc && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
848d90bee97SLionel Sambuc || cmdentry.u.bltin == dotcmd
849d90bee97SLionel Sambuc || cmdentry.u.bltin == evalcmd))) {
850d90bee97SLionel Sambuc INTOFF;
851d90bee97SLionel Sambuc jp = makejob(cmd, 1);
852d90bee97SLionel Sambuc mode = cmd->ncmd.backgnd;
853d90bee97SLionel Sambuc if (flags & EV_BACKCMD) {
854d90bee97SLionel Sambuc mode = FORK_NOJOB;
855d90bee97SLionel Sambuc if (sh_pipe(pip) < 0)
856d90bee97SLionel Sambuc error("Pipe call failed");
857d90bee97SLionel Sambuc }
858d90bee97SLionel Sambuc #ifdef DO_SHAREDVFORK
859d90bee97SLionel Sambuc /* It is essential that if DO_SHAREDVFORK is defined that the
860d90bee97SLionel Sambuc * child's address space is actually shared with the parent as
861d90bee97SLionel Sambuc * we rely on this.
862d90bee97SLionel Sambuc */
863*0a6a1f1dSLionel Sambuc if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL) {
864d90bee97SLionel Sambuc pid_t pid;
865*0a6a1f1dSLionel Sambuc int serrno;
866d90bee97SLionel Sambuc
867d90bee97SLionel Sambuc savelocalvars = localvars;
868d90bee97SLionel Sambuc localvars = NULL;
869d90bee97SLionel Sambuc vforked = 1;
870d90bee97SLionel Sambuc switch (pid = vfork()) {
871d90bee97SLionel Sambuc case -1:
872*0a6a1f1dSLionel Sambuc serrno = errno;
873*0a6a1f1dSLionel Sambuc TRACE(("Vfork failed, errno=%d\n", serrno));
874d90bee97SLionel Sambuc INTON;
875*0a6a1f1dSLionel Sambuc error("Cannot vfork (%s)", strerror(serrno));
876d90bee97SLionel Sambuc break;
877d90bee97SLionel Sambuc case 0:
878d90bee97SLionel Sambuc /* Make sure that exceptions only unwind to
879d90bee97SLionel Sambuc * after the vfork(2)
880d90bee97SLionel Sambuc */
881d90bee97SLionel Sambuc if (setjmp(jmploc.loc)) {
882d90bee97SLionel Sambuc if (exception == EXSHELLPROC) {
883d90bee97SLionel Sambuc /* We can't progress with the vfork,
884d90bee97SLionel Sambuc * so, set vforked = 2 so the parent
885d90bee97SLionel Sambuc * knows, and _exit();
886d90bee97SLionel Sambuc */
887d90bee97SLionel Sambuc vforked = 2;
888d90bee97SLionel Sambuc _exit(0);
889d90bee97SLionel Sambuc } else {
890d90bee97SLionel Sambuc _exit(exerrno);
891d90bee97SLionel Sambuc }
892d90bee97SLionel Sambuc }
893d90bee97SLionel Sambuc savehandler = handler;
894d90bee97SLionel Sambuc handler = &jmploc;
895d90bee97SLionel Sambuc listmklocal(varlist.list, VEXPORT | VNOFUNC);
896d90bee97SLionel Sambuc forkchild(jp, cmd, mode, vforked);
897d90bee97SLionel Sambuc break;
898d90bee97SLionel Sambuc default:
899d90bee97SLionel Sambuc handler = savehandler; /* restore from vfork(2) */
900d90bee97SLionel Sambuc poplocalvars();
901d90bee97SLionel Sambuc localvars = savelocalvars;
902d90bee97SLionel Sambuc if (vforked == 2) {
903d90bee97SLionel Sambuc vforked = 0;
904d90bee97SLionel Sambuc
905d90bee97SLionel Sambuc (void)waitpid(pid, NULL, 0);
906d90bee97SLionel Sambuc /* We need to progress in a normal fork fashion */
907d90bee97SLionel Sambuc goto normal_fork;
908d90bee97SLionel Sambuc }
909d90bee97SLionel Sambuc vforked = 0;
910d90bee97SLionel Sambuc forkparent(jp, cmd, mode, pid);
911d90bee97SLionel Sambuc goto parent;
912d90bee97SLionel Sambuc }
913d90bee97SLionel Sambuc } else {
914d90bee97SLionel Sambuc normal_fork:
915d90bee97SLionel Sambuc #endif
916d90bee97SLionel Sambuc if (forkshell(jp, cmd, mode) != 0)
917d90bee97SLionel Sambuc goto parent; /* at end of routine */
918d90bee97SLionel Sambuc FORCEINTON;
919d90bee97SLionel Sambuc #ifdef DO_SHAREDVFORK
920d90bee97SLionel Sambuc }
921d90bee97SLionel Sambuc #endif
922d90bee97SLionel Sambuc if (flags & EV_BACKCMD) {
923d90bee97SLionel Sambuc if (!vforked) {
924d90bee97SLionel Sambuc FORCEINTON;
925d90bee97SLionel Sambuc }
926d90bee97SLionel Sambuc close(pip[0]);
927d90bee97SLionel Sambuc if (pip[1] != 1) {
928d90bee97SLionel Sambuc close(1);
929d90bee97SLionel Sambuc copyfd(pip[1], 1, 1);
930d90bee97SLionel Sambuc close(pip[1]);
931d90bee97SLionel Sambuc }
932d90bee97SLionel Sambuc }
933d90bee97SLionel Sambuc flags |= EV_EXIT;
934d90bee97SLionel Sambuc }
935d90bee97SLionel Sambuc
936d90bee97SLionel Sambuc /* This is the child process if a fork occurred. */
937d90bee97SLionel Sambuc /* Execute the command. */
938d90bee97SLionel Sambuc switch (cmdentry.cmdtype) {
939d90bee97SLionel Sambuc case CMDFUNCTION:
940d90bee97SLionel Sambuc #ifdef DEBUG
941d90bee97SLionel Sambuc trputs("Shell function: "); trargs(argv);
942d90bee97SLionel Sambuc #endif
943d90bee97SLionel Sambuc redirect(cmd->ncmd.redirect, REDIR_PUSH);
944d90bee97SLionel Sambuc saveparam = shellparam;
945d90bee97SLionel Sambuc shellparam.malloc = 0;
946d90bee97SLionel Sambuc shellparam.reset = 1;
947d90bee97SLionel Sambuc shellparam.nparam = argc - 1;
948d90bee97SLionel Sambuc shellparam.p = argv + 1;
949d90bee97SLionel Sambuc shellparam.optnext = NULL;
950d90bee97SLionel Sambuc INTOFF;
951d90bee97SLionel Sambuc savelocalvars = localvars;
952d90bee97SLionel Sambuc localvars = NULL;
953d90bee97SLionel Sambuc INTON;
954d90bee97SLionel Sambuc if (setjmp(jmploc.loc)) {
955d90bee97SLionel Sambuc if (exception == EXSHELLPROC) {
956d90bee97SLionel Sambuc freeparam((volatile struct shparam *)
957d90bee97SLionel Sambuc &saveparam);
958d90bee97SLionel Sambuc } else {
959d90bee97SLionel Sambuc freeparam(&shellparam);
960d90bee97SLionel Sambuc shellparam = saveparam;
961d90bee97SLionel Sambuc }
962d90bee97SLionel Sambuc poplocalvars();
963d90bee97SLionel Sambuc localvars = savelocalvars;
964d90bee97SLionel Sambuc handler = savehandler;
965d90bee97SLionel Sambuc longjmp(handler->loc, 1);
966d90bee97SLionel Sambuc }
967d90bee97SLionel Sambuc savehandler = handler;
968d90bee97SLionel Sambuc handler = &jmploc;
969d90bee97SLionel Sambuc listmklocal(varlist.list, VEXPORT);
970d90bee97SLionel Sambuc /* stop shell blowing its stack */
971d90bee97SLionel Sambuc if (++funcnest > 1000)
972d90bee97SLionel Sambuc error("too many nested function calls");
973d90bee97SLionel Sambuc evaltree(cmdentry.u.func, flags & EV_TESTED);
974d90bee97SLionel Sambuc funcnest--;
975d90bee97SLionel Sambuc INTOFF;
976d90bee97SLionel Sambuc poplocalvars();
977d90bee97SLionel Sambuc localvars = savelocalvars;
978d90bee97SLionel Sambuc freeparam(&shellparam);
979d90bee97SLionel Sambuc shellparam = saveparam;
980d90bee97SLionel Sambuc handler = savehandler;
981d90bee97SLionel Sambuc popredir();
982d90bee97SLionel Sambuc INTON;
983d90bee97SLionel Sambuc if (evalskip == SKIPFUNC) {
984*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
985d90bee97SLionel Sambuc skipcount = 0;
986d90bee97SLionel Sambuc }
987d90bee97SLionel Sambuc if (flags & EV_EXIT)
988d90bee97SLionel Sambuc exitshell(exitstatus);
989d90bee97SLionel Sambuc break;
990d90bee97SLionel Sambuc
991d90bee97SLionel Sambuc case CMDBUILTIN:
992d90bee97SLionel Sambuc case CMDSPLBLTIN:
993d90bee97SLionel Sambuc #ifdef DEBUG
994d90bee97SLionel Sambuc trputs("builtin command: "); trargs(argv);
995d90bee97SLionel Sambuc #endif
996d90bee97SLionel Sambuc mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
997d90bee97SLionel Sambuc if (flags == EV_BACKCMD) {
998d90bee97SLionel Sambuc memout.nleft = 0;
999d90bee97SLionel Sambuc memout.nextc = memout.buf;
1000d90bee97SLionel Sambuc memout.bufsize = 64;
1001d90bee97SLionel Sambuc mode |= REDIR_BACKQ;
1002d90bee97SLionel Sambuc }
1003d90bee97SLionel Sambuc e = -1;
1004d90bee97SLionel Sambuc savehandler = handler;
1005d90bee97SLionel Sambuc savecmdname = commandname;
1006d90bee97SLionel Sambuc handler = &jmploc;
1007d90bee97SLionel Sambuc temp_path = 0;
1008d90bee97SLionel Sambuc if (!setjmp(jmploc.loc)) {
1009d90bee97SLionel Sambuc /* We need to ensure the command hash table isn't
1010d90bee97SLionel Sambuc * corruped by temporary PATH assignments.
1011d90bee97SLionel Sambuc * However we must ensure the 'local' command works!
1012d90bee97SLionel Sambuc */
1013d90bee97SLionel Sambuc if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
1014d90bee97SLionel Sambuc cmdentry.u.bltin == typecmd)) {
1015d90bee97SLionel Sambuc savelocalvars = localvars;
1016d90bee97SLionel Sambuc localvars = 0;
1017d90bee97SLionel Sambuc temp_path = 1;
1018d90bee97SLionel Sambuc mklocal(path - 5 /* PATH= */, 0);
1019d90bee97SLionel Sambuc }
1020d90bee97SLionel Sambuc redirect(cmd->ncmd.redirect, mode);
1021d90bee97SLionel Sambuc
1022d90bee97SLionel Sambuc /* exec is a special builtin, but needs this list... */
1023d90bee97SLionel Sambuc cmdenviron = varlist.list;
1024d90bee97SLionel Sambuc /* we must check 'readonly' flag for all builtins */
1025d90bee97SLionel Sambuc listsetvar(varlist.list,
1026d90bee97SLionel Sambuc cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
1027d90bee97SLionel Sambuc commandname = argv[0];
1028d90bee97SLionel Sambuc /* initialize nextopt */
1029d90bee97SLionel Sambuc argptr = argv + 1;
1030d90bee97SLionel Sambuc optptr = NULL;
1031d90bee97SLionel Sambuc /* and getopt */
1032d90bee97SLionel Sambuc optreset = 1;
1033d90bee97SLionel Sambuc optind = 1;
1034d90bee97SLionel Sambuc builtin_flags = flags;
1035d90bee97SLionel Sambuc exitstatus = cmdentry.u.bltin(argc, argv);
1036d90bee97SLionel Sambuc } else {
1037d90bee97SLionel Sambuc e = exception;
1038d90bee97SLionel Sambuc exitstatus = e == EXINT ? SIGINT + 128 :
1039d90bee97SLionel Sambuc e == EXEXEC ? exerrno : 2;
1040d90bee97SLionel Sambuc }
1041d90bee97SLionel Sambuc handler = savehandler;
1042d90bee97SLionel Sambuc flushall();
1043d90bee97SLionel Sambuc out1 = &output;
1044d90bee97SLionel Sambuc out2 = &errout;
1045d90bee97SLionel Sambuc freestdout();
1046d90bee97SLionel Sambuc if (temp_path) {
1047d90bee97SLionel Sambuc poplocalvars();
1048d90bee97SLionel Sambuc localvars = savelocalvars;
1049d90bee97SLionel Sambuc }
1050d90bee97SLionel Sambuc cmdenviron = NULL;
1051d90bee97SLionel Sambuc if (e != EXSHELLPROC) {
1052d90bee97SLionel Sambuc commandname = savecmdname;
1053d90bee97SLionel Sambuc if (flags & EV_EXIT)
1054d90bee97SLionel Sambuc exitshell(exitstatus);
1055d90bee97SLionel Sambuc }
1056d90bee97SLionel Sambuc if (e != -1) {
1057d90bee97SLionel Sambuc if ((e != EXERROR && e != EXEXEC)
1058d90bee97SLionel Sambuc || cmdentry.cmdtype == CMDSPLBLTIN)
1059d90bee97SLionel Sambuc exraise(e);
1060d90bee97SLionel Sambuc FORCEINTON;
1061d90bee97SLionel Sambuc }
1062d90bee97SLionel Sambuc if (cmdentry.u.bltin != execcmd)
1063d90bee97SLionel Sambuc popredir();
1064d90bee97SLionel Sambuc if (flags == EV_BACKCMD) {
1065d90bee97SLionel Sambuc backcmd->buf = memout.buf;
1066d90bee97SLionel Sambuc backcmd->nleft = memout.nextc - memout.buf;
1067d90bee97SLionel Sambuc memout.buf = NULL;
1068d90bee97SLionel Sambuc }
1069d90bee97SLionel Sambuc break;
1070d90bee97SLionel Sambuc
1071d90bee97SLionel Sambuc default:
1072d90bee97SLionel Sambuc #ifdef DEBUG
1073d90bee97SLionel Sambuc trputs("normal command: "); trargs(argv);
1074d90bee97SLionel Sambuc #endif
1075d90bee97SLionel Sambuc redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
1076d90bee97SLionel Sambuc if (!vforked)
1077d90bee97SLionel Sambuc for (sp = varlist.list ; sp ; sp = sp->next)
1078d90bee97SLionel Sambuc setvareq(sp->text, VEXPORT|VSTACK);
1079d90bee97SLionel Sambuc envp = environment();
1080d90bee97SLionel Sambuc shellexec(argv, envp, path, cmdentry.u.index, vforked);
1081d90bee97SLionel Sambuc break;
1082d90bee97SLionel Sambuc }
1083d90bee97SLionel Sambuc goto out;
1084d90bee97SLionel Sambuc
1085d90bee97SLionel Sambuc parent: /* parent process gets here (if we forked) */
1086d90bee97SLionel Sambuc if (mode == FORK_FG) { /* argument to fork */
1087d90bee97SLionel Sambuc exitstatus = waitforjob(jp);
1088d90bee97SLionel Sambuc } else if (mode == FORK_NOJOB) {
1089d90bee97SLionel Sambuc backcmd->fd = pip[0];
1090d90bee97SLionel Sambuc close(pip[1]);
1091d90bee97SLionel Sambuc backcmd->jp = jp;
1092d90bee97SLionel Sambuc }
1093d90bee97SLionel Sambuc FORCEINTON;
1094d90bee97SLionel Sambuc
1095d90bee97SLionel Sambuc out:
1096d90bee97SLionel Sambuc if (lastarg)
1097d90bee97SLionel Sambuc /* dsl: I think this is intended to be used to support
1098d90bee97SLionel Sambuc * '_' in 'vi' command mode during line editing...
1099d90bee97SLionel Sambuc * However I implemented that within libedit itself.
1100d90bee97SLionel Sambuc */
1101d90bee97SLionel Sambuc setvar("_", lastarg, 0);
1102d90bee97SLionel Sambuc popstackmark(&smark);
1103d90bee97SLionel Sambuc }
1104d90bee97SLionel Sambuc
1105d90bee97SLionel Sambuc
1106d90bee97SLionel Sambuc /*
1107d90bee97SLionel Sambuc * Search for a command. This is called before we fork so that the
1108d90bee97SLionel Sambuc * location of the command will be available in the parent as well as
1109d90bee97SLionel Sambuc * the child. The check for "goodname" is an overly conservative
1110d90bee97SLionel Sambuc * check that the name will not be subject to expansion.
1111d90bee97SLionel Sambuc */
1112d90bee97SLionel Sambuc
1113d90bee97SLionel Sambuc STATIC void
prehash(union node * n)1114d90bee97SLionel Sambuc prehash(union node *n)
1115d90bee97SLionel Sambuc {
1116d90bee97SLionel Sambuc struct cmdentry entry;
1117d90bee97SLionel Sambuc
1118d90bee97SLionel Sambuc if (n && n->type == NCMD && n->ncmd.args)
1119d90bee97SLionel Sambuc if (goodname(n->ncmd.args->narg.text))
1120d90bee97SLionel Sambuc find_command(n->ncmd.args->narg.text, &entry, 0,
1121d90bee97SLionel Sambuc pathval());
1122d90bee97SLionel Sambuc }
1123d90bee97SLionel Sambuc
1124*0a6a1f1dSLionel Sambuc STATIC int
in_function(void)1125*0a6a1f1dSLionel Sambuc in_function(void)
1126*0a6a1f1dSLionel Sambuc {
1127*0a6a1f1dSLionel Sambuc return funcnest;
1128*0a6a1f1dSLionel Sambuc }
1129d90bee97SLionel Sambuc
1130*0a6a1f1dSLionel Sambuc STATIC enum skipstate
current_skipstate(void)1131*0a6a1f1dSLionel Sambuc current_skipstate(void)
1132*0a6a1f1dSLionel Sambuc {
1133*0a6a1f1dSLionel Sambuc return evalskip;
1134*0a6a1f1dSLionel Sambuc }
1135*0a6a1f1dSLionel Sambuc
1136*0a6a1f1dSLionel Sambuc STATIC void
stop_skipping(void)1137*0a6a1f1dSLionel Sambuc stop_skipping(void)
1138*0a6a1f1dSLionel Sambuc {
1139*0a6a1f1dSLionel Sambuc evalskip = SKIPNONE;
1140*0a6a1f1dSLionel Sambuc skipcount = 0;
1141*0a6a1f1dSLionel Sambuc }
1142d90bee97SLionel Sambuc
1143d90bee97SLionel Sambuc /*
1144d90bee97SLionel Sambuc * Builtin commands. Builtin commands whose functions are closely
1145d90bee97SLionel Sambuc * tied to evaluation are implemented here.
1146d90bee97SLionel Sambuc */
1147d90bee97SLionel Sambuc
1148d90bee97SLionel Sambuc /*
1149d90bee97SLionel Sambuc * No command given.
1150d90bee97SLionel Sambuc */
1151d90bee97SLionel Sambuc
1152d90bee97SLionel Sambuc int
bltincmd(int argc,char ** argv)1153d90bee97SLionel Sambuc bltincmd(int argc, char **argv)
1154d90bee97SLionel Sambuc {
1155d90bee97SLionel Sambuc /*
1156d90bee97SLionel Sambuc * Preserve exitstatus of a previous possible redirection
1157d90bee97SLionel Sambuc * as POSIX mandates
1158d90bee97SLionel Sambuc */
1159d90bee97SLionel Sambuc return back_exitstatus;
1160d90bee97SLionel Sambuc }
1161d90bee97SLionel Sambuc
1162d90bee97SLionel Sambuc
1163d90bee97SLionel Sambuc /*
1164d90bee97SLionel Sambuc * Handle break and continue commands. Break, continue, and return are
1165d90bee97SLionel Sambuc * all handled by setting the evalskip flag. The evaluation routines
1166d90bee97SLionel Sambuc * above all check this flag, and if it is set they start skipping
1167d90bee97SLionel Sambuc * commands rather than executing them. The variable skipcount is
1168d90bee97SLionel Sambuc * the number of loops to break/continue, or the number of function
1169d90bee97SLionel Sambuc * levels to return. (The latter is always 1.) It should probably
1170d90bee97SLionel Sambuc * be an error to break out of more loops than exist, but it isn't
1171d90bee97SLionel Sambuc * in the standard shell so we don't make it one here.
1172d90bee97SLionel Sambuc */
1173d90bee97SLionel Sambuc
1174d90bee97SLionel Sambuc int
breakcmd(int argc,char ** argv)1175d90bee97SLionel Sambuc breakcmd(int argc, char **argv)
1176d90bee97SLionel Sambuc {
1177d90bee97SLionel Sambuc int n = argc > 1 ? number(argv[1]) : 1;
1178d90bee97SLionel Sambuc
1179d90bee97SLionel Sambuc if (n > loopnest)
1180d90bee97SLionel Sambuc n = loopnest;
1181d90bee97SLionel Sambuc if (n > 0) {
1182d90bee97SLionel Sambuc evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1183d90bee97SLionel Sambuc skipcount = n;
1184d90bee97SLionel Sambuc }
1185d90bee97SLionel Sambuc return 0;
1186d90bee97SLionel Sambuc }
1187d90bee97SLionel Sambuc
1188*0a6a1f1dSLionel Sambuc int
dotcmd(int argc,char ** argv)1189*0a6a1f1dSLionel Sambuc dotcmd(int argc, char **argv)
1190*0a6a1f1dSLionel Sambuc {
1191*0a6a1f1dSLionel Sambuc exitstatus = 0;
1192*0a6a1f1dSLionel Sambuc
1193*0a6a1f1dSLionel Sambuc if (argc >= 2) { /* That's what SVR2 does */
1194*0a6a1f1dSLionel Sambuc char *fullname;
1195*0a6a1f1dSLionel Sambuc /*
1196*0a6a1f1dSLionel Sambuc * dot_funcnest needs to be 0 when not in a dotcmd, so it
1197*0a6a1f1dSLionel Sambuc * cannot be restored with (funcnest + 1).
1198*0a6a1f1dSLionel Sambuc */
1199*0a6a1f1dSLionel Sambuc int dot_funcnest_old;
1200*0a6a1f1dSLionel Sambuc struct stackmark smark;
1201*0a6a1f1dSLionel Sambuc
1202*0a6a1f1dSLionel Sambuc setstackmark(&smark);
1203*0a6a1f1dSLionel Sambuc fullname = find_dot_file(argv[1]);
1204*0a6a1f1dSLionel Sambuc setinputfile(fullname, 1);
1205*0a6a1f1dSLionel Sambuc commandname = fullname;
1206*0a6a1f1dSLionel Sambuc dot_funcnest_old = dot_funcnest;
1207*0a6a1f1dSLionel Sambuc dot_funcnest = funcnest + 1;
1208*0a6a1f1dSLionel Sambuc cmdloop(0);
1209*0a6a1f1dSLionel Sambuc dot_funcnest = dot_funcnest_old;
1210*0a6a1f1dSLionel Sambuc popfile();
1211*0a6a1f1dSLionel Sambuc popstackmark(&smark);
1212*0a6a1f1dSLionel Sambuc }
1213*0a6a1f1dSLionel Sambuc return exitstatus;
1214*0a6a1f1dSLionel Sambuc }
1215*0a6a1f1dSLionel Sambuc
1216*0a6a1f1dSLionel Sambuc /*
1217*0a6a1f1dSLionel Sambuc * Take commands from a file. To be compatible we should do a path
1218*0a6a1f1dSLionel Sambuc * search for the file, which is necessary to find sub-commands.
1219*0a6a1f1dSLionel Sambuc */
1220*0a6a1f1dSLionel Sambuc
1221*0a6a1f1dSLionel Sambuc STATIC char *
find_dot_file(char * basename)1222*0a6a1f1dSLionel Sambuc find_dot_file(char *basename)
1223*0a6a1f1dSLionel Sambuc {
1224*0a6a1f1dSLionel Sambuc char *fullname;
1225*0a6a1f1dSLionel Sambuc const char *path = pathval();
1226*0a6a1f1dSLionel Sambuc struct stat statb;
1227*0a6a1f1dSLionel Sambuc
1228*0a6a1f1dSLionel Sambuc /* don't try this for absolute or relative paths */
1229*0a6a1f1dSLionel Sambuc if (strchr(basename, '/'))
1230*0a6a1f1dSLionel Sambuc return basename;
1231*0a6a1f1dSLionel Sambuc
1232*0a6a1f1dSLionel Sambuc while ((fullname = padvance(&path, basename)) != NULL) {
1233*0a6a1f1dSLionel Sambuc if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
1234*0a6a1f1dSLionel Sambuc /*
1235*0a6a1f1dSLionel Sambuc * Don't bother freeing here, since it will
1236*0a6a1f1dSLionel Sambuc * be freed by the caller.
1237*0a6a1f1dSLionel Sambuc */
1238*0a6a1f1dSLionel Sambuc return fullname;
1239*0a6a1f1dSLionel Sambuc }
1240*0a6a1f1dSLionel Sambuc stunalloc(fullname);
1241*0a6a1f1dSLionel Sambuc }
1242*0a6a1f1dSLionel Sambuc
1243*0a6a1f1dSLionel Sambuc /* not found in the PATH */
1244*0a6a1f1dSLionel Sambuc error("%s: not found", basename);
1245*0a6a1f1dSLionel Sambuc /* NOTREACHED */
1246*0a6a1f1dSLionel Sambuc }
1247*0a6a1f1dSLionel Sambuc
1248*0a6a1f1dSLionel Sambuc
1249d90bee97SLionel Sambuc
1250d90bee97SLionel Sambuc /*
1251d90bee97SLionel Sambuc * The return command.
1252*0a6a1f1dSLionel Sambuc *
1253*0a6a1f1dSLionel Sambuc * Quoth the POSIX standard:
1254*0a6a1f1dSLionel Sambuc * The return utility shall cause the shell to stop executing the current
1255*0a6a1f1dSLionel Sambuc * function or dot script. If the shell is not currently executing
1256*0a6a1f1dSLionel Sambuc * a function or dot script, the results are unspecified.
1257*0a6a1f1dSLionel Sambuc *
1258*0a6a1f1dSLionel Sambuc * As for the unspecified part, there seems to be no de-facto standard: bash
1259*0a6a1f1dSLionel Sambuc * ignores the return with a warning, zsh ignores the return in interactive
1260*0a6a1f1dSLionel Sambuc * mode but seems to liken it to exit in a script. (checked May 2014)
1261*0a6a1f1dSLionel Sambuc *
1262*0a6a1f1dSLionel Sambuc * We choose to silently ignore the return. Older versions of this shell
1263*0a6a1f1dSLionel Sambuc * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This
1264*0a6a1f1dSLionel Sambuc * had at least the problem of circumventing the check for stopped jobs,
1265*0a6a1f1dSLionel Sambuc * which would occur for exit or ^D.
1266d90bee97SLionel Sambuc */
1267d90bee97SLionel Sambuc
1268d90bee97SLionel Sambuc int
returncmd(int argc,char ** argv)1269d90bee97SLionel Sambuc returncmd(int argc, char **argv)
1270d90bee97SLionel Sambuc {
1271d90bee97SLionel Sambuc int ret = argc > 1 ? number(argv[1]) : exitstatus;
1272d90bee97SLionel Sambuc
1273*0a6a1f1dSLionel Sambuc if ((dot_funcnest == 0 && funcnest)
1274*0a6a1f1dSLionel Sambuc || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) {
1275d90bee97SLionel Sambuc evalskip = SKIPFUNC;
1276d90bee97SLionel Sambuc skipcount = 1;
1277*0a6a1f1dSLionel Sambuc } else if (dot_funcnest > 0) {
1278d90bee97SLionel Sambuc evalskip = SKIPFILE;
1279d90bee97SLionel Sambuc skipcount = 1;
1280*0a6a1f1dSLionel Sambuc } else {
1281*0a6a1f1dSLionel Sambuc /* XXX: should a warning be issued? */
1282*0a6a1f1dSLionel Sambuc ret = 0;
1283d90bee97SLionel Sambuc }
1284*0a6a1f1dSLionel Sambuc
1285*0a6a1f1dSLionel Sambuc return ret;
1286d90bee97SLionel Sambuc }
1287d90bee97SLionel Sambuc
1288d90bee97SLionel Sambuc
1289d90bee97SLionel Sambuc int
falsecmd(int argc,char ** argv)1290d90bee97SLionel Sambuc falsecmd(int argc, char **argv)
1291d90bee97SLionel Sambuc {
1292d90bee97SLionel Sambuc return 1;
1293d90bee97SLionel Sambuc }
1294d90bee97SLionel Sambuc
1295d90bee97SLionel Sambuc
1296d90bee97SLionel Sambuc int
truecmd(int argc,char ** argv)1297d90bee97SLionel Sambuc truecmd(int argc, char **argv)
1298d90bee97SLionel Sambuc {
1299d90bee97SLionel Sambuc return 0;
1300d90bee97SLionel Sambuc }
1301d90bee97SLionel Sambuc
1302d90bee97SLionel Sambuc
1303d90bee97SLionel Sambuc int
execcmd(int argc,char ** argv)1304d90bee97SLionel Sambuc execcmd(int argc, char **argv)
1305d90bee97SLionel Sambuc {
1306d90bee97SLionel Sambuc if (argc > 1) {
1307d90bee97SLionel Sambuc struct strlist *sp;
1308d90bee97SLionel Sambuc
1309d90bee97SLionel Sambuc iflag = 0; /* exit on error */
1310d90bee97SLionel Sambuc mflag = 0;
1311d90bee97SLionel Sambuc optschanged();
1312d90bee97SLionel Sambuc for (sp = cmdenviron; sp; sp = sp->next)
1313d90bee97SLionel Sambuc setvareq(sp->text, VEXPORT|VSTACK);
1314d90bee97SLionel Sambuc shellexec(argv + 1, environment(), pathval(), 0, 0);
1315d90bee97SLionel Sambuc }
1316d90bee97SLionel Sambuc return 0;
1317d90bee97SLionel Sambuc }
1318d90bee97SLionel Sambuc
1319d90bee97SLionel Sambuc static int
conv_time(clock_t ticks,char * seconds,size_t l)1320d90bee97SLionel Sambuc conv_time(clock_t ticks, char *seconds, size_t l)
1321d90bee97SLionel Sambuc {
1322d90bee97SLionel Sambuc static clock_t tpm = 0;
1323d90bee97SLionel Sambuc clock_t mins;
1324d90bee97SLionel Sambuc int i;
1325d90bee97SLionel Sambuc
1326d90bee97SLionel Sambuc if (!tpm)
1327d90bee97SLionel Sambuc tpm = sysconf(_SC_CLK_TCK) * 60;
1328d90bee97SLionel Sambuc
1329d90bee97SLionel Sambuc mins = ticks / tpm;
1330d90bee97SLionel Sambuc snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1331d90bee97SLionel Sambuc
1332d90bee97SLionel Sambuc if (seconds[0] == '6' && seconds[1] == '0') {
1333d90bee97SLionel Sambuc /* 59.99995 got rounded up... */
1334d90bee97SLionel Sambuc mins++;
1335d90bee97SLionel Sambuc strlcpy(seconds, "0.0", l);
1336d90bee97SLionel Sambuc return mins;
1337d90bee97SLionel Sambuc }
1338d90bee97SLionel Sambuc
1339d90bee97SLionel Sambuc /* suppress trailing zeros */
1340d90bee97SLionel Sambuc i = strlen(seconds) - 1;
1341d90bee97SLionel Sambuc for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1342d90bee97SLionel Sambuc seconds[i] = 0;
1343d90bee97SLionel Sambuc return mins;
1344d90bee97SLionel Sambuc }
1345d90bee97SLionel Sambuc
1346d90bee97SLionel Sambuc int
timescmd(int argc,char ** argv)1347d90bee97SLionel Sambuc timescmd(int argc, char **argv)
1348d90bee97SLionel Sambuc {
1349d90bee97SLionel Sambuc struct tms tms;
1350d90bee97SLionel Sambuc int u, s, cu, cs;
1351d90bee97SLionel Sambuc char us[8], ss[8], cus[8], css[8];
1352d90bee97SLionel Sambuc
1353d90bee97SLionel Sambuc nextopt("");
1354d90bee97SLionel Sambuc
1355d90bee97SLionel Sambuc times(&tms);
1356d90bee97SLionel Sambuc
1357d90bee97SLionel Sambuc u = conv_time(tms.tms_utime, us, sizeof(us));
1358d90bee97SLionel Sambuc s = conv_time(tms.tms_stime, ss, sizeof(ss));
1359d90bee97SLionel Sambuc cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1360d90bee97SLionel Sambuc cs = conv_time(tms.tms_cstime, css, sizeof(css));
1361d90bee97SLionel Sambuc
1362d90bee97SLionel Sambuc outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1363d90bee97SLionel Sambuc u, us, s, ss, cu, cus, cs, css);
1364d90bee97SLionel Sambuc
1365d90bee97SLionel Sambuc return 0;
1366d90bee97SLionel Sambuc }
1367