Sat Aug 25 14:48:22 2018 UTC ()
Pull up following revision(s) (requested by kre in ticket #989):

	bin/sh/eval.c: revision 1.156
	bin/sh/eval.h: revision 1.20
	bin/sh/exec.c: revision 1.53

Fix several bugs in the command / type builtin ( including PR bin/48499 )

1. Make command -pv (and -pV) work (which is not as easy as the PR
   suggests it might be (the "check and cause error" was there because
   it did not work, not in order to prevent it from working).

2. Stop -v and -V being both used (that makes no sense).

3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
   (which it did, as when -v -or -V is used with command, it and type are
    implemented using the same code).

4. make "command -v word" DTRT for sh keywords (was treating them as an error).

5. Require at least one arg for "command -[vV]" or "type" else usage & error.
   Strictly this should also apply to "command" and "command -p" (no -v)
   but that's handled elsewhere, so perhaps some other time.   Perhaps
   "command -v" (and -V) should be limited to 1 command name (where "type"
   can have many) as in the POSIX definitions, but I don't think that matters.

6. With "command -V alias", (or "type alias" which is the same thing),
   (but not "command -v alias") alter the output format, so we get
	ll is an alias for: ls -al
   instead of the old
	ll is an alias for
	ls -al
   (and note there was a space, for some reason, after "for")
   That is, unless the alias value contains any \n characters, in which
   case (something approximating) the old multi-line format is retained.
   Also note: that if code wants to parse/use the value of an alias, it
   should be using the output of "alias name", not command or type.

Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".

Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c


(martin)
diff -r1.140.2.5 -r1.140.2.6 src/bin/sh/eval.c
diff -r1.19.8.1 -r1.19.8.2 src/bin/sh/eval.h
diff -r1.47.2.3 -r1.47.2.4 src/bin/sh/exec.c

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

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

cvs diff -r1.19.8.1 -r1.19.8.2 src/bin/sh/eval.h (switch to unified diff)

--- src/bin/sh/eval.h 2018/08/25 14:22:49 1.19.8.1
+++ src/bin/sh/eval.h 2018/08/25 14:48:22 1.19.8.2
@@ -1,78 +1,80 @@ @@ -1,78 +1,80 @@
1/* $NetBSD: eval.h,v 1.19.8.1 2018/08/25 14:22:49 martin Exp $ */ 1/* $NetBSD: eval.h,v 1.19.8.2 2018/08/25 14:48:22 martin Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1991, 1993 4 * Copyright (c) 1991, 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 * @(#)eval.h 8.2 (Berkeley) 5/4/95 34 * @(#)eval.h 8.2 (Berkeley) 5/4/95
35 */ 35 */
36 36
37extern const char *commandname; /* currently executing command */ 37extern const char *commandname; /* currently executing command */
38extern int exitstatus; /* exit status of last command */ 38extern int exitstatus; /* exit status of last command */
39extern int back_exitstatus; /* exit status of backquoted command */ 39extern int back_exitstatus; /* exit status of backquoted command */
40extern struct strlist *cmdenviron; /* environment for builtin command */ 40extern struct strlist *cmdenviron; /* environment for builtin command */
41 41
42 42
43struct backcmd { /* result of evalbackcmd */ 43struct backcmd { /* result of evalbackcmd */
44 int fd; /* file descriptor to read from */ 44 int fd; /* file descriptor to read from */
45 char *buf; /* buffer */ 45 char *buf; /* buffer */
46 int nleft; /* number of chars in buffer */ 46 int nleft; /* number of chars in buffer */
47 struct job *jp; /* job structure for command */ 47 struct job *jp; /* job structure for command */
48}; 48};
49 49
50void evalstring(char *, int); 50void evalstring(char *, int);
51union node; /* BLETCH for ansi C */ 51union node; /* BLETCH for ansi C */
52void evaltree(union node *, int); 52void evaltree(union node *, int);
53void evalbackcmd(union node *, struct backcmd *); 53void evalbackcmd(union node *, struct backcmd *);
54 54
 55const char *syspath(void);
 56
55/* in_function returns nonzero if we are currently evaluating a function */ 57/* in_function returns nonzero if we are currently evaluating a function */
56int in_function(void); /* return non-zero, if evaluating a function */ 58int in_function(void); /* return non-zero, if evaluating a function */
57 59
58/* reasons for skipping commands (see comment on breakcmd routine) */ 60/* reasons for skipping commands (see comment on breakcmd routine) */
59enum skipstate { 61enum skipstate {
60 SKIPNONE = 0, /* not skipping */ 62 SKIPNONE = 0, /* not skipping */
61 SKIPBREAK, /* break */ 63 SKIPBREAK, /* break */
62 SKIPCONT, /* continue */ 64 SKIPCONT, /* continue */
63 SKIPFUNC, /* return in a function */ 65 SKIPFUNC, /* return in a function */
64 SKIPFILE /* return in a dot command */ 66 SKIPFILE /* return in a dot command */
65}; 67};
66 68
67enum skipstate current_skipstate(void); 69enum skipstate current_skipstate(void);
68void stop_skipping(void); /* reset internal skipping state to SKIPNONE */ 70void stop_skipping(void); /* reset internal skipping state to SKIPNONE */
69 71
70/* 72/*
71 * Only for use by reset() in init.c! 73 * Only for use by reset() in init.c!
72 */ 74 */
73void reset_eval(void); 75void reset_eval(void);
74 76
75/* flags in argument to evaltree */ 77/* flags in argument to evaltree */
76#define EV_EXIT 0x01 /* exit after evaluating tree */ 78#define EV_EXIT 0x01 /* exit after evaluating tree */
77#define EV_TESTED 0x02 /* exit status is checked; ignore -e flag */ 79#define EV_TESTED 0x02 /* exit status is checked; ignore -e flag */
78#define EV_BACKCMD 0x04 /* command executing within back quotes */ 80#define EV_BACKCMD 0x04 /* command executing within back quotes */

cvs diff -r1.47.2.3 -r1.47.2.4 src/bin/sh/exec.c (switch to unified diff)

--- src/bin/sh/exec.c 2018/07/13 14:29:15 1.47.2.3
+++ src/bin/sh/exec.c 2018/08/25 14:48:22 1.47.2.4
@@ -1,1161 +1,1183 @@ @@ -1,1161 +1,1183 @@
1/* $NetBSD: exec.c,v 1.47.2.3 2018/07/13 14:29:15 martin Exp $ */ 1/* $NetBSD: exec.c,v 1.47.2.4 2018/08/25 14:48:22 martin Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1991, 1993 4 * Copyright (c) 1991, 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[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 38static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
39#else 39#else
40__RCSID("$NetBSD: exec.c,v 1.47.2.3 2018/07/13 14:29:15 martin Exp $"); 40__RCSID("$NetBSD: exec.c,v 1.47.2.4 2018/08/25 14:48:22 martin Exp $");
41#endif 41#endif
42#endif /* not lint */ 42#endif /* not lint */
43 43
44#include <sys/types.h> 44#include <sys/types.h>
45#include <sys/stat.h> 45#include <sys/stat.h>
46#include <sys/wait.h> 46#include <sys/wait.h>
47#include <unistd.h> 47#include <unistd.h>
48#include <fcntl.h> 48#include <fcntl.h>
49#include <errno.h> 49#include <errno.h>
50#include <stdio.h> 50#include <stdio.h>
51#include <stdlib.h> 51#include <stdlib.h>
52 52
53/* 53/*
54 * When commands are first encountered, they are entered in a hash table. 54 * When commands are first encountered, they are entered in a hash table.
55 * This ensures that a full path search will not have to be done for them 55 * This ensures that a full path search will not have to be done for them
56 * on each invocation. 56 * on each invocation.
57 * 57 *
58 * We should investigate converting to a linear search, even though that 58 * We should investigate converting to a linear search, even though that
59 * would make the command name "hash" a misnomer. 59 * would make the command name "hash" a misnomer.
60 */ 60 */
61 61
62#include "shell.h" 62#include "shell.h"
63#include "main.h" 63#include "main.h"
64#include "nodes.h" 64#include "nodes.h"
65#include "parser.h" 65#include "parser.h"
66#include "redir.h" 66#include "redir.h"
67#include "eval.h" 67#include "eval.h"
68#include "exec.h" 68#include "exec.h"
69#include "builtins.h" 69#include "builtins.h"
70#include "var.h" 70#include "var.h"
71#include "options.h" 71#include "options.h"
72#include "input.h" 72#include "input.h"
73#include "output.h" 73#include "output.h"
74#include "syntax.h" 74#include "syntax.h"
75#include "memalloc.h" 75#include "memalloc.h"
76#include "error.h" 76#include "error.h"
77#include "init.h" 77#include "init.h"
78#include "mystring.h" 78#include "mystring.h"
79#include "show.h" 79#include "show.h"
80#include "jobs.h" 80#include "jobs.h"
81#include "alias.h" 81#include "alias.h"
82 82
83 83
84#define CMDTABLESIZE 31 /* should be prime */ 84#define CMDTABLESIZE 31 /* should be prime */
85#define ARB 1 /* actual size determined at run time */ 85#define ARB 1 /* actual size determined at run time */
86 86
87 87
88 88
89struct tblentry { 89struct tblentry {
90 struct tblentry *next; /* next entry in hash chain */ 90 struct tblentry *next; /* next entry in hash chain */
91 union param param; /* definition of builtin function */ 91 union param param; /* definition of builtin function */
92 short cmdtype; /* index identifying command */ 92 short cmdtype; /* index identifying command */
93 char rehash; /* if set, cd done since entry created */ 93 char rehash; /* if set, cd done since entry created */
94 char fn_ln1; /* for functions, LINENO from 1 */ 94 char fn_ln1; /* for functions, LINENO from 1 */
95 int lineno; /* for functions abs LINENO of definition */ 95 int lineno; /* for functions abs LINENO of definition */
96 char cmdname[ARB]; /* name of command */ 96 char cmdname[ARB]; /* name of command */
97}; 97};
98 98
99 99
100STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 100STATIC struct tblentry *cmdtable[CMDTABLESIZE];
101STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 101STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
102int exerrno = 0; /* Last exec error */ 102int exerrno = 0; /* Last exec error */
103 103
104 104
105STATIC void tryexec(char *, char **, char **, int); 105STATIC void tryexec(char *, char **, char **, int);
106STATIC void printentry(struct tblentry *, int); 106STATIC void printentry(struct tblentry *, int);
107STATIC void addcmdentry(char *, struct cmdentry *); 107STATIC void addcmdentry(char *, struct cmdentry *);
108STATIC void clearcmdentry(int); 108STATIC void clearcmdentry(int);
109STATIC struct tblentry *cmdlookup(const char *, int); 109STATIC struct tblentry *cmdlookup(const char *, int);
110STATIC void delete_cmd_entry(void); 110STATIC void delete_cmd_entry(void);
111 111
112#ifndef BSD 112#ifndef BSD
113STATIC void execinterp(char **, char **); 113STATIC void execinterp(char **, char **);
114#endif 114#endif
115 115
116 116
117extern const char *const parsekwd[]; 117extern const char *const parsekwd[];
118 118
119/* 119/*
120 * Exec a program. Never returns. If you change this routine, you may 120 * Exec a program. Never returns. If you change this routine, you may
121 * have to change the find_command routine as well. 121 * have to change the find_command routine as well.
122 */ 122 */
123 123
124void 124void
125shellexec(char **argv, char **envp, const char *path, int idx, int vforked) 125shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
126{ 126{
127 char *cmdname; 127 char *cmdname;
128 int e; 128 int e;
129 129
130 if (strchr(argv[0], '/') != NULL) { 130 if (strchr(argv[0], '/') != NULL) {
131 tryexec(argv[0], argv, envp, vforked); 131 tryexec(argv[0], argv, envp, vforked);
132 e = errno; 132 e = errno;
133 } else { 133 } else {
134 e = ENOENT; 134 e = ENOENT;
135 while ((cmdname = padvance(&path, argv[0], 1)) != NULL) { 135 while ((cmdname = padvance(&path, argv[0], 1)) != NULL) {
136 if (--idx < 0 && pathopt == NULL) { 136 if (--idx < 0 && pathopt == NULL) {
137 tryexec(cmdname, argv, envp, vforked); 137 tryexec(cmdname, argv, envp, vforked);
138 if (errno != ENOENT && errno != ENOTDIR) 138 if (errno != ENOENT && errno != ENOTDIR)
139 e = errno; 139 e = errno;
140 } 140 }
141 stunalloc(cmdname); 141 stunalloc(cmdname);
142 } 142 }
143 } 143 }
144 144
145 /* Map to POSIX errors */ 145 /* Map to POSIX errors */
146 switch (e) { 146 switch (e) {
147 case EACCES: /* particularly this (unless no search perm) */ 147 case EACCES: /* particularly this (unless no search perm) */
148 /* 148 /*
149 * should perhaps check if this EACCES is an exec() 149 * should perhaps check if this EACCES is an exec()
150 * EACESS or a namei() EACESS - the latter should be 127 150 * EACESS or a namei() EACESS - the latter should be 127
151 * - but not today 151 * - but not today
152 */ 152 */
153 case EINVAL: /* also explicitly these */ 153 case EINVAL: /* also explicitly these */
154 case ENOEXEC: 154 case ENOEXEC:
155 default: /* and anything else */ 155 default: /* and anything else */
156 exerrno = 126; 156 exerrno = 126;
157 break; 157 break;
158 158
159 case ENOENT: /* these are the "pathname lookup failed" errors */ 159 case ENOENT: /* these are the "pathname lookup failed" errors */
160 case ELOOP: 160 case ELOOP:
161 case ENOTDIR: 161 case ENOTDIR:
162 case ENAMETOOLONG: 162 case ENAMETOOLONG:
163 exerrno = 127; 163 exerrno = 127;
164 break; 164 break;
165 } 165 }
166 CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL, 166 CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL,
167 ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", 167 ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
168 argv[0], e, vforked, suppressint)); 168 argv[0], e, vforked, suppressint));
169 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); 169 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
170 /* NOTREACHED */ 170 /* NOTREACHED */
171} 171}
172 172
173 173
174STATIC void 174STATIC void
175tryexec(char *cmd, char **argv, char **envp, int vforked) 175tryexec(char *cmd, char **argv, char **envp, int vforked)
176{ 176{
177 int e; 177 int e;
178#ifndef BSD 178#ifndef BSD
179 char *p; 179 char *p;
180#endif 180#endif
181 181
182#ifdef SYSV 182#ifdef SYSV
183 do { 183 do {
184 execve(cmd, argv, envp); 184 execve(cmd, argv, envp);
185 } while (errno == EINTR); 185 } while (errno == EINTR);
186#else 186#else
187 execve(cmd, argv, envp); 187 execve(cmd, argv, envp);
188#endif 188#endif
189 e = errno; 189 e = errno;
190 if (e == ENOEXEC) { 190 if (e == ENOEXEC) {
191 if (vforked) { 191 if (vforked) {
192 /* We are currently vfork(2)ed, so raise an 192 /* We are currently vfork(2)ed, so raise an
193 * exception, and evalcommand will try again 193 * exception, and evalcommand will try again
194 * with a normal fork(2). 194 * with a normal fork(2).
195 */ 195 */
196 exraise(EXSHELLPROC); 196 exraise(EXSHELLPROC);
197 } 197 }
198#ifdef DEBUG 198#ifdef DEBUG
199 VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd)); 199 VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd));
200#endif 200#endif
201 initshellproc(); 201 initshellproc();
202 setinputfile(cmd, 0); 202 setinputfile(cmd, 0);
203 commandname = arg0 = savestr(argv[0]); 203 commandname = arg0 = savestr(argv[0]);
204#ifndef BSD 204#ifndef BSD
205 pgetc(); pungetc(); /* fill up input buffer */ 205 pgetc(); pungetc(); /* fill up input buffer */
206 p = parsenextc; 206 p = parsenextc;
207 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 207 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
208 argv[0] = cmd; 208 argv[0] = cmd;
209 execinterp(argv, envp); 209 execinterp(argv, envp);
210 } 210 }
211#endif 211#endif
212 setparam(argv + 1); 212 setparam(argv + 1);
213 exraise(EXSHELLPROC); 213 exraise(EXSHELLPROC);
214 } 214 }
215 errno = e; 215 errno = e;
216} 216}
217 217
218 218
219#ifndef BSD 219#ifndef BSD
220/* 220/*
221 * Execute an interpreter introduced by "#!", for systems where this 221 * Execute an interpreter introduced by "#!", for systems where this
222 * feature has not been built into the kernel. If the interpreter is 222 * feature has not been built into the kernel. If the interpreter is
223 * the shell, return (effectively ignoring the "#!"). If the execution 223 * the shell, return (effectively ignoring the "#!"). If the execution
224 * of the interpreter fails, exit. 224 * of the interpreter fails, exit.
225 * 225 *
226 * This code peeks inside the input buffer in order to avoid actually 226 * This code peeks inside the input buffer in order to avoid actually
227 * reading any input. It would benefit from a rewrite. 227 * reading any input. It would benefit from a rewrite.
228 */ 228 */
229 229
230#define NEWARGS 5 230#define NEWARGS 5
231 231
232STATIC void 232STATIC void
233execinterp(char **argv, char **envp) 233execinterp(char **argv, char **envp)
234{ 234{
235 int n; 235 int n;
236 char *inp; 236 char *inp;
237 char *outp; 237 char *outp;
238 char c; 238 char c;
239 char *p; 239 char *p;
240 char **ap; 240 char **ap;
241 char *newargs[NEWARGS]; 241 char *newargs[NEWARGS];
242 int i; 242 int i;
243 char **ap2; 243 char **ap2;
244 char **new; 244 char **new;
245 245
246 n = parsenleft - 2; 246 n = parsenleft - 2;
247 inp = parsenextc + 2; 247 inp = parsenextc + 2;
248 ap = newargs; 248 ap = newargs;
249 for (;;) { 249 for (;;) {
250 while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 250 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
251 inp++; 251 inp++;
252 if (n < 0) 252 if (n < 0)
253 goto bad; 253 goto bad;
254 if ((c = *inp++) == '\n') 254 if ((c = *inp++) == '\n')
255 break; 255 break;
256 if (ap == &newargs[NEWARGS]) 256 if (ap == &newargs[NEWARGS])
257bad: error("Bad #! line"); 257bad: error("Bad #! line");
258 STARTSTACKSTR(outp); 258 STARTSTACKSTR(outp);
259 do { 259 do {
260 STPUTC(c, outp); 260 STPUTC(c, outp);
261 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 261 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
262 STPUTC('\0', outp); 262 STPUTC('\0', outp);
263 n++, inp--; 263 n++, inp--;
264 *ap++ = grabstackstr(outp); 264 *ap++ = grabstackstr(outp);
265 } 265 }
266 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 266 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
267 p = newargs[0]; 267 p = newargs[0];
268 for (;;) { 268 for (;;) {
269 if (equal(p, "sh") || equal(p, "ash")) { 269 if (equal(p, "sh") || equal(p, "ash")) {
270 return; 270 return;
271 } 271 }
272 while (*p != '/') { 272 while (*p != '/') {
273 if (*p == '\0') 273 if (*p == '\0')
274 goto break2; 274 goto break2;
275 p++; 275 p++;
276 } 276 }
277 p++; 277 p++;
278 } 278 }
279break2:; 279break2:;
280 } 280 }
281 i = (char *)ap - (char *)newargs; /* size in bytes */ 281 i = (char *)ap - (char *)newargs; /* size in bytes */
282 if (i == 0) 282 if (i == 0)
283 error("Bad #! line"); 283 error("Bad #! line");
284 for (ap2 = argv ; *ap2++ != NULL ; ); 284 for (ap2 = argv ; *ap2++ != NULL ; );
285 new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 285 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
286 ap = newargs, ap2 = new; 286 ap = newargs, ap2 = new;
287 while ((i -= sizeof (char **)) >= 0) 287 while ((i -= sizeof (char **)) >= 0)
288 *ap2++ = *ap++; 288 *ap2++ = *ap++;
289 ap = argv; 289 ap = argv;
290 while (*ap2++ = *ap++); 290 while (*ap2++ = *ap++);
291 shellexec(new, envp, pathval(), 0); 291 shellexec(new, envp, pathval(), 0);
292 /* NOTREACHED */ 292 /* NOTREACHED */
293} 293}
294#endif 294#endif
295 295
296 296
297 297
298/* 298/*
299 * Do a path search. The variable path (passed by reference) should be 299 * Do a path search. The variable path (passed by reference) should be
300 * set to the start of the path before the first call; padvance will update 300 * set to the start of the path before the first call; padvance will update
301 * this value as it proceeds. Successive calls to padvance will return 301 * this value as it proceeds. Successive calls to padvance will return
302 * the possible path expansions in sequence. If an option (indicated by 302 * the possible path expansions in sequence. If an option (indicated by
303 * a percent sign) appears in the path entry then the global variable 303 * a percent sign) appears in the path entry then the global variable
304 * pathopt will be set to point to it; otherwise pathopt will be set to 304 * pathopt will be set to point to it; otherwise pathopt will be set to
305 * NULL. 305 * NULL.
306 */ 306 */
307 307
308const char *pathopt; 308const char *pathopt;
309 309
310char * 310char *
311padvance(const char **path, const char *name, int magic_percent) 311padvance(const char **path, const char *name, int magic_percent)
312{ 312{
313 const char *p; 313 const char *p;
314 char *q; 314 char *q;
315 const char *start; 315 const char *start;
316 int len; 316 int len;
317 317
318 if (*path == NULL) 318 if (*path == NULL)
319 return NULL; 319 return NULL;
320 if (magic_percent) 320 if (magic_percent)
321 magic_percent = '%'; 321 magic_percent = '%';
322 322
323 start = *path; 323 start = *path;
324 for (p = start ; *p && *p != ':' && *p != magic_percent ; p++) 324 for (p = start ; *p && *p != ':' && *p != magic_percent ; p++)
325 ; 325 ;
326 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 326 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
327 while (stackblocksize() < len) 327 while (stackblocksize() < len)
328 growstackblock(); 328 growstackblock();
329 q = stackblock(); 329 q = stackblock();
330 if (p != start) { 330 if (p != start) {
331 memcpy(q, start, p - start); 331 memcpy(q, start, p - start);
332 q += p - start; 332 q += p - start;
333 if (q[-1] != '/') 333 if (q[-1] != '/')
334 *q++ = '/'; 334 *q++ = '/';
335 } 335 }
336 strcpy(q, name); 336 strcpy(q, name);
337 pathopt = NULL; 337 pathopt = NULL;
338 if (*p == magic_percent) { 338 if (*p == magic_percent) {
339 pathopt = ++p; 339 pathopt = ++p;
340 while (*p && *p != ':') 340 while (*p && *p != ':')
341 p++; 341 p++;
342 } 342 }
343 if (*p == ':') 343 if (*p == ':')
344 *path = p + 1; 344 *path = p + 1;
345 else 345 else
346 *path = NULL; 346 *path = NULL;
347 return grabstackstr(q + strlen(name) + 1); 347 return grabstackstr(q + strlen(name) + 1);
348} 348}
349 349
350 350
351/*** Command hashing code ***/ 351/*** Command hashing code ***/
352 352
353 353
354int 354int
355hashcmd(int argc, char **argv) 355hashcmd(int argc, char **argv)
356{ 356{
357 struct tblentry **pp; 357 struct tblentry **pp;
358 struct tblentry *cmdp; 358 struct tblentry *cmdp;
359 int c; 359 int c;
360 struct cmdentry entry; 360 struct cmdentry entry;
361 char *name; 361 char *name;
362 int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0; 362 int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0;
363 363
364 while ((c = nextopt("bcfrsuv")) != '\0') 364 while ((c = nextopt("bcfrsuv")) != '\0')
365 switch (c) { 365 switch (c) {
366 case 'b': bopt = 1; break; 366 case 'b': bopt = 1; break;
367 case 'c': uopt = 1; break; /* c == u */ 367 case 'c': uopt = 1; break; /* c == u */
368 case 'f': fopt = 1; break; 368 case 'f': fopt = 1; break;
369 case 'r': ropt = 1; break; 369 case 'r': ropt = 1; break;
370 case 's': sopt = 1; break; 370 case 's': sopt = 1; break;
371 case 'u': uopt = 1; break; 371 case 'u': uopt = 1; break;
372 case 'v': verbose = 1; break; 372 case 'v': verbose = 1; break;
373 } 373 }
374 374
375 if (ropt) 375 if (ropt)
376 clearcmdentry(0); 376 clearcmdentry(0);
377 377
378 if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0) 378 if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0)
379 allopt = bopt = fopt = sopt = uopt = 1; 379 allopt = bopt = fopt = sopt = uopt = 1;
380 380
381 if (*argptr == NULL) { 381 if (*argptr == NULL) {
382 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 382 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
383 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 383 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
384 switch (cmdp->cmdtype) { 384 switch (cmdp->cmdtype) {
385 case CMDNORMAL: 385 case CMDNORMAL:
386 if (!uopt) 386 if (!uopt)
387 continue; 387 continue;
388 break; 388 break;
389 case CMDBUILTIN: 389 case CMDBUILTIN:
390 if (!bopt) 390 if (!bopt)
391 continue; 391 continue;
392 break; 392 break;
393 case CMDSPLBLTIN: 393 case CMDSPLBLTIN:
394 if (!sopt) 394 if (!sopt)
395 continue; 395 continue;
396 break; 396 break;
397 case CMDFUNCTION: 397 case CMDFUNCTION:
398 if (!fopt) 398 if (!fopt)
399 continue; 399 continue;
400 break; 400 break;
401 default: /* never happens */ 401 default: /* never happens */
402 continue; 402 continue;
403 } 403 }
404 if (!allopt || verbose || 404 if (!allopt || verbose ||
405 cmdp->cmdtype == CMDNORMAL) 405 cmdp->cmdtype == CMDNORMAL)
406 printentry(cmdp, verbose); 406 printentry(cmdp, verbose);
407 } 407 }
408 } 408 }
409 return 0; 409 return 0;
410 } 410 }
411 411
412 while ((name = *argptr++) != NULL) { 412 while ((name = *argptr++) != NULL) {
413 if ((cmdp = cmdlookup(name, 0)) != NULL) { 413 if ((cmdp = cmdlookup(name, 0)) != NULL) {
414 switch (cmdp->cmdtype) { 414 switch (cmdp->cmdtype) {
415 case CMDNORMAL: 415 case CMDNORMAL:
416 if (!uopt) 416 if (!uopt)
417 continue; 417 continue;
418 delete_cmd_entry(); 418 delete_cmd_entry();
419 break; 419 break;
420 case CMDBUILTIN: 420 case CMDBUILTIN:
421 if (!bopt) 421 if (!bopt)
422 continue; 422 continue;
423 if (builtinloc >= 0) 423 if (builtinloc >= 0)
424 delete_cmd_entry(); 424 delete_cmd_entry();
425 break; 425 break;
426 case CMDSPLBLTIN: 426 case CMDSPLBLTIN:
427 if (!sopt) 427 if (!sopt)
428 continue; 428 continue;
429 break; 429 break;
430 case CMDFUNCTION: 430 case CMDFUNCTION:
431 if (!fopt) 431 if (!fopt)
432 continue; 432 continue;
433 break; 433 break;
434 } 434 }
435 } 435 }
436 find_command(name, &entry, DO_ERR, pathval()); 436 find_command(name, &entry, DO_ERR, pathval());
437 if (verbose) { 437 if (verbose) {
438 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 438 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
439 cmdp = cmdlookup(name, 0); 439 cmdp = cmdlookup(name, 0);
440 if (cmdp != NULL) 440 if (cmdp != NULL)
441 printentry(cmdp, verbose); 441 printentry(cmdp, verbose);
442 } 442 }
443 flushall(); 443 flushall();
444 } 444 }
445 } 445 }
446 return 0; 446 return 0;
447} 447}
448 448
449STATIC void 449STATIC void
450printentry(struct tblentry *cmdp, int verbose) 450printentry(struct tblentry *cmdp, int verbose)
451{ 451{
452 int idx; 452 int idx;
453 const char *path; 453 const char *path;
454 char *name; 454 char *name;
455 455
456 switch (cmdp->cmdtype) { 456 switch (cmdp->cmdtype) {
457 case CMDNORMAL: 457 case CMDNORMAL:
458 idx = cmdp->param.index; 458 idx = cmdp->param.index;
459 path = pathval(); 459 path = pathval();
460 do { 460 do {
461 name = padvance(&path, cmdp->cmdname, 1); 461 name = padvance(&path, cmdp->cmdname, 1);
462 stunalloc(name); 462 stunalloc(name);
463 } while (--idx >= 0); 463 } while (--idx >= 0);
464 if (verbose) 464 if (verbose)
465 out1fmt("Command from PATH[%d]: ", 465 out1fmt("Command from PATH[%d]: ",
466 cmdp->param.index); 466 cmdp->param.index);
467 out1str(name); 467 out1str(name);
468 break; 468 break;
469 case CMDSPLBLTIN: 469 case CMDSPLBLTIN:
470 if (verbose) 470 if (verbose)
471 out1str("special "); 471 out1str("special ");
472 /* FALLTHROUGH */ 472 /* FALLTHROUGH */
473 case CMDBUILTIN: 473 case CMDBUILTIN:
474 if (verbose) 474 if (verbose)
475 out1str("builtin "); 475 out1str("builtin ");
476 out1fmt("%s", cmdp->cmdname); 476 out1fmt("%s", cmdp->cmdname);
477 break; 477 break;
478 case CMDFUNCTION: 478 case CMDFUNCTION:
479 if (verbose) 479 if (verbose)
480 out1str("function "); 480 out1str("function ");
481 out1fmt("%s", cmdp->cmdname); 481 out1fmt("%s", cmdp->cmdname);
482 if (verbose) { 482 if (verbose) {
483 struct procstat ps; 483 struct procstat ps;
484 484
485 INTOFF; 485 INTOFF;
486 commandtext(&ps, getfuncnode(cmdp->param.func)); 486 commandtext(&ps, getfuncnode(cmdp->param.func));
487 INTON; 487 INTON;
488 out1str("() { "); 488 out1str("() { ");
489 out1str(ps.cmd); 489 out1str(ps.cmd);
490 out1str("; }"); 490 out1str("; }");
491 } 491 }
492 break; 492 break;
493 default: 493 default:
494 error("internal error: %s cmdtype %d", 494 error("internal error: %s cmdtype %d",
495 cmdp->cmdname, cmdp->cmdtype); 495 cmdp->cmdname, cmdp->cmdtype);
496 } 496 }
497 if (cmdp->rehash) 497 if (cmdp->rehash)
498 out1c('*'); 498 out1c('*');
499 out1c('\n'); 499 out1c('\n');
500} 500}
501 501
502 502
503 503
504/* 504/*
505 * Resolve a command name. If you change this routine, you may have to 505 * Resolve a command name. If you change this routine, you may have to
506 * change the shellexec routine as well. 506 * change the shellexec routine as well.
507 */ 507 */
508 508
509void 509void
510find_command(char *name, struct cmdentry *entry, int act, const char *path) 510find_command(char *name, struct cmdentry *entry, int act, const char *path)
511{ 511{
512 struct tblentry *cmdp, loc_cmd; 512 struct tblentry *cmdp, loc_cmd;
513 int idx; 513 int idx;
514 int prev; 514 int prev;
515 char *fullname; 515 char *fullname;
516 struct stat statb; 516 struct stat statb;
517 int e; 517 int e;
518 int (*bltin)(int,char **); 518 int (*bltin)(int,char **);
519 519
520 /* If name contains a slash, don't use PATH or hash table */ 520 /* If name contains a slash, don't use PATH or hash table */
521 if (strchr(name, '/') != NULL) { 521 if (strchr(name, '/') != NULL) {
522 if (act & DO_ABS) { 522 if (act & DO_ABS) {
523 while (stat(name, &statb) < 0) { 523 while (stat(name, &statb) < 0) {
524#ifdef SYSV 524#ifdef SYSV
525 if (errno == EINTR) 525 if (errno == EINTR)
526 continue; 526 continue;
527#endif 527#endif
528 if (errno != ENOENT && errno != ENOTDIR) 528 if (errno != ENOENT && errno != ENOTDIR)
529 e = errno; 529 e = errno;
530 entry->cmdtype = CMDUNKNOWN; 530 entry->cmdtype = CMDUNKNOWN;
531 entry->u.index = -1; 531 entry->u.index = -1;
532 return; 532 return;
533 } 533 }
534 entry->cmdtype = CMDNORMAL; 534 entry->cmdtype = CMDNORMAL;
535 entry->u.index = -1; 535 entry->u.index = -1;
536 return; 536 return;
537 } 537 }
538 entry->cmdtype = CMDNORMAL; 538 entry->cmdtype = CMDNORMAL;
539 entry->u.index = 0; 539 entry->u.index = 0;
540 return; 540 return;
541 } 541 }
542 542
543 if (path != pathval()) 543 if (path != pathval())
544 act |= DO_ALTPATH; 544 act |= DO_ALTPATH;
545 545
546 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) 546 if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
547 act |= DO_ALTBLTIN; 547 act |= DO_ALTBLTIN;
548 548
549 /* If name is in the table, check answer will be ok */ 549 /* If name is in the table, check answer will be ok */
550 if ((cmdp = cmdlookup(name, 0)) != NULL) { 550 if ((cmdp = cmdlookup(name, 0)) != NULL) {
551 do { 551 do {
552 switch (cmdp->cmdtype) { 552 switch (cmdp->cmdtype) {
553 case CMDNORMAL: 553 case CMDNORMAL:
554 if (act & DO_ALTPATH) { 554 if (act & DO_ALTPATH) {
555 cmdp = NULL; 555 cmdp = NULL;
556 continue; 556 continue;
557 } 557 }
558 break; 558 break;
559 case CMDFUNCTION: 559 case CMDFUNCTION:
560 if (act & DO_NOFUNC) { 560 if (act & DO_NOFUNC) {
561 cmdp = NULL; 561 cmdp = NULL;
562 continue; 562 continue;
563 } 563 }
564 break; 564 break;
565 case CMDBUILTIN: 565 case CMDBUILTIN:
566 if ((act & DO_ALTBLTIN) || builtinloc >= 0) { 566 if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
567 cmdp = NULL; 567 cmdp = NULL;
568 continue; 568 continue;
569 } 569 }
570 break; 570 break;
571 } 571 }
572 /* if not invalidated by cd, we're done */ 572 /* if not invalidated by cd, we're done */
573 if (cmdp->rehash == 0) 573 if (cmdp->rehash == 0)
574 goto success; 574 goto success;
575 } while (0); 575 } while (0);
576 } 576 }
577 577
578 /* If %builtin not in path, check for builtin next */ 578 /* If %builtin not in path, check for builtin next */
579 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && 579 if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
580 (bltin = find_builtin(name)) != 0) 580 (bltin = find_builtin(name)) != 0)
581 goto builtin_success; 581 goto builtin_success;
582 582
583 /* We have to search path. */ 583 /* We have to search path. */
584 prev = -1; /* where to start */ 584 prev = -1; /* where to start */
585 if (cmdp) { /* doing a rehash */ 585 if (cmdp) { /* doing a rehash */
586 if (cmdp->cmdtype == CMDBUILTIN) 586 if (cmdp->cmdtype == CMDBUILTIN)
587 prev = builtinloc; 587 prev = builtinloc;
588 else 588 else
589 prev = cmdp->param.index; 589 prev = cmdp->param.index;
590 } 590 }
591 591
592 e = ENOENT; 592 e = ENOENT;
593 idx = -1; 593 idx = -1;
594loop: 594loop:
595 while ((fullname = padvance(&path, name, 1)) != NULL) { 595 while ((fullname = padvance(&path, name, 1)) != NULL) {
596 stunalloc(fullname); 596 stunalloc(fullname);
597 idx++; 597 idx++;
598 if (pathopt) { 598 if (pathopt) {
599 if (prefix("builtin", pathopt)) { 599 if (prefix("builtin", pathopt)) {
600 if ((bltin = find_builtin(name)) == 0) 600 if ((bltin = find_builtin(name)) == 0)
601 goto loop; 601 goto loop;
602 goto builtin_success; 602 goto builtin_success;
603 } else if (prefix("func", pathopt)) { 603 } else if (prefix("func", pathopt)) {
604 /* handled below */ 604 /* handled below */
605 } else { 605 } else {
606 /* ignore unimplemented options */ 606 /* ignore unimplemented options */
607 goto loop; 607 goto loop;
608 } 608 }
609 } 609 }
610 /* if rehash, don't redo absolute path names */ 610 /* if rehash, don't redo absolute path names */
611 if (fullname[0] == '/' && idx <= prev) { 611 if (fullname[0] == '/' && idx <= prev) {
612 if (idx < prev) 612 if (idx < prev)
613 goto loop; 613 goto loop;
614 VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n", 614 VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n",
615 name)); 615 name));
616 goto success; 616 goto success;
617 } 617 }
618 while (stat(fullname, &statb) < 0) { 618 while (stat(fullname, &statb) < 0) {
619#ifdef SYSV 619#ifdef SYSV
620 if (errno == EINTR) 620 if (errno == EINTR)
621 continue; 621 continue;
622#endif 622#endif
623 if (errno != ENOENT && errno != ENOTDIR) 623 if (errno != ENOENT && errno != ENOTDIR)
624 e = errno; 624 e = errno;
625 goto loop; 625 goto loop;
626 } 626 }
627 e = EACCES; /* if we fail, this will be the error */ 627 e = EACCES; /* if we fail, this will be the error */
628 if (!S_ISREG(statb.st_mode)) 628 if (!S_ISREG(statb.st_mode))
629 goto loop; 629 goto loop;
630 if (pathopt) { /* this is a %func directory */ 630 if (pathopt) { /* this is a %func directory */
631 char *endname; 631 char *endname;
632 632
633 if (act & DO_NOFUNC) 633 if (act & DO_NOFUNC)
634 goto loop; 634 goto loop;
635 endname = fullname + strlen(fullname) + 1; 635 endname = fullname + strlen(fullname) + 1;
636 grabstackstr(endname); 636 grabstackstr(endname);
637 readcmdfile(fullname); 637 readcmdfile(fullname);
638 if ((cmdp = cmdlookup(name, 0)) == NULL || 638 if ((cmdp = cmdlookup(name, 0)) == NULL ||
639 cmdp->cmdtype != CMDFUNCTION) 639 cmdp->cmdtype != CMDFUNCTION)
640 error("%s not defined in %s", name, fullname); 640 error("%s not defined in %s", name, fullname);
641 ungrabstackstr(fullname, endname); 641 ungrabstackstr(fullname, endname);
642 goto success; 642 goto success;
643 } 643 }
644#ifdef notdef 644#ifdef notdef
645 /* XXX this code stops root executing stuff, and is buggy 645 /* XXX this code stops root executing stuff, and is buggy
646 if you need a group from the group list. */ 646 if you need a group from the group list. */
647 if (statb.st_uid == geteuid()) { 647 if (statb.st_uid == geteuid()) {
648 if ((statb.st_mode & 0100) == 0) 648 if ((statb.st_mode & 0100) == 0)
649 goto loop; 649 goto loop;
650 } else if (statb.st_gid == getegid()) { 650 } else if (statb.st_gid == getegid()) {
651 if ((statb.st_mode & 010) == 0) 651 if ((statb.st_mode & 010) == 0)
652 goto loop; 652 goto loop;
653 } else { 653 } else {
654 if ((statb.st_mode & 01) == 0) 654 if ((statb.st_mode & 01) == 0)
655 goto loop; 655 goto loop;
656 } 656 }
657#endif 657#endif
658 VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name, 658 VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name,
659 fullname)); 659 fullname));
660 INTOFF; 660 INTOFF;
661 if (act & DO_ALTPATH) { 661 if (act & DO_ALTPATH) {
662 /* 662 /*
663 * this should be a grabstackstr() but is not needed: 663 * this should be a grabstackstr() but is not needed:
664 * fullname is no longer needed for anything 664 * fullname is no longer needed for anything
665 stalloc(strlen(fullname) + 1); 665 stalloc(strlen(fullname) + 1);
666 */ 666 */
667 cmdp = &loc_cmd; 667 cmdp = &loc_cmd;
668 } else 668 } else
669 cmdp = cmdlookup(name, 1); 669 cmdp = cmdlookup(name, 1);
670 cmdp->cmdtype = CMDNORMAL; 670 cmdp->cmdtype = CMDNORMAL;
671 cmdp->param.index = idx; 671 cmdp->param.index = idx;
672 INTON; 672 INTON;
673 goto success; 673 goto success;
674 } 674 }
675 675
676 /* We failed. If there was an entry for this command, delete it */ 676 /* We failed. If there was an entry for this command, delete it */
677 if (cmdp) 677 if (cmdp)
678 delete_cmd_entry(); 678 delete_cmd_entry();
679 if (act & DO_ERR) 679 if (act & DO_ERR)
680 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 680 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
681 entry->cmdtype = CMDUNKNOWN; 681 entry->cmdtype = CMDUNKNOWN;
682 return; 682 return;
683 683
684builtin_success: 684builtin_success:
685 INTOFF; 685 INTOFF;
686 if (act & DO_ALTPATH) 686 if (act & DO_ALTPATH)
687 cmdp = &loc_cmd; 687 cmdp = &loc_cmd;
688 else 688 else
689 cmdp = cmdlookup(name, 1); 689 cmdp = cmdlookup(name, 1);
690 if (cmdp->cmdtype == CMDFUNCTION) 690 if (cmdp->cmdtype == CMDFUNCTION)
691 /* DO_NOFUNC must have been set */ 691 /* DO_NOFUNC must have been set */
692 cmdp = &loc_cmd; 692 cmdp = &loc_cmd;
693 cmdp->cmdtype = CMDBUILTIN; 693 cmdp->cmdtype = CMDBUILTIN;
694 cmdp->param.bltin = bltin; 694 cmdp->param.bltin = bltin;
695 INTON; 695 INTON;
696success: 696success:
697 if (cmdp) { 697 if (cmdp) {
698 cmdp->rehash = 0; 698 cmdp->rehash = 0;
699 entry->cmdtype = cmdp->cmdtype; 699 entry->cmdtype = cmdp->cmdtype;
700 entry->lineno = cmdp->lineno; 700 entry->lineno = cmdp->lineno;
701 entry->lno_frel = cmdp->fn_ln1; 701 entry->lno_frel = cmdp->fn_ln1;
702 entry->u = cmdp->param; 702 entry->u = cmdp->param;
703 } else 703 } else
704 entry->cmdtype = CMDUNKNOWN; 704 entry->cmdtype = CMDUNKNOWN;
705} 705}
706 706
707 707
708 708
709/* 709/*
710 * Search the table of builtin commands. 710 * Search the table of builtin commands.
711 */ 711 */
712 712
713int 713int
714(*find_builtin(char *name))(int, char **) 714(*find_builtin(char *name))(int, char **)
715{ 715{
716 const struct builtincmd *bp; 716 const struct builtincmd *bp;
717 717
718 for (bp = builtincmd ; bp->name ; bp++) { 718 for (bp = builtincmd ; bp->name ; bp++) {
719 if (*bp->name == *name 719 if (*bp->name == *name
720 && (*name == '%' || equal(bp->name, name))) 720 && (*name == '%' || equal(bp->name, name)))
721 return bp->builtin; 721 return bp->builtin;
722 } 722 }
723 return 0; 723 return 0;
724} 724}
725 725
726int 726int
727(*find_splbltin(char *name))(int, char **) 727(*find_splbltin(char *name))(int, char **)
728{ 728{
729 const struct builtincmd *bp; 729 const struct builtincmd *bp;
730 730
731 for (bp = splbltincmd ; bp->name ; bp++) { 731 for (bp = splbltincmd ; bp->name ; bp++) {
732 if (*bp->name == *name && equal(bp->name, name)) 732 if (*bp->name == *name && equal(bp->name, name))
733 return bp->builtin; 733 return bp->builtin;
734 } 734 }
735 return 0; 735 return 0;
736} 736}
737 737
738/* 738/*
739 * At shell startup put special builtins into hash table. 739 * At shell startup put special builtins into hash table.
740 * ensures they are executed first (see posix). 740 * ensures they are executed first (see posix).
741 * We stop functions being added with the same name 741 * We stop functions being added with the same name
742 * (as they are impossible to call) 742 * (as they are impossible to call)
743 */ 743 */
744 744
745void 745void
746hash_special_builtins(void) 746hash_special_builtins(void)
747{ 747{
748 const struct builtincmd *bp; 748 const struct builtincmd *bp;
749 struct tblentry *cmdp; 749 struct tblentry *cmdp;
750 750
751 for (bp = splbltincmd ; bp->name ; bp++) { 751 for (bp = splbltincmd ; bp->name ; bp++) {
752 cmdp = cmdlookup(bp->name, 1); 752 cmdp = cmdlookup(bp->name, 1);
753 cmdp->cmdtype = CMDSPLBLTIN; 753 cmdp->cmdtype = CMDSPLBLTIN;
754 cmdp->param.bltin = bp->builtin; 754 cmdp->param.bltin = bp->builtin;
755 } 755 }
756} 756}
757 757
758 758
759 759
760/* 760/*
761 * Called when a cd is done. Marks all commands so the next time they 761 * Called when a cd is done. Marks all commands so the next time they
762 * are executed they will be rehashed. 762 * are executed they will be rehashed.
763 */ 763 */
764 764
765void 765void
766hashcd(void) 766hashcd(void)
767{ 767{
768 struct tblentry **pp; 768 struct tblentry **pp;
769 struct tblentry *cmdp; 769 struct tblentry *cmdp;
770 770
771 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 771 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
772 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 772 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
773 if (cmdp->cmdtype == CMDNORMAL 773 if (cmdp->cmdtype == CMDNORMAL
774 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 774 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
775 cmdp->rehash = 1; 775 cmdp->rehash = 1;
776 } 776 }
777 } 777 }
778} 778}
779 779
780 780
781 781
782/* 782/*
783 * Fix command hash table when PATH changed. 783 * Fix command hash table when PATH changed.
784 * Called before PATH is changed. The argument is the new value of PATH; 784 * Called before PATH is changed. The argument is the new value of PATH;
785 * pathval() still returns the old value at this point. 785 * pathval() still returns the old value at this point.
786 * Called with interrupts off. 786 * Called with interrupts off.
787 */ 787 */
788 788
789void 789void
790changepath(const char *newval) 790changepath(const char *newval)
791{ 791{
792 const char *old, *new; 792 const char *old, *new;
793 int idx; 793 int idx;
794 int firstchange; 794 int firstchange;
795 int bltin; 795 int bltin;
796 796
797 old = pathval(); 797 old = pathval();
798 new = newval; 798 new = newval;
799 firstchange = 9999; /* assume no change */ 799 firstchange = 9999; /* assume no change */
800 idx = 0; 800 idx = 0;
801 bltin = -1; 801 bltin = -1;
802 for (;;) { 802 for (;;) {
803 if (*old != *new) { 803 if (*old != *new) {
804 firstchange = idx; 804 firstchange = idx;
805 if ((*old == '\0' && *new == ':') 805 if ((*old == '\0' && *new == ':')
806 || (*old == ':' && *new == '\0')) 806 || (*old == ':' && *new == '\0'))
807 firstchange++; 807 firstchange++;
808 old = new; /* ignore subsequent differences */ 808 old = new; /* ignore subsequent differences */
809 } 809 }
810 if (*new == '\0') 810 if (*new == '\0')
811 break; 811 break;
812 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 812 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
813 bltin = idx; 813 bltin = idx;
814 if (*new == ':') { 814 if (*new == ':') {
815 idx++; 815 idx++;
816 } 816 }
817 new++, old++; 817 new++, old++;
818 } 818 }
819 if (builtinloc < 0 && bltin >= 0) 819 if (builtinloc < 0 && bltin >= 0)
820 builtinloc = bltin; /* zap builtins */ 820 builtinloc = bltin; /* zap builtins */
821 if (builtinloc >= 0 && bltin < 0) 821 if (builtinloc >= 0 && bltin < 0)
822 firstchange = 0; 822 firstchange = 0;
823 clearcmdentry(firstchange); 823 clearcmdentry(firstchange);
824 builtinloc = bltin; 824 builtinloc = bltin;
825} 825}
826 826
827 827
828/* 828/*
829 * Clear out command entries. The argument specifies the first entry in 829 * Clear out command entries. The argument specifies the first entry in
830 * PATH which has changed. 830 * PATH which has changed.
831 */ 831 */
832 832
833STATIC void 833STATIC void
834clearcmdentry(int firstchange) 834clearcmdentry(int firstchange)
835{ 835{
836 struct tblentry **tblp; 836 struct tblentry **tblp;
837 struct tblentry **pp; 837 struct tblentry **pp;
838 struct tblentry *cmdp; 838 struct tblentry *cmdp;
839 839
840 INTOFF; 840 INTOFF;
841 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 841 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
842 pp = tblp; 842 pp = tblp;
843 while ((cmdp = *pp) != NULL) { 843 while ((cmdp = *pp) != NULL) {
844 if ((cmdp->cmdtype == CMDNORMAL && 844 if ((cmdp->cmdtype == CMDNORMAL &&
845 cmdp->param.index >= firstchange) 845 cmdp->param.index >= firstchange)
846 || (cmdp->cmdtype == CMDBUILTIN && 846 || (cmdp->cmdtype == CMDBUILTIN &&
847 builtinloc >= firstchange)) { 847 builtinloc >= firstchange)) {
848 *pp = cmdp->next; 848 *pp = cmdp->next;
849 ckfree(cmdp); 849 ckfree(cmdp);
850 } else { 850 } else {
851 pp = &cmdp->next; 851 pp = &cmdp->next;
852 } 852 }
853 } 853 }
854 } 854 }
855 INTON; 855 INTON;
856} 856}
857 857
858 858
859/* 859/*
860 * Delete all functions. 860 * Delete all functions.
861 */ 861 */
862 862
863#ifdef mkinit 863#ifdef mkinit
864MKINIT void deletefuncs(void); 864MKINIT void deletefuncs(void);
865MKINIT void hash_special_builtins(void); 865MKINIT void hash_special_builtins(void);
866 866
867INIT { 867INIT {
868 hash_special_builtins(); 868 hash_special_builtins();
869} 869}
870 870
871SHELLPROC { 871SHELLPROC {
872 deletefuncs(); 872 deletefuncs();
873} 873}
874#endif 874#endif
875 875
876void 876void
877deletefuncs(void) 877deletefuncs(void)
878{ 878{
879 struct tblentry **tblp; 879 struct tblentry **tblp;
880 struct tblentry **pp; 880 struct tblentry **pp;
881 struct tblentry *cmdp; 881 struct tblentry *cmdp;
882 882
883 INTOFF; 883 INTOFF;
884 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 884 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
885 pp = tblp; 885 pp = tblp;
886 while ((cmdp = *pp) != NULL) { 886 while ((cmdp = *pp) != NULL) {
887 if (cmdp->cmdtype == CMDFUNCTION) { 887 if (cmdp->cmdtype == CMDFUNCTION) {
888 *pp = cmdp->next; 888 *pp = cmdp->next;
889 freefunc(cmdp->param.func); 889 freefunc(cmdp->param.func);
890 ckfree(cmdp); 890 ckfree(cmdp);
891 } else { 891 } else {
892 pp = &cmdp->next; 892 pp = &cmdp->next;
893 } 893 }
894 } 894 }
895 } 895 }
896 INTON; 896 INTON;
897} 897}
898 898
899 899
900 900
901/* 901/*
902 * Locate a command in the command hash table. If "add" is nonzero, 902 * Locate a command in the command hash table. If "add" is nonzero,
903 * add the command to the table if it is not already present. The 903 * add the command to the table if it is not already present. The
904 * variable "lastcmdentry" is set to point to the address of the link 904 * variable "lastcmdentry" is set to point to the address of the link
905 * pointing to the entry, so that delete_cmd_entry can delete the 905 * pointing to the entry, so that delete_cmd_entry can delete the
906 * entry. 906 * entry.
907 */ 907 */
908 908
909struct tblentry **lastcmdentry; 909struct tblentry **lastcmdentry;
910 910
911 911
912STATIC struct tblentry * 912STATIC struct tblentry *
913cmdlookup(const char *name, int add) 913cmdlookup(const char *name, int add)
914{ 914{
915 int hashval; 915 int hashval;
916 const char *p; 916 const char *p;
917 struct tblentry *cmdp; 917 struct tblentry *cmdp;
918 struct tblentry **pp; 918 struct tblentry **pp;
919 919
920 p = name; 920 p = name;
921 hashval = *p << 4; 921 hashval = *p << 4;
922 while (*p) 922 while (*p)
923 hashval += *p++; 923 hashval += *p++;
924 hashval &= 0x7FFF; 924 hashval &= 0x7FFF;
925 pp = &cmdtable[hashval % CMDTABLESIZE]; 925 pp = &cmdtable[hashval % CMDTABLESIZE];
926 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 926 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
927 if (equal(cmdp->cmdname, name)) 927 if (equal(cmdp->cmdname, name))
928 break; 928 break;
929 pp = &cmdp->next; 929 pp = &cmdp->next;
930 } 930 }
931 if (add && cmdp == NULL) { 931 if (add && cmdp == NULL) {
932 INTOFF; 932 INTOFF;
933 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 933 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
934 + strlen(name) + 1); 934 + strlen(name) + 1);
935 cmdp->next = NULL; 935 cmdp->next = NULL;
936 cmdp->cmdtype = CMDUNKNOWN; 936 cmdp->cmdtype = CMDUNKNOWN;
937 cmdp->rehash = 0; 937 cmdp->rehash = 0;
938 strcpy(cmdp->cmdname, name); 938 strcpy(cmdp->cmdname, name);
939 INTON; 939 INTON;
940 } 940 }
941 lastcmdentry = pp; 941 lastcmdentry = pp;
942 return cmdp; 942 return cmdp;
943} 943}
944 944
945/* 945/*
946 * Delete the command entry returned on the last lookup. 946 * Delete the command entry returned on the last lookup.
947 */ 947 */
948 948
949STATIC void 949STATIC void
950delete_cmd_entry(void) 950delete_cmd_entry(void)
951{ 951{
952 struct tblentry *cmdp; 952 struct tblentry *cmdp;
953 953
954 INTOFF; 954 INTOFF;
955 cmdp = *lastcmdentry; 955 cmdp = *lastcmdentry;
956 *lastcmdentry = cmdp->next; 956 *lastcmdentry = cmdp->next;
957 ckfree(cmdp); 957 ckfree(cmdp);
958 INTON; 958 INTON;
959} 959}
960 960
961 961
962 962
963#ifdef notdef 963#ifdef notdef
964void 964void
965getcmdentry(char *name, struct cmdentry *entry) 965getcmdentry(char *name, struct cmdentry *entry)
966{ 966{
967 struct tblentry *cmdp = cmdlookup(name, 0); 967 struct tblentry *cmdp = cmdlookup(name, 0);
968 968
969 if (cmdp) { 969 if (cmdp) {
970 entry->u = cmdp->param; 970 entry->u = cmdp->param;
971 entry->cmdtype = cmdp->cmdtype; 971 entry->cmdtype = cmdp->cmdtype;
972 } else { 972 } else {
973 entry->cmdtype = CMDUNKNOWN; 973 entry->cmdtype = CMDUNKNOWN;
974 entry->u.index = 0; 974 entry->u.index = 0;
975 } 975 }
976} 976}
977#endif 977#endif
978 978
979 979
980/* 980/*
981 * Add a new command entry, replacing any existing command entry for 981 * Add a new command entry, replacing any existing command entry for
982 * the same name - except special builtins. 982 * the same name - except special builtins.
983 */ 983 */
984 984
985STATIC void 985STATIC void
986addcmdentry(char *name, struct cmdentry *entry) 986addcmdentry(char *name, struct cmdentry *entry)
987{ 987{
988 struct tblentry *cmdp; 988 struct tblentry *cmdp;
989 989
990 INTOFF; 990 INTOFF;
991 cmdp = cmdlookup(name, 1); 991 cmdp = cmdlookup(name, 1);
992 if (cmdp->cmdtype != CMDSPLBLTIN) { 992 if (cmdp->cmdtype != CMDSPLBLTIN) {
993 if (cmdp->cmdtype == CMDFUNCTION) 993 if (cmdp->cmdtype == CMDFUNCTION)
994 unreffunc(cmdp->param.func); 994 unreffunc(cmdp->param.func);
995 cmdp->cmdtype = entry->cmdtype; 995 cmdp->cmdtype = entry->cmdtype;
996 cmdp->lineno = entry->lineno; 996 cmdp->lineno = entry->lineno;
997 cmdp->fn_ln1 = entry->lno_frel; 997 cmdp->fn_ln1 = entry->lno_frel;
998 cmdp->param = entry->u; 998 cmdp->param = entry->u;
999 } 999 }
1000 INTON; 1000 INTON;
1001} 1001}
1002 1002
1003 1003
1004/* 1004/*
1005 * Define a shell function. 1005 * Define a shell function.
1006 */ 1006 */
1007 1007
1008void 1008void
1009defun(char *name, union node *func, int lineno) 1009defun(char *name, union node *func, int lineno)
1010{ 1010{
1011 struct cmdentry entry; 1011 struct cmdentry entry;
1012 1012
1013 INTOFF; 1013 INTOFF;
1014 entry.cmdtype = CMDFUNCTION; 1014 entry.cmdtype = CMDFUNCTION;
1015 entry.lineno = lineno; 1015 entry.lineno = lineno;
1016 entry.lno_frel = fnline1; 1016 entry.lno_frel = fnline1;
1017 entry.u.func = copyfunc(func); 1017 entry.u.func = copyfunc(func);
1018 addcmdentry(name, &entry); 1018 addcmdentry(name, &entry);
1019 INTON; 1019 INTON;
1020} 1020}
1021 1021
1022 1022
1023/* 1023/*
1024 * Delete a function if it exists. 1024 * Delete a function if it exists.
1025 */ 1025 */
1026 1026
1027int 1027int
1028unsetfunc(char *name) 1028unsetfunc(char *name)
1029{ 1029{
1030 struct tblentry *cmdp; 1030 struct tblentry *cmdp;
1031 1031
1032 if ((cmdp = cmdlookup(name, 0)) != NULL && 1032 if ((cmdp = cmdlookup(name, 0)) != NULL &&
1033 cmdp->cmdtype == CMDFUNCTION) { 1033 cmdp->cmdtype == CMDFUNCTION) {
1034 unreffunc(cmdp->param.func); 1034 unreffunc(cmdp->param.func);
1035 delete_cmd_entry(); 1035 delete_cmd_entry();
1036 } 1036 }
1037 return 0; 1037 return 0;
1038} 1038}
1039 1039
1040/* 1040/*
1041 * Locate and print what a word is... 1041 * Locate and print what a word is...
1042 * also used for 'command -[v|V]' 1042 * also used for 'command -[v|V]'
1043 */ 1043 */
1044 1044
1045int 1045int
1046typecmd(int argc, char **argv) 1046typecmd(int argc, char **argv)
1047{ 1047{
1048 struct cmdentry entry; 1048 struct cmdentry entry;
1049 struct tblentry *cmdp; 1049 struct tblentry *cmdp;
1050 const char * const *pp; 1050 const char * const *pp;
1051 struct alias *ap; 1051 struct alias *ap;
1052 int err = 0; 1052 int err = 0;
1053 char *arg; 1053 char *arg;
1054 int c; 1054 int c;
1055 int V_flag = 0; 1055 int V_flag = 0;
1056 int v_flag = 0; 1056 int v_flag = 0;
1057 int p_flag = 0; 1057 int p_flag = 0;
1058 1058
1059 while ((c = nextopt("vVp")) != 0) { 1059 while ((c = nextopt("vVp")) != 0) {
1060 switch (c) { 1060 switch (c) {
1061 case 'v': v_flag = 1; break; 1061 case 'v': v_flag = 1; break;
1062 case 'V': V_flag = 1; break; 1062 case 'V': V_flag = 1; break;
1063 case 'p': p_flag = 1; break; 1063 case 'p': p_flag = 1; break;
1064 } 1064 }
1065 } 1065 }
1066 1066
1067 if (p_flag && (v_flag || V_flag)) 1067 if (argv[0][0] != 'c' && v_flag | V_flag | p_flag)
1068 error("cannot specify -p with -v or -V"); 1068 error("usage: %s name...", argv[0]);
 1069
 1070 if (v_flag && V_flag)
 1071 error("-v and -V cannot both be specified");
 1072
 1073 if (*argptr == NULL)
 1074 error("usage: %s%s name ...", argv[0],
 1075 argv[0][0] == 'c' ? " [-p] [-v|-V]" : "");
1069 1076
1070 while ((arg = *argptr++)) { 1077 while ((arg = *argptr++)) {
1071 if (!v_flag) 1078 if (!v_flag)
1072 out1str(arg); 1079 out1str(arg);
1073 /* First look at the keywords */ 1080 /* First look at the keywords */
1074 for (pp = parsekwd; *pp; pp++) 1081 for (pp = parsekwd; *pp; pp++)
1075 if (**pp == *arg && equal(*pp, arg)) 1082 if (**pp == *arg && equal(*pp, arg))
1076 break; 1083 break;
1077 1084
1078 if (*pp) { 1085 if (*pp) {
1079 if (v_flag) 1086 if (v_flag)
1080 err = 1; 1087 out1fmt("%s\n", arg);
1081 else 1088 else
1082 out1str(" is a shell keyword\n"); 1089 out1str(" is a shell keyword\n");
1083 continue; 1090 continue;
1084 } 1091 }
1085 1092
1086 /* Then look at the aliases */ 1093 /* Then look at the aliases */
1087 if ((ap = lookupalias(arg, 1)) != NULL) { 1094 if ((ap = lookupalias(arg, 1)) != NULL) {
1088 if (!v_flag) 1095 int ml = 0;
1089 out1fmt(" is an alias for \n"); 1096
 1097 if (!v_flag) {
 1098 out1str(" is an alias ");
 1099 if (strchr(ap->val, '\n')) {
 1100 out1str("(multiline)...\n");
 1101 ml = 1;
 1102 } else
 1103 out1str("for: ");
 1104 }
1090 out1fmt("%s\n", ap->val); 1105 out1fmt("%s\n", ap->val);
 1106 if (ml && *argptr != NULL)
 1107 out1c('\n');
1091 continue; 1108 continue;
1092 } 1109 }
1093 1110
1094 /* Then check if it is a tracked alias */ 1111 /* Then check if it is a tracked alias */
1095 if ((cmdp = cmdlookup(arg, 0)) != NULL) { 1112 if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) {
1096 entry.cmdtype = cmdp->cmdtype; 1113 entry.cmdtype = cmdp->cmdtype;
1097 entry.u = cmdp->param; 1114 entry.u = cmdp->param;
1098 } else { 1115 } else {
 1116 cmdp = NULL;
1099 /* Finally use brute force */ 1117 /* Finally use brute force */
1100 find_command(arg, &entry, DO_ABS, pathval()); 1118 find_command(arg, &entry, DO_ABS,
 1119 p_flag ? syspath() + 5 : pathval());
1101 } 1120 }
1102 1121
1103 switch (entry.cmdtype) { 1122 switch (entry.cmdtype) {
1104 case CMDNORMAL: { 1123 case CMDNORMAL: {
1105 if (strchr(arg, '/') == NULL) { 1124 if (strchr(arg, '/') == NULL) {
1106 const char *path = pathval(); 1125 const char *path;
1107 char *name; 1126 char *name;
1108 int j = entry.u.index; 1127 int j = entry.u.index;
 1128
 1129 path = p_flag ? syspath() + 5 : pathval();
 1130
1109 do { 1131 do {
1110 name = padvance(&path, arg, 1); 1132 name = padvance(&path, arg, 1);
1111 stunalloc(name); 1133 stunalloc(name);
1112 } while (--j >= 0); 1134 } while (--j >= 0);
1113 if (!v_flag) 1135 if (!v_flag)
1114 out1fmt(" is%s ", 1136 out1fmt(" is%s ",
1115 cmdp ? " a tracked alias for" : ""); 1137 cmdp ? " a tracked alias for" : "");
1116 out1fmt("%s\n", name); 1138 out1fmt("%s\n", name);
1117 } else { 1139 } else {
1118 if (access(arg, X_OK) == 0) { 1140 if (access(arg, X_OK) == 0) {
1119 if (!v_flag) 1141 if (!v_flag)
1120 out1fmt(" is "); 1142 out1fmt(" is ");
1121 out1fmt("%s\n", arg); 1143 out1fmt("%s\n", arg);
1122 } else { 1144 } else {
1123 if (!v_flag) 1145 if (!v_flag)
1124 out1fmt(": %s\n", 1146 out1fmt(": %s\n",
1125 strerror(errno)); 1147 strerror(errno));
1126 else 1148 else
1127 err = 126; 1149 err = 126;
1128 } 1150 }
1129 } 1151 }
1130 break; 1152 break;
1131 } 1153 }
1132 case CMDFUNCTION: 1154 case CMDFUNCTION:
1133 if (!v_flag) 1155 if (!v_flag)
1134 out1str(" is a shell function\n"); 1156 out1str(" is a shell function\n");
1135 else 1157 else
1136 out1fmt("%s\n", arg); 1158 out1fmt("%s\n", arg);
1137 break; 1159 break;
1138 1160
1139 case CMDBUILTIN: 1161 case CMDBUILTIN:
1140 if (!v_flag) 1162 if (!v_flag)
1141 out1str(" is a shell builtin\n"); 1163 out1str(" is a shell builtin\n");
1142 else 1164 else
1143 out1fmt("%s\n", arg); 1165 out1fmt("%s\n", arg);
1144 break; 1166 break;
1145 1167
1146 case CMDSPLBLTIN: 1168 case CMDSPLBLTIN:
1147 if (!v_flag) 1169 if (!v_flag)
1148 out1str(" is a special shell builtin\n"); 1170 out1str(" is a special shell builtin\n");
1149 else 1171 else
1150 out1fmt("%s\n", arg); 1172 out1fmt("%s\n", arg);
1151 break; 1173 break;
1152 1174
1153 default: 1175 default:
1154 if (!v_flag) 1176 if (!v_flag)
1155 out1str(": not found\n"); 1177 out1str(": not found\n");
1156 err = 127; 1178 err = 127;
1157 break; 1179 break;
1158 } 1180 }
1159 } 1181 }
1160 return err; 1182 return err;
1161} 1183}