Sat Aug 25 02:42:49 2018 UTC ()
PR bin/53548

Deal with the new shell internal exit reason EXEXIT in the case of
a shell which has vfork()'d.   It takes a peculiar set of circumstances
to get into a situation where this is ever relevant, but it can be
done.   See the PR for details.


(kre)
diff -r1.160 -r1.161 src/bin/sh/eval.c

cvs diff -r1.160 -r1.161 src/bin/sh/eval.c (switch to unified diff)

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