blob: 2a9e96a3570c69061d717ae8b146f29b86a2a526 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
54#define _GNU_SOURCE
55#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000056#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000057#include <paths.h>
58#include <setjmp.h>
59#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000060#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000061#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000062#endif
63
Denis Vlasenkob012b102007-02-19 22:43:01 +000064#if defined(__uClinux__)
65#error "Do not even bother, ash will not run on uClinux"
66#endif
67
Denis Vlasenkob012b102007-02-19 22:43:01 +000068
Denis Vlasenko01631112007-12-16 17:20:38 +000069/* ============ Hash table sizes. Configurable. */
70
71#define VTABSIZE 39
72#define ATABSIZE 39
73#define CMDTABLESIZE 31 /* should be prime */
74
75
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000076/* ============ Misc helpers */
77
78#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
79
80/* C99 say: "char" declaration may be signed or unsigned default */
81#define signed_char2int(sc) ((int)((signed char)sc))
82
83
Denis Vlasenkob012b102007-02-19 22:43:01 +000084/* ============ Shell options */
85
86static const char *const optletters_optnames[] = {
87 "e" "errexit",
88 "f" "noglob",
89 "I" "ignoreeof",
90 "i" "interactive",
91 "m" "monitor",
92 "n" "noexec",
93 "s" "stdin",
94 "x" "xtrace",
95 "v" "verbose",
96 "C" "noclobber",
97 "a" "allexport",
98 "b" "notify",
99 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000100 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000101#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000102 ,"\0" "nolog"
103 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000104#endif
105};
106
107#define optletters(n) optletters_optnames[(n)][0]
108#define optnames(n) (&optletters_optnames[(n)][1])
109
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000110enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000111
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000112static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000113
114#define eflag optlist[0]
115#define fflag optlist[1]
116#define Iflag optlist[2]
117#define iflag optlist[3]
118#define mflag optlist[4]
119#define nflag optlist[5]
120#define sflag optlist[6]
121#define xflag optlist[7]
122#define vflag optlist[8]
123#define Cflag optlist[9]
124#define aflag optlist[10]
125#define bflag optlist[11]
126#define uflag optlist[12]
127#define viflag optlist[13]
128#if DEBUG
129#define nolog optlist[14]
130#define debug optlist[15]
131#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000132
133
Denis Vlasenkob012b102007-02-19 22:43:01 +0000134/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000135
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000136static const char homestr[] ALIGN1 = "HOME";
137static const char snlfmt[] ALIGN1 = "%s\n";
138static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000139
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000140/*
Eric Andersenc470f442003-07-28 09:56:35 +0000141 * We enclose jmp_buf in a structure so that we can declare pointers to
142 * jump locations. The global variable handler contains the location to
143 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000144 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000145 * exception handlers, the user should save the value of handler on entry
146 * to an inner scope, set handler to point to a jmploc structure for the
147 * inner scope, and restore handler on exit from the scope.
148 */
Eric Andersenc470f442003-07-28 09:56:35 +0000149struct jmploc {
150 jmp_buf loc;
151};
Denis Vlasenko01631112007-12-16 17:20:38 +0000152
153struct globals_misc {
154 /* pid of main shell */
155 int rootpid;
156 /* shell level: 0 for the main shell, 1 for its children, and so on */
157 int shlvl;
158#define rootshell (!shlvl)
159 char *minusc; /* argument to -c option */
160
161 char *curdir; // = nullstr; /* current working directory */
162 char *physdir; // = nullstr; /* physical working directory */
163
164 char *arg0; /* value of $0 */
165
166 struct jmploc *exception_handler;
167 int exception;
168 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000169#define EXINT 0 /* SIGINT received */
170#define EXERROR 1 /* a generic error */
171#define EXSHELLPROC 2 /* execute a shell procedure */
172#define EXEXEC 3 /* command execution failed */
173#define EXEXIT 4 /* exit the shell */
174#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000175 volatile int suppressint;
176 volatile sig_atomic_t intpending;
177 /* do we generate EXSIG events */
178 int exsig;
179 /* last pending signal */
180 volatile sig_atomic_t pendingsig;
Eric Andersen2870d962001-07-02 17:27:21 +0000181
Denis Vlasenko01631112007-12-16 17:20:38 +0000182 /* trap handler commands */
183 char *trap[NSIG];
184 smallint isloginsh;
185 char nullstr[1]; /* zero length string */
186 /*
187 * Sigmode records the current value of the signal handlers for the various
188 * modes. A value of zero means that the current handler is not known.
189 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
190 */
191 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000192#define S_DFL 1 /* default signal handling (SIG_DFL) */
193#define S_CATCH 2 /* signal is caught */
194#define S_IGN 3 /* signal is ignored (SIG_IGN) */
195#define S_HARD_IGN 4 /* signal is ignored permenantly */
196#define S_RESET 5 /* temporary - to reset a hard ignored sig */
197
Denis Vlasenko01631112007-12-16 17:20:38 +0000198 /* indicates specified signal received */
199 char gotsig[NSIG - 1];
200};
201/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
202static struct globals_misc *const ptr_to_globals_misc __attribute__ ((section (".data")));
203#define G_misc (*ptr_to_globals_misc)
204#define rootpid (G_misc.rootpid )
205#define shlvl (G_misc.shlvl )
206#define minusc (G_misc.minusc )
207#define curdir (G_misc.curdir )
208#define physdir (G_misc.physdir )
209#define arg0 (G_misc.arg0 )
210#define exception_handler (G_misc.exception_handler)
211#define exception (G_misc.exception )
212#define suppressint (G_misc.suppressint )
213#define intpending (G_misc.intpending )
214#define exsig (G_misc.exsig )
215#define pendingsig (G_misc.pendingsig )
216#define trap (G_misc.trap )
217#define isloginsh (G_misc.isloginsh)
218#define nullstr (G_misc.nullstr )
219#define sigmode (G_misc.sigmode )
220#define gotsig (G_misc.gotsig )
221#define INIT_G_misc() do { \
222 (*(struct globals_misc**)&ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
223 curdir = nullstr; \
224 physdir = nullstr; \
225} while (0)
226
227
228/* ============ Interrupts / exceptions */
229
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000230/*
Eric Andersen2870d962001-07-02 17:27:21 +0000231 * These macros allow the user to suspend the handling of interrupt signals
232 * over a period of time. This is similar to SIGHOLD to or sigblock, but
233 * much more efficient and portable. (But hacking the kernel is so much
234 * more fun than worrying about efficiency and portability. :-))
235 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000236#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000237 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000238 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000239 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000240 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000241
242/*
243 * Called to raise an exception. Since C doesn't include exceptions, we
244 * just do a longjmp to the exception handler. The type of exception is
245 * stored in the global variable "exception".
246 */
247static void raise_exception(int) ATTRIBUTE_NORETURN;
248static void
249raise_exception(int e)
250{
251#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000252 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000253 abort();
254#endif
255 INT_OFF;
256 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000257 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000258}
259
260/*
261 * Called from trap.c when a SIGINT is received. (If the user specifies
262 * that SIGINT is to be trapped or ignored using the trap builtin, then
263 * this routine is not called.) Suppressint is nonzero when interrupts
264 * are held using the INT_OFF macro. (The test for iflag is just
265 * defensive programming.)
266 */
267static void raise_interrupt(void) ATTRIBUTE_NORETURN;
268static void
269raise_interrupt(void)
270{
271 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000272 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000273
274 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000275 /* Signal is not automatically re-enabled after it is raised,
276 * do it ourself */
277 sigemptyset(&mask);
278 sigprocmask(SIG_SETMASK, &mask, 0);
279 /* pendingsig = 0; - now done in onsig() */
280
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281 i = EXSIG;
282 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
283 if (!(rootshell && iflag)) {
284 signal(SIGINT, SIG_DFL);
285 raise(SIGINT);
286 }
287 i = EXINT;
288 }
289 raise_exception(i);
290 /* NOTREACHED */
291}
292
293#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000294static void
295int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000296{
297 if (--suppressint == 0 && intpending) {
298 raise_interrupt();
299 }
300}
301#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000302static void
303force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000304{
305 suppressint = 0;
306 if (intpending)
307 raise_interrupt();
308}
309#define FORCE_INT_ON force_int_on()
310#else
311#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000312 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000313 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000314 if (--suppressint == 0 && intpending) \
315 raise_interrupt(); \
316 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000317#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000318 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000319 xbarrier(); \
320 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000321 if (intpending) \
322 raise_interrupt(); \
323 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000324#endif /* ASH_OPTIMIZE_FOR_SIZE */
325
326#define SAVE_INT(v) ((v) = suppressint)
327
328#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000329 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000330 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000331 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000332 if (suppressint == 0 && intpending) \
333 raise_interrupt(); \
334 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000335
336#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000337 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000338 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000339 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000340 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000341 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000342 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000343/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000344
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000345/*
346 * Ignore a signal. Only one usage site - in forkchild()
347 */
348static void
349ignoresig(int signo)
350{
351 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
352 signal(signo, SIG_IGN);
353 }
354 sigmode[signo - 1] = S_HARD_IGN;
355}
356
357/*
358 * Signal handler. Only one usage site - in setsignal()
359 */
360static void
361onsig(int signo)
362{
363 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000364 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000365
366 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000367 if (!suppressint) {
368 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000369 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000370 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000371 intpending = 1;
372 }
373}
374
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000375
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000376/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000377
Eric Andersenc470f442003-07-28 09:56:35 +0000378static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000379outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000380{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000381 INT_OFF;
382 fputs(p, file);
383 INT_ON;
384}
385
386static void
387flush_stdout_stderr(void)
388{
389 INT_OFF;
390 fflush(stdout);
391 fflush(stderr);
392 INT_ON;
393}
394
395static void
396flush_stderr(void)
397{
398 INT_OFF;
399 fflush(stderr);
400 INT_ON;
401}
402
403static void
404outcslow(int c, FILE *dest)
405{
406 INT_OFF;
407 putc(c, dest);
408 fflush(dest);
409 INT_ON;
410}
411
412static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
413static int
414out1fmt(const char *fmt, ...)
415{
416 va_list ap;
417 int r;
418
419 INT_OFF;
420 va_start(ap, fmt);
421 r = vprintf(fmt, ap);
422 va_end(ap);
423 INT_ON;
424 return r;
425}
426
427static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
428static int
429fmtstr(char *outbuf, size_t length, const char *fmt, ...)
430{
431 va_list ap;
432 int ret;
433
434 va_start(ap, fmt);
435 INT_OFF;
436 ret = vsnprintf(outbuf, length, fmt, ap);
437 va_end(ap);
438 INT_ON;
439 return ret;
440}
441
442static void
443out1str(const char *p)
444{
445 outstr(p, stdout);
446}
447
448static void
449out2str(const char *p)
450{
451 outstr(p, stderr);
452 flush_stderr();
453}
454
455
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000456/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000457
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000458/* control characters in argument strings */
459#define CTLESC '\201' /* escape next character */
460#define CTLVAR '\202' /* variable defn */
461#define CTLENDVAR '\203'
462#define CTLBACKQ '\204'
463#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
464/* CTLBACKQ | CTLQUOTE == '\205' */
465#define CTLARI '\206' /* arithmetic expression */
466#define CTLENDARI '\207'
467#define CTLQUOTEMARK '\210'
468
469/* variable substitution byte (follows CTLVAR) */
470#define VSTYPE 0x0f /* type of variable substitution */
471#define VSNUL 0x10 /* colon--treat the empty string as unset */
472#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
473
474/* values of VSTYPE field */
475#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
476#define VSMINUS 0x2 /* ${var-text} */
477#define VSPLUS 0x3 /* ${var+text} */
478#define VSQUESTION 0x4 /* ${var?message} */
479#define VSASSIGN 0x5 /* ${var=text} */
480#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
481#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
482#define VSTRIMLEFT 0x8 /* ${var#pattern} */
483#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
484#define VSLENGTH 0xa /* ${#var} */
485
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000486static const char dolatstr[] ALIGN1 = {
487 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
488};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000489
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000490#define NCMD 0
491#define NPIPE 1
492#define NREDIR 2
493#define NBACKGND 3
494#define NSUBSHELL 4
495#define NAND 5
496#define NOR 6
497#define NSEMI 7
498#define NIF 8
499#define NWHILE 9
500#define NUNTIL 10
501#define NFOR 11
502#define NCASE 12
503#define NCLIST 13
504#define NDEFUN 14
505#define NARG 15
506#define NTO 16
507#define NCLOBBER 17
508#define NFROM 18
509#define NFROMTO 19
510#define NAPPEND 20
511#define NTOFD 21
512#define NFROMFD 22
513#define NHERE 23
514#define NXHERE 24
515#define NNOT 25
516
517union node;
518
519struct ncmd {
520 int type;
521 union node *assign;
522 union node *args;
523 union node *redirect;
524};
525
526struct npipe {
527 int type;
528 int backgnd;
529 struct nodelist *cmdlist;
530};
531
532struct nredir {
533 int type;
534 union node *n;
535 union node *redirect;
536};
537
538struct nbinary {
539 int type;
540 union node *ch1;
541 union node *ch2;
542};
543
544struct nif {
545 int type;
546 union node *test;
547 union node *ifpart;
548 union node *elsepart;
549};
550
551struct nfor {
552 int type;
553 union node *args;
554 union node *body;
555 char *var;
556};
557
558struct ncase {
559 int type;
560 union node *expr;
561 union node *cases;
562};
563
564struct nclist {
565 int type;
566 union node *next;
567 union node *pattern;
568 union node *body;
569};
570
571struct narg {
572 int type;
573 union node *next;
574 char *text;
575 struct nodelist *backquote;
576};
577
578struct nfile {
579 int type;
580 union node *next;
581 int fd;
582 union node *fname;
583 char *expfname;
584};
585
586struct ndup {
587 int type;
588 union node *next;
589 int fd;
590 int dupfd;
591 union node *vname;
592};
593
594struct nhere {
595 int type;
596 union node *next;
597 int fd;
598 union node *doc;
599};
600
601struct nnot {
602 int type;
603 union node *com;
604};
605
606union node {
607 int type;
608 struct ncmd ncmd;
609 struct npipe npipe;
610 struct nredir nredir;
611 struct nbinary nbinary;
612 struct nif nif;
613 struct nfor nfor;
614 struct ncase ncase;
615 struct nclist nclist;
616 struct narg narg;
617 struct nfile nfile;
618 struct ndup ndup;
619 struct nhere nhere;
620 struct nnot nnot;
621};
622
623struct nodelist {
624 struct nodelist *next;
625 union node *n;
626};
627
628struct funcnode {
629 int count;
630 union node n;
631};
632
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000633/*
634 * Free a parse tree.
635 */
636static void
637freefunc(struct funcnode *f)
638{
639 if (f && --f->count < 0)
640 free(f);
641}
642
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000643
644/* ============ Debugging output */
645
646#if DEBUG
647
648static FILE *tracefile;
649
650static void
651trace_printf(const char *fmt, ...)
652{
653 va_list va;
654
655 if (debug != 1)
656 return;
657 va_start(va, fmt);
658 vfprintf(tracefile, fmt, va);
659 va_end(va);
660}
661
662static void
663trace_vprintf(const char *fmt, va_list va)
664{
665 if (debug != 1)
666 return;
667 vfprintf(tracefile, fmt, va);
668}
669
670static void
671trace_puts(const char *s)
672{
673 if (debug != 1)
674 return;
675 fputs(s, tracefile);
676}
677
678static void
679trace_puts_quoted(char *s)
680{
681 char *p;
682 char c;
683
684 if (debug != 1)
685 return;
686 putc('"', tracefile);
687 for (p = s; *p; p++) {
688 switch (*p) {
689 case '\n': c = 'n'; goto backslash;
690 case '\t': c = 't'; goto backslash;
691 case '\r': c = 'r'; goto backslash;
692 case '"': c = '"'; goto backslash;
693 case '\\': c = '\\'; goto backslash;
694 case CTLESC: c = 'e'; goto backslash;
695 case CTLVAR: c = 'v'; goto backslash;
696 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
697 case CTLBACKQ: c = 'q'; goto backslash;
698 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
699 backslash:
700 putc('\\', tracefile);
701 putc(c, tracefile);
702 break;
703 default:
704 if (*p >= ' ' && *p <= '~')
705 putc(*p, tracefile);
706 else {
707 putc('\\', tracefile);
708 putc(*p >> 6 & 03, tracefile);
709 putc(*p >> 3 & 07, tracefile);
710 putc(*p & 07, tracefile);
711 }
712 break;
713 }
714 }
715 putc('"', tracefile);
716}
717
718static void
719trace_puts_args(char **ap)
720{
721 if (debug != 1)
722 return;
723 if (!*ap)
724 return;
725 while (1) {
726 trace_puts_quoted(*ap);
727 if (!*++ap) {
728 putc('\n', tracefile);
729 break;
730 }
731 putc(' ', tracefile);
732 }
733}
734
735static void
736opentrace(void)
737{
738 char s[100];
739#ifdef O_APPEND
740 int flags;
741#endif
742
743 if (debug != 1) {
744 if (tracefile)
745 fflush(tracefile);
746 /* leave open because libedit might be using it */
747 return;
748 }
749 strcpy(s, "./trace");
750 if (tracefile) {
751 if (!freopen(s, "a", tracefile)) {
752 fprintf(stderr, "Can't re-open %s\n", s);
753 debug = 0;
754 return;
755 }
756 } else {
757 tracefile = fopen(s, "a");
758 if (tracefile == NULL) {
759 fprintf(stderr, "Can't open %s\n", s);
760 debug = 0;
761 return;
762 }
763 }
764#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000765 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000766 if (flags >= 0)
767 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
768#endif
769 setlinebuf(tracefile);
770 fputs("\nTracing started.\n", tracefile);
771}
772
773static void
774indent(int amount, char *pfx, FILE *fp)
775{
776 int i;
777
778 for (i = 0; i < amount; i++) {
779 if (pfx && i == amount - 1)
780 fputs(pfx, fp);
781 putc('\t', fp);
782 }
783}
784
785/* little circular references here... */
786static void shtree(union node *n, int ind, char *pfx, FILE *fp);
787
788static void
789sharg(union node *arg, FILE *fp)
790{
791 char *p;
792 struct nodelist *bqlist;
793 int subtype;
794
795 if (arg->type != NARG) {
796 out1fmt("<node type %d>\n", arg->type);
797 abort();
798 }
799 bqlist = arg->narg.backquote;
800 for (p = arg->narg.text; *p; p++) {
801 switch (*p) {
802 case CTLESC:
803 putc(*++p, fp);
804 break;
805 case CTLVAR:
806 putc('$', fp);
807 putc('{', fp);
808 subtype = *++p;
809 if (subtype == VSLENGTH)
810 putc('#', fp);
811
812 while (*p != '=')
813 putc(*p++, fp);
814
815 if (subtype & VSNUL)
816 putc(':', fp);
817
818 switch (subtype & VSTYPE) {
819 case VSNORMAL:
820 putc('}', fp);
821 break;
822 case VSMINUS:
823 putc('-', fp);
824 break;
825 case VSPLUS:
826 putc('+', fp);
827 break;
828 case VSQUESTION:
829 putc('?', fp);
830 break;
831 case VSASSIGN:
832 putc('=', fp);
833 break;
834 case VSTRIMLEFT:
835 putc('#', fp);
836 break;
837 case VSTRIMLEFTMAX:
838 putc('#', fp);
839 putc('#', fp);
840 break;
841 case VSTRIMRIGHT:
842 putc('%', fp);
843 break;
844 case VSTRIMRIGHTMAX:
845 putc('%', fp);
846 putc('%', fp);
847 break;
848 case VSLENGTH:
849 break;
850 default:
851 out1fmt("<subtype %d>", subtype);
852 }
853 break;
854 case CTLENDVAR:
855 putc('}', fp);
856 break;
857 case CTLBACKQ:
858 case CTLBACKQ|CTLQUOTE:
859 putc('$', fp);
860 putc('(', fp);
861 shtree(bqlist->n, -1, NULL, fp);
862 putc(')', fp);
863 break;
864 default:
865 putc(*p, fp);
866 break;
867 }
868 }
869}
870
871static void
872shcmd(union node *cmd, FILE *fp)
873{
874 union node *np;
875 int first;
876 const char *s;
877 int dftfd;
878
879 first = 1;
880 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000881 if (!first)
882 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000883 sharg(np, fp);
884 first = 0;
885 }
886 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000887 if (!first)
888 putc(' ', fp);
889 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000890 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000891 case NTO: s = ">>"+1; dftfd = 1; break;
892 case NCLOBBER: s = ">|"; dftfd = 1; break;
893 case NAPPEND: s = ">>"; dftfd = 1; break;
894 case NTOFD: s = ">&"; dftfd = 1; break;
895 case NFROM: s = "<"; break;
896 case NFROMFD: s = "<&"; break;
897 case NFROMTO: s = "<>"; break;
898 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000899 }
900 if (np->nfile.fd != dftfd)
901 fprintf(fp, "%d", np->nfile.fd);
902 fputs(s, fp);
903 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
904 fprintf(fp, "%d", np->ndup.dupfd);
905 } else {
906 sharg(np->nfile.fname, fp);
907 }
908 first = 0;
909 }
910}
911
912static void
913shtree(union node *n, int ind, char *pfx, FILE *fp)
914{
915 struct nodelist *lp;
916 const char *s;
917
918 if (n == NULL)
919 return;
920
921 indent(ind, pfx, fp);
922 switch (n->type) {
923 case NSEMI:
924 s = "; ";
925 goto binop;
926 case NAND:
927 s = " && ";
928 goto binop;
929 case NOR:
930 s = " || ";
931 binop:
932 shtree(n->nbinary.ch1, ind, NULL, fp);
933 /* if (ind < 0) */
934 fputs(s, fp);
935 shtree(n->nbinary.ch2, ind, NULL, fp);
936 break;
937 case NCMD:
938 shcmd(n, fp);
939 if (ind >= 0)
940 putc('\n', fp);
941 break;
942 case NPIPE:
943 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
944 shcmd(lp->n, fp);
945 if (lp->next)
946 fputs(" | ", fp);
947 }
948 if (n->npipe.backgnd)
949 fputs(" &", fp);
950 if (ind >= 0)
951 putc('\n', fp);
952 break;
953 default:
954 fprintf(fp, "<node type %d>", n->type);
955 if (ind >= 0)
956 putc('\n', fp);
957 break;
958 }
959}
960
961static void
962showtree(union node *n)
963{
964 trace_puts("showtree called\n");
965 shtree(n, 1, NULL, stdout);
966}
967
968#define TRACE(param) trace_printf param
969#define TRACEV(param) trace_vprintf param
970
971#else
972
973#define TRACE(param)
974#define TRACEV(param)
975
976#endif /* DEBUG */
977
978
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000979/* ============ Parser data */
980
981/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000982 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
983 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000984struct strlist {
985 struct strlist *next;
986 char *text;
987};
988
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000989#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000990struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000991#endif
992
Denis Vlasenkob012b102007-02-19 22:43:01 +0000993struct strpush {
994 struct strpush *prev; /* preceding string on stack */
995 char *prevstring;
996 int prevnleft;
997#if ENABLE_ASH_ALIAS
998 struct alias *ap; /* if push was associated with an alias */
999#endif
1000 char *string; /* remember the string since it may change */
1001};
1002
1003struct parsefile {
1004 struct parsefile *prev; /* preceding file on stack */
1005 int linno; /* current line */
1006 int fd; /* file descriptor (or -1 if string) */
1007 int nleft; /* number of chars left in this line */
1008 int lleft; /* number of chars left in this buffer */
1009 char *nextc; /* next char in buffer */
1010 char *buf; /* input buffer */
1011 struct strpush *strpush; /* for pushing strings at this level */
1012 struct strpush basestrpush; /* so pushing one is fast */
1013};
1014
1015static struct parsefile basepf; /* top level input file */
1016static struct parsefile *parsefile = &basepf; /* current input file */
1017static int startlinno; /* line # where last token started */
1018static char *commandname; /* currently executing command */
1019static struct strlist *cmdenviron; /* environment for builtin command */
1020static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001021
1022
1023/* ============ Message printing */
1024
1025static void
1026ash_vmsg(const char *msg, va_list ap)
1027{
1028 fprintf(stderr, "%s: ", arg0);
1029 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001030 if (strcmp(arg0, commandname))
1031 fprintf(stderr, "%s: ", commandname);
1032 if (!iflag || parsefile->fd)
1033 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001034 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001035 vfprintf(stderr, msg, ap);
1036 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001037}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001038
1039/*
1040 * Exverror is called to raise the error exception. If the second argument
1041 * is not NULL then error prints an error message using printf style
1042 * formatting. It then raises the error exception.
1043 */
1044static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1045static void
1046ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001047{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001048#if DEBUG
1049 if (msg) {
1050 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1051 TRACEV((msg, ap));
1052 TRACE(("\") pid=%d\n", getpid()));
1053 } else
1054 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1055 if (msg)
1056#endif
1057 ash_vmsg(msg, ap);
1058
1059 flush_stdout_stderr();
1060 raise_exception(cond);
1061 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001062}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001063
1064static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1065static void
1066ash_msg_and_raise_error(const char *msg, ...)
1067{
1068 va_list ap;
1069
1070 va_start(ap, msg);
1071 ash_vmsg_and_raise(EXERROR, msg, ap);
1072 /* NOTREACHED */
1073 va_end(ap);
1074}
1075
1076static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1077static void
1078ash_msg_and_raise(int cond, const char *msg, ...)
1079{
1080 va_list ap;
1081
1082 va_start(ap, msg);
1083 ash_vmsg_and_raise(cond, msg, ap);
1084 /* NOTREACHED */
1085 va_end(ap);
1086}
1087
1088/*
1089 * error/warning routines for external builtins
1090 */
1091static void
1092ash_msg(const char *fmt, ...)
1093{
1094 va_list ap;
1095
1096 va_start(ap, fmt);
1097 ash_vmsg(fmt, ap);
1098 va_end(ap);
1099}
1100
1101/*
1102 * Return a string describing an error. The returned string may be a
1103 * pointer to a static buffer that will be overwritten on the next call.
1104 * Action describes the operation that got the error.
1105 */
1106static const char *
1107errmsg(int e, const char *em)
1108{
1109 if (e == ENOENT || e == ENOTDIR) {
1110 return em;
1111 }
1112 return strerror(e);
1113}
1114
1115
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001116/* ============ Memory allocation */
1117
1118/*
1119 * It appears that grabstackstr() will barf with such alignments
1120 * because stalloc() will return a string allocated in a new stackblock.
1121 */
1122#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1123enum {
1124 /* Most machines require the value returned from malloc to be aligned
1125 * in some way. The following macro will get this right
1126 * on many machines. */
1127 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1128 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001129 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001130};
1131
1132struct stack_block {
1133 struct stack_block *prev;
1134 char space[MINSIZE];
1135};
1136
1137struct stackmark {
1138 struct stack_block *stackp;
1139 char *stacknxt;
1140 size_t stacknleft;
1141 struct stackmark *marknext;
1142};
1143
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001144
Denis Vlasenko01631112007-12-16 17:20:38 +00001145struct globals_memstack {
1146 struct stack_block *g_stackp; // = &stackbase;
1147 struct stackmark *markp;
1148 char *g_stacknxt; // = stackbase.space;
1149 char *sstrend; // = stackbase.space + MINSIZE;
1150 size_t g_stacknleft; // = MINSIZE;
1151 int herefd; // = -1;
1152 struct stack_block stackbase;
1153};
1154/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1155static struct globals_memstack *const ptr_to_globals_memstack __attribute__ ((section (".data")));
1156#define G_memstack (*ptr_to_globals_memstack)
1157#define g_stackp (G_memstack.g_stackp )
1158#define markp (G_memstack.markp )
1159#define g_stacknxt (G_memstack.g_stacknxt )
1160#define sstrend (G_memstack.sstrend )
1161#define g_stacknleft (G_memstack.g_stacknleft)
1162#define herefd (G_memstack.herefd )
1163#define stackbase (G_memstack.stackbase )
1164#define INIT_G_memstack() do { \
1165 (*(struct globals_memstack**)&ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1166 g_stackp = &stackbase; \
1167 g_stacknxt = stackbase.space; \
1168 g_stacknleft = MINSIZE; \
1169 sstrend = stackbase.space + MINSIZE; \
1170 herefd = -1; \
1171} while (0)
1172
1173#define stackblock() ((void *)g_stacknxt)
1174#define stackblocksize() g_stacknleft
1175
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001176
1177static void *
1178ckrealloc(void * p, size_t nbytes)
1179{
1180 p = realloc(p, nbytes);
1181 if (!p)
1182 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1183 return p;
1184}
1185
1186static void *
1187ckmalloc(size_t nbytes)
1188{
1189 return ckrealloc(NULL, nbytes);
1190}
1191
1192/*
1193 * Make a copy of a string in safe storage.
1194 */
1195static char *
1196ckstrdup(const char *s)
1197{
1198 char *p = strdup(s);
1199 if (!p)
1200 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1201 return p;
1202}
1203
1204/*
1205 * Parse trees for commands are allocated in lifo order, so we use a stack
1206 * to make this more efficient, and also to avoid all sorts of exception
1207 * handling code to handle interrupts in the middle of a parse.
1208 *
1209 * The size 504 was chosen because the Ultrix malloc handles that size
1210 * well.
1211 */
1212static void *
1213stalloc(size_t nbytes)
1214{
1215 char *p;
1216 size_t aligned;
1217
1218 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001219 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001220 size_t len;
1221 size_t blocksize;
1222 struct stack_block *sp;
1223
1224 blocksize = aligned;
1225 if (blocksize < MINSIZE)
1226 blocksize = MINSIZE;
1227 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1228 if (len < blocksize)
1229 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1230 INT_OFF;
1231 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001232 sp->prev = g_stackp;
1233 g_stacknxt = sp->space;
1234 g_stacknleft = blocksize;
1235 sstrend = g_stacknxt + blocksize;
1236 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001237 INT_ON;
1238 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001239 p = g_stacknxt;
1240 g_stacknxt += aligned;
1241 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001242 return p;
1243}
1244
1245static void
1246stunalloc(void *p)
1247{
1248#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001249 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001250 write(2, "stunalloc\n", 10);
1251 abort();
1252 }
1253#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001254 g_stacknleft += g_stacknxt - (char *)p;
1255 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256}
1257
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001258/*
1259 * Like strdup but works with the ash stack.
1260 */
1261static char *
1262ststrdup(const char *p)
1263{
1264 size_t len = strlen(p) + 1;
1265 return memcpy(stalloc(len), p, len);
1266}
1267
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001268static void
1269setstackmark(struct stackmark *mark)
1270{
Denis Vlasenko01631112007-12-16 17:20:38 +00001271 mark->stackp = g_stackp;
1272 mark->stacknxt = g_stacknxt;
1273 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274 mark->marknext = markp;
1275 markp = mark;
1276}
1277
1278static void
1279popstackmark(struct stackmark *mark)
1280{
1281 struct stack_block *sp;
1282
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001283 if (!mark->stackp)
1284 return;
1285
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001286 INT_OFF;
1287 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001288 while (g_stackp != mark->stackp) {
1289 sp = g_stackp;
1290 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001291 free(sp);
1292 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001293 g_stacknxt = mark->stacknxt;
1294 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001295 sstrend = mark->stacknxt + mark->stacknleft;
1296 INT_ON;
1297}
1298
1299/*
1300 * When the parser reads in a string, it wants to stick the string on the
1301 * stack and only adjust the stack pointer when it knows how big the
1302 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1303 * of space on top of the stack and stackblocklen returns the length of
1304 * this block. Growstackblock will grow this space by at least one byte,
1305 * possibly moving it (like realloc). Grabstackblock actually allocates the
1306 * part of the block that has been used.
1307 */
1308static void
1309growstackblock(void)
1310{
1311 size_t newlen;
1312
Denis Vlasenko01631112007-12-16 17:20:38 +00001313 newlen = g_stacknleft * 2;
1314 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001315 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1316 if (newlen < 128)
1317 newlen += 128;
1318
Denis Vlasenko01631112007-12-16 17:20:38 +00001319 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001320 struct stack_block *oldstackp;
1321 struct stackmark *xmark;
1322 struct stack_block *sp;
1323 struct stack_block *prevstackp;
1324 size_t grosslen;
1325
1326 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001327 oldstackp = g_stackp;
1328 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001329 prevstackp = sp->prev;
1330 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1331 sp = ckrealloc(sp, grosslen);
1332 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001333 g_stackp = sp;
1334 g_stacknxt = sp->space;
1335 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001336 sstrend = sp->space + newlen;
1337
1338 /*
1339 * Stack marks pointing to the start of the old block
1340 * must be relocated to point to the new block
1341 */
1342 xmark = markp;
1343 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001344 xmark->stackp = g_stackp;
1345 xmark->stacknxt = g_stacknxt;
1346 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347 xmark = xmark->marknext;
1348 }
1349 INT_ON;
1350 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001351 char *oldspace = g_stacknxt;
1352 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001353 char *p = stalloc(newlen);
1354
1355 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001356 g_stacknxt = memcpy(p, oldspace, oldlen);
1357 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358 }
1359}
1360
1361static void
1362grabstackblock(size_t len)
1363{
1364 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001365 g_stacknxt += len;
1366 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001367}
1368
1369/*
1370 * The following routines are somewhat easier to use than the above.
1371 * The user declares a variable of type STACKSTR, which may be declared
1372 * to be a register. The macro STARTSTACKSTR initializes things. Then
1373 * the user uses the macro STPUTC to add characters to the string. In
1374 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1375 * grown as necessary. When the user is done, she can just leave the
1376 * string there and refer to it using stackblock(). Or she can allocate
1377 * the space for it using grabstackstr(). If it is necessary to allow
1378 * someone else to use the stack temporarily and then continue to grow
1379 * the string, the user should use grabstack to allocate the space, and
1380 * then call ungrabstr(p) to return to the previous mode of operation.
1381 *
1382 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1383 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1384 * is space for at least one character.
1385 */
1386static void *
1387growstackstr(void)
1388{
1389 size_t len = stackblocksize();
1390 if (herefd >= 0 && len >= 1024) {
1391 full_write(herefd, stackblock(), len);
1392 return stackblock();
1393 }
1394 growstackblock();
1395 return stackblock() + len;
1396}
1397
1398/*
1399 * Called from CHECKSTRSPACE.
1400 */
1401static char *
1402makestrspace(size_t newlen, char *p)
1403{
Denis Vlasenko01631112007-12-16 17:20:38 +00001404 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001405 size_t size = stackblocksize();
1406
1407 for (;;) {
1408 size_t nleft;
1409
1410 size = stackblocksize();
1411 nleft = size - len;
1412 if (nleft >= newlen)
1413 break;
1414 growstackblock();
1415 }
1416 return stackblock() + len;
1417}
1418
1419static char *
1420stack_nputstr(const char *s, size_t n, char *p)
1421{
1422 p = makestrspace(n, p);
1423 p = memcpy(p, s, n) + n;
1424 return p;
1425}
1426
1427static char *
1428stack_putstr(const char *s, char *p)
1429{
1430 return stack_nputstr(s, strlen(s), p);
1431}
1432
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001433static char *
1434_STPUTC(int c, char *p)
1435{
1436 if (p == sstrend)
1437 p = growstackstr();
1438 *p++ = c;
1439 return p;
1440}
1441
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001442#define STARTSTACKSTR(p) ((p) = stackblock())
1443#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001444#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001445 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001446 char *q = (p); \
1447 size_t l = (n); \
1448 size_t m = sstrend - q; \
1449 if (l > m) \
1450 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001451 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001452#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001453#define STACKSTRNUL(p) \
1454 do { \
1455 if ((p) == sstrend) \
1456 p = growstackstr(); \
1457 *p = '\0'; \
1458 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001459#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001460#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001461#define STADJUST(amount, p) (p += (amount))
1462
1463#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1464#define ungrabstackstr(s, p) stunalloc((s))
1465#define stackstrend() ((void *)sstrend)
1466
1467
1468/* ============ String helpers */
1469
1470/*
1471 * prefix -- see if pfx is a prefix of string.
1472 */
1473static char *
1474prefix(const char *string, const char *pfx)
1475{
1476 while (*pfx) {
1477 if (*pfx++ != *string++)
1478 return 0;
1479 }
1480 return (char *) string;
1481}
1482
1483/*
1484 * Check for a valid number. This should be elsewhere.
1485 */
1486static int
1487is_number(const char *p)
1488{
1489 do {
1490 if (!isdigit(*p))
1491 return 0;
1492 } while (*++p != '\0');
1493 return 1;
1494}
1495
1496/*
1497 * Convert a string of digits to an integer, printing an error message on
1498 * failure.
1499 */
1500static int
1501number(const char *s)
1502{
1503 if (!is_number(s))
1504 ash_msg_and_raise_error(illnum, s);
1505 return atoi(s);
1506}
1507
1508/*
1509 * Produce a possibly single quoted string suitable as input to the shell.
1510 * The return string is allocated on the stack.
1511 */
1512static char *
1513single_quote(const char *s)
1514{
1515 char *p;
1516
1517 STARTSTACKSTR(p);
1518
1519 do {
1520 char *q;
1521 size_t len;
1522
1523 len = strchrnul(s, '\'') - s;
1524
1525 q = p = makestrspace(len + 3, p);
1526
1527 *q++ = '\'';
1528 q = memcpy(q, s, len) + len;
1529 *q++ = '\'';
1530 s += len;
1531
1532 STADJUST(q - p, p);
1533
1534 len = strspn(s, "'");
1535 if (!len)
1536 break;
1537
1538 q = p = makestrspace(len + 3, p);
1539
1540 *q++ = '"';
1541 q = memcpy(q, s, len) + len;
1542 *q++ = '"';
1543 s += len;
1544
1545 STADJUST(q - p, p);
1546 } while (*s);
1547
1548 USTPUTC(0, p);
1549
1550 return stackblock();
1551}
1552
1553
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001554/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001555
1556static char **argptr; /* argument list for builtin commands */
1557static char *optionarg; /* set by nextopt (like getopt) */
1558static char *optptr; /* used by nextopt */
1559
1560/*
1561 * XXX - should get rid of. have all builtins use getopt(3). the
1562 * library getopt must have the BSD extension static variable "optreset"
1563 * otherwise it can't be used within the shell safely.
1564 *
1565 * Standard option processing (a la getopt) for builtin routines. The
1566 * only argument that is passed to nextopt is the option string; the
1567 * other arguments are unnecessary. It return the character, or '\0' on
1568 * end of input.
1569 */
1570static int
1571nextopt(const char *optstring)
1572{
1573 char *p;
1574 const char *q;
1575 char c;
1576
1577 p = optptr;
1578 if (p == NULL || *p == '\0') {
1579 p = *argptr;
1580 if (p == NULL || *p != '-' || *++p == '\0')
1581 return '\0';
1582 argptr++;
1583 if (LONE_DASH(p)) /* check for "--" */
1584 return '\0';
1585 }
1586 c = *p++;
1587 for (q = optstring; *q != c; ) {
1588 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001589 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001590 if (*++q == ':')
1591 q++;
1592 }
1593 if (*++q == ':') {
1594 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001595 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001596 optionarg = p;
1597 p = NULL;
1598 }
1599 optptr = p;
1600 return c;
1601}
1602
1603
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001604/* ============ Math support definitions */
1605
1606#if ENABLE_ASH_MATH_SUPPORT_64
1607typedef int64_t arith_t;
1608#define arith_t_type long long
1609#else
1610typedef long arith_t;
1611#define arith_t_type long
1612#endif
1613
1614#if ENABLE_ASH_MATH_SUPPORT
1615static arith_t dash_arith(const char *);
1616static arith_t arith(const char *expr, int *perrcode);
1617#endif
1618
1619#if ENABLE_ASH_RANDOM_SUPPORT
1620static unsigned long rseed;
1621#ifndef DYNAMIC_VAR
1622#define DYNAMIC_VAR
1623#endif
1624#endif
1625
1626
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001627/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001628
Denis Vlasenko01631112007-12-16 17:20:38 +00001629/*
1630 * The parsefile structure pointed to by the global variable parsefile
1631 * contains information about the current file being read.
1632 */
1633struct redirtab {
1634 struct redirtab *next;
1635 int renamed[10];
1636 int nullredirs;
1637};
1638
1639struct shparam {
1640 int nparam; /* # of positional parameters (without $0) */
1641#if ENABLE_ASH_GETOPTS
1642 int optind; /* next parameter to be processed by getopts */
1643 int optoff; /* used by getopts */
1644#endif
1645 unsigned char malloced; /* if parameter list dynamically allocated */
1646 char **p; /* parameter list */
1647};
1648
1649/*
1650 * Free the list of positional parameters.
1651 */
1652static void
1653freeparam(volatile struct shparam *param)
1654{
1655 char **ap;
1656
1657 if (param->malloced) {
1658 for (ap = param->p; *ap; ap++)
1659 free(*ap);
1660 free(param->p);
1661 }
1662}
1663
1664#if ENABLE_ASH_GETOPTS
1665static void getoptsreset(const char *value);
1666#endif
1667
1668struct var {
1669 struct var *next; /* next entry in hash list */
1670 int flags; /* flags are defined above */
1671 const char *text; /* name=value */
1672 void (*func)(const char *); /* function to be called when */
1673 /* the variable gets set/unset */
1674};
1675
1676struct localvar {
1677 struct localvar *next; /* next local variable in list */
1678 struct var *vp; /* the variable that was made local */
1679 int flags; /* saved flags */
1680 const char *text; /* saved text */
1681};
1682
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001683/* flags */
1684#define VEXPORT 0x01 /* variable is exported */
1685#define VREADONLY 0x02 /* variable cannot be modified */
1686#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1687#define VTEXTFIXED 0x08 /* text is statically allocated */
1688#define VSTACK 0x10 /* text is allocated on the stack */
1689#define VUNSET 0x20 /* the variable is not set */
1690#define VNOFUNC 0x40 /* don't call the callback function */
1691#define VNOSET 0x80 /* do not set variable - just readonly test */
1692#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1693#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001694# define VDYNAMIC 0x200 /* dynamic variable */
1695#else
1696# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001697#endif
1698
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001700static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#define defifs (defifsvar + 4)
1702#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001703static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001704#endif
1705
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001706
Denis Vlasenko01631112007-12-16 17:20:38 +00001707/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001708#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001709static void
1710change_lc_all(const char *value)
1711{
1712 if (value && *value != '\0')
1713 setlocale(LC_ALL, value);
1714}
1715static void
1716change_lc_ctype(const char *value)
1717{
1718 if (value && *value != '\0')
1719 setlocale(LC_CTYPE, value);
1720}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001721#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001722#if ENABLE_ASH_MAIL
1723static void chkmail(void);
1724static void changemail(const char *);
1725#endif
1726static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001727#if ENABLE_ASH_RANDOM_SUPPORT
1728static void change_random(const char *);
1729#endif
1730
Denis Vlasenko01631112007-12-16 17:20:38 +00001731static const struct {
1732 int flags;
1733 const char *text;
1734 void (*func)(const char *);
1735} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001736#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001737 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001739 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001741#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001742 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1743 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001745 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1746 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1747 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1748 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#endif
1752#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754#endif
1755#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001756 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1757 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#endif
1759#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001761#endif
1762};
1763
Denis Vlasenko01631112007-12-16 17:20:38 +00001764
1765struct globals_var {
1766 struct shparam shellparam; /* $@ current positional parameters */
1767 struct redirtab *redirlist;
1768 int g_nullredirs;
1769 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1770 struct var *vartab[VTABSIZE];
1771 struct var varinit[ARRAY_SIZE(varinit_data)];
1772};
1773/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1774static struct globals_var *const ptr_to_globals_var __attribute__ ((section (".data")));
1775#define G_var (*ptr_to_globals_var)
1776#define shellparam (G_var.shellparam )
1777#define redirlist (G_var.redirlist )
1778#define g_nullredirs (G_var.g_nullredirs )
1779#define preverrout_fd (G_var.preverrout_fd)
1780#define vartab (G_var.vartab )
1781#define varinit (G_var.varinit )
1782#define INIT_G_var() do { \
1783 int i; \
1784 (*(struct globals_var**)&ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1785 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1786 varinit[i].flags = varinit_data[i].flags; \
1787 varinit[i].text = varinit_data[i].text; \
1788 varinit[i].func = varinit_data[i].func; \
1789 } \
1790} while (0)
1791
1792#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001794#define vmail (&vifs)[1]
1795#define vmpath (&vmail)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001797#define vmpath vifs
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001798#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001799#define vpath (&vmpath)[1]
1800#define vps1 (&vpath)[1]
1801#define vps2 (&vps1)[1]
1802#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803#define voptind (&vps4)[1]
1804#if ENABLE_ASH_GETOPTS
1805#define vrandom (&voptind)[1]
1806#else
1807#define vrandom (&vps4)[1]
1808#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001809
1810/*
1811 * The following macros access the values of the above variables.
1812 * They have to skip over the name. They return the null string
1813 * for unset variables.
1814 */
1815#define ifsval() (vifs.text + 4)
1816#define ifsset() ((vifs.flags & VUNSET) == 0)
1817#define mailval() (vmail.text + 5)
1818#define mpathval() (vmpath.text + 9)
1819#define pathval() (vpath.text + 5)
1820#define ps1val() (vps1.text + 4)
1821#define ps2val() (vps2.text + 4)
1822#define ps4val() (vps4.text + 4)
1823#define optindval() (voptind.text + 7)
1824
1825#define mpathset() ((vmpath.flags & VUNSET) == 0)
1826
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001827
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001828#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1829#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1830
Denis Vlasenko01631112007-12-16 17:20:38 +00001831#if ENABLE_ASH_GETOPTS
1832static void
1833getoptsreset(const char *value)
1834{
1835 shellparam.optind = number(value);
1836 shellparam.optoff = -1;
1837}
1838#endif
1839
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840/*
1841 * Return of a legal variable name (a letter or underscore followed by zero or
1842 * more letters, underscores, and digits).
1843 */
1844static char *
1845endofname(const char *name)
1846{
1847 char *p;
1848
1849 p = (char *) name;
1850 if (!is_name(*p))
1851 return p;
1852 while (*++p) {
1853 if (!is_in_name(*p))
1854 break;
1855 }
1856 return p;
1857}
1858
1859/*
1860 * Compares two strings up to the first = or '\0'. The first
1861 * string must be terminated by '='; the second may be terminated by
1862 * either '=' or '\0'.
1863 */
1864static int
1865varcmp(const char *p, const char *q)
1866{
1867 int c, d;
1868
1869 while ((c = *p) == (d = *q)) {
1870 if (!c || c == '=')
1871 goto out;
1872 p++;
1873 q++;
1874 }
1875 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001876 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001877 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001878 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879 out:
1880 return c - d;
1881}
1882
1883static int
1884varequal(const char *a, const char *b)
1885{
1886 return !varcmp(a, b);
1887}
1888
1889/*
1890 * Find the appropriate entry in the hash table from the name.
1891 */
1892static struct var **
1893hashvar(const char *p)
1894{
1895 unsigned hashval;
1896
1897 hashval = ((unsigned char) *p) << 4;
1898 while (*p && *p != '=')
1899 hashval += (unsigned char) *p++;
1900 return &vartab[hashval % VTABSIZE];
1901}
1902
1903static int
1904vpcmp(const void *a, const void *b)
1905{
1906 return varcmp(*(const char **)a, *(const char **)b);
1907}
1908
1909/*
1910 * This routine initializes the builtin variables.
1911 */
1912static void
1913initvar(void)
1914{
1915 struct var *vp;
1916 struct var *end;
1917 struct var **vpp;
1918
1919 /*
1920 * PS1 depends on uid
1921 */
1922#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1923 vps1.text = "PS1=\\w \\$ ";
1924#else
1925 if (!geteuid())
1926 vps1.text = "PS1=# ";
1927#endif
1928 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001929 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001930 do {
1931 vpp = hashvar(vp->text);
1932 vp->next = *vpp;
1933 *vpp = vp;
1934 } while (++vp < end);
1935}
1936
1937static struct var **
1938findvar(struct var **vpp, const char *name)
1939{
1940 for (; *vpp; vpp = &(*vpp)->next) {
1941 if (varequal((*vpp)->text, name)) {
1942 break;
1943 }
1944 }
1945 return vpp;
1946}
1947
1948/*
1949 * Find the value of a variable. Returns NULL if not set.
1950 */
1951static char *
1952lookupvar(const char *name)
1953{
1954 struct var *v;
1955
1956 v = *findvar(hashvar(name), name);
1957 if (v) {
1958#ifdef DYNAMIC_VAR
1959 /*
1960 * Dynamic variables are implemented roughly the same way they are
1961 * in bash. Namely, they're "special" so long as they aren't unset.
1962 * As soon as they're unset, they're no longer dynamic, and dynamic
1963 * lookup will no longer happen at that point. -- PFM.
1964 */
1965 if ((v->flags & VDYNAMIC))
1966 (*v->func)(NULL);
1967#endif
1968 if (!(v->flags & VUNSET))
1969 return strchrnul(v->text, '=') + 1;
1970 }
1971 return NULL;
1972}
1973
1974/*
1975 * Search the environment of a builtin command.
1976 */
1977static char *
1978bltinlookup(const char *name)
1979{
1980 struct strlist *sp;
1981
1982 for (sp = cmdenviron; sp; sp = sp->next) {
1983 if (varequal(sp->text, name))
1984 return strchrnul(sp->text, '=') + 1;
1985 }
1986 return lookupvar(name);
1987}
1988
1989/*
1990 * Same as setvar except that the variable and value are passed in
1991 * the first argument as name=value. Since the first argument will
1992 * be actually stored in the table, it should not be a string that
1993 * will go away.
1994 * Called with interrupts off.
1995 */
1996static void
1997setvareq(char *s, int flags)
1998{
1999 struct var *vp, **vpp;
2000
2001 vpp = hashvar(s);
2002 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2003 vp = *findvar(vpp, s);
2004 if (vp) {
2005 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2006 const char *n;
2007
2008 if (flags & VNOSAVE)
2009 free(s);
2010 n = vp->text;
2011 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2012 }
2013
2014 if (flags & VNOSET)
2015 return;
2016
2017 if (vp->func && (flags & VNOFUNC) == 0)
2018 (*vp->func)(strchrnul(s, '=') + 1);
2019
2020 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2021 free((char*)vp->text);
2022
2023 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2024 } else {
2025 if (flags & VNOSET)
2026 return;
2027 /* not found */
2028 vp = ckmalloc(sizeof(*vp));
2029 vp->next = *vpp;
2030 vp->func = NULL;
2031 *vpp = vp;
2032 }
2033 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2034 s = ckstrdup(s);
2035 vp->text = s;
2036 vp->flags = flags;
2037}
2038
2039/*
2040 * Set the value of a variable. The flags argument is ored with the
2041 * flags of the variable. If val is NULL, the variable is unset.
2042 */
2043static void
2044setvar(const char *name, const char *val, int flags)
2045{
2046 char *p, *q;
2047 size_t namelen;
2048 char *nameeq;
2049 size_t vallen;
2050
2051 q = endofname(name);
2052 p = strchrnul(q, '=');
2053 namelen = p - name;
2054 if (!namelen || p != q)
2055 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2056 vallen = 0;
2057 if (val == NULL) {
2058 flags |= VUNSET;
2059 } else {
2060 vallen = strlen(val);
2061 }
2062 INT_OFF;
2063 nameeq = ckmalloc(namelen + vallen + 2);
2064 p = memcpy(nameeq, name, namelen) + namelen;
2065 if (val) {
2066 *p++ = '=';
2067 p = memcpy(p, val, vallen) + vallen;
2068 }
2069 *p = '\0';
2070 setvareq(nameeq, flags | VNOSAVE);
2071 INT_ON;
2072}
2073
2074#if ENABLE_ASH_GETOPTS
2075/*
2076 * Safe version of setvar, returns 1 on success 0 on failure.
2077 */
2078static int
2079setvarsafe(const char *name, const char *val, int flags)
2080{
2081 int err;
2082 volatile int saveint;
2083 struct jmploc *volatile savehandler = exception_handler;
2084 struct jmploc jmploc;
2085
2086 SAVE_INT(saveint);
2087 if (setjmp(jmploc.loc))
2088 err = 1;
2089 else {
2090 exception_handler = &jmploc;
2091 setvar(name, val, flags);
2092 err = 0;
2093 }
2094 exception_handler = savehandler;
2095 RESTORE_INT(saveint);
2096 return err;
2097}
2098#endif
2099
2100/*
2101 * Unset the specified variable.
2102 */
2103static int
2104unsetvar(const char *s)
2105{
2106 struct var **vpp;
2107 struct var *vp;
2108 int retval;
2109
2110 vpp = findvar(hashvar(s), s);
2111 vp = *vpp;
2112 retval = 2;
2113 if (vp) {
2114 int flags = vp->flags;
2115
2116 retval = 1;
2117 if (flags & VREADONLY)
2118 goto out;
2119#ifdef DYNAMIC_VAR
2120 vp->flags &= ~VDYNAMIC;
2121#endif
2122 if (flags & VUNSET)
2123 goto ok;
2124 if ((flags & VSTRFIXED) == 0) {
2125 INT_OFF;
2126 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2127 free((char*)vp->text);
2128 *vpp = vp->next;
2129 free(vp);
2130 INT_ON;
2131 } else {
2132 setvar(s, 0, 0);
2133 vp->flags &= ~VEXPORT;
2134 }
2135 ok:
2136 retval = 0;
2137 }
2138 out:
2139 return retval;
2140}
2141
2142/*
2143 * Process a linked list of variable assignments.
2144 */
2145static void
2146listsetvar(struct strlist *list_set_var, int flags)
2147{
2148 struct strlist *lp = list_set_var;
2149
2150 if (!lp)
2151 return;
2152 INT_OFF;
2153 do {
2154 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002155 lp = lp->next;
2156 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002157 INT_ON;
2158}
2159
2160/*
2161 * Generate a list of variables satisfying the given conditions.
2162 */
2163static char **
2164listvars(int on, int off, char ***end)
2165{
2166 struct var **vpp;
2167 struct var *vp;
2168 char **ep;
2169 int mask;
2170
2171 STARTSTACKSTR(ep);
2172 vpp = vartab;
2173 mask = on | off;
2174 do {
2175 for (vp = *vpp; vp; vp = vp->next) {
2176 if ((vp->flags & mask) == on) {
2177 if (ep == stackstrend())
2178 ep = growstackstr();
2179 *ep++ = (char *) vp->text;
2180 }
2181 }
2182 } while (++vpp < vartab + VTABSIZE);
2183 if (ep == stackstrend())
2184 ep = growstackstr();
2185 if (end)
2186 *end = ep;
2187 *ep++ = NULL;
2188 return grabstackstr(ep);
2189}
2190
2191
2192/* ============ Path search helper
2193 *
2194 * The variable path (passed by reference) should be set to the start
2195 * of the path before the first call; padvance will update
2196 * this value as it proceeds. Successive calls to padvance will return
2197 * the possible path expansions in sequence. If an option (indicated by
2198 * a percent sign) appears in the path entry then the global variable
2199 * pathopt will be set to point to it; otherwise pathopt will be set to
2200 * NULL.
2201 */
2202static const char *pathopt; /* set by padvance */
2203
2204static char *
2205padvance(const char **path, const char *name)
2206{
2207 const char *p;
2208 char *q;
2209 const char *start;
2210 size_t len;
2211
2212 if (*path == NULL)
2213 return NULL;
2214 start = *path;
2215 for (p = start; *p && *p != ':' && *p != '%'; p++);
2216 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2217 while (stackblocksize() < len)
2218 growstackblock();
2219 q = stackblock();
2220 if (p != start) {
2221 memcpy(q, start, p - start);
2222 q += p - start;
2223 *q++ = '/';
2224 }
2225 strcpy(q, name);
2226 pathopt = NULL;
2227 if (*p == '%') {
2228 pathopt = ++p;
2229 while (*p && *p != ':') p++;
2230 }
2231 if (*p == ':')
2232 *path = p + 1;
2233 else
2234 *path = NULL;
2235 return stalloc(len);
2236}
2237
2238
2239/* ============ Prompt */
2240
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002241static smallint doprompt; /* if set, prompt the user */
2242static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002243
2244#if ENABLE_FEATURE_EDITING
2245static line_input_t *line_input_state;
2246static const char *cmdedit_prompt;
2247static void
2248putprompt(const char *s)
2249{
2250 if (ENABLE_ASH_EXPAND_PRMT) {
2251 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002252 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002253 return;
2254 }
2255 cmdedit_prompt = s;
2256}
2257#else
2258static void
2259putprompt(const char *s)
2260{
2261 out2str(s);
2262}
2263#endif
2264
2265#if ENABLE_ASH_EXPAND_PRMT
2266/* expandstr() needs parsing machinery, so it is far away ahead... */
2267static const char *expandstr(const char *ps);
2268#else
2269#define expandstr(s) s
2270#endif
2271
2272static void
2273setprompt(int whichprompt)
2274{
2275 const char *prompt;
2276#if ENABLE_ASH_EXPAND_PRMT
2277 struct stackmark smark;
2278#endif
2279
2280 needprompt = 0;
2281
2282 switch (whichprompt) {
2283 case 1:
2284 prompt = ps1val();
2285 break;
2286 case 2:
2287 prompt = ps2val();
2288 break;
2289 default: /* 0 */
2290 prompt = nullstr;
2291 }
2292#if ENABLE_ASH_EXPAND_PRMT
2293 setstackmark(&smark);
2294 stalloc(stackblocksize());
2295#endif
2296 putprompt(expandstr(prompt));
2297#if ENABLE_ASH_EXPAND_PRMT
2298 popstackmark(&smark);
2299#endif
2300}
2301
2302
2303/* ============ The cd and pwd commands */
2304
2305#define CD_PHYSICAL 1
2306#define CD_PRINT 2
2307
2308static int docd(const char *, int);
2309
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002310static int
2311cdopt(void)
2312{
2313 int flags = 0;
2314 int i, j;
2315
2316 j = 'L';
2317 while ((i = nextopt("LP"))) {
2318 if (i != j) {
2319 flags ^= CD_PHYSICAL;
2320 j = i;
2321 }
2322 }
2323
2324 return flags;
2325}
2326
2327/*
2328 * Update curdir (the name of the current directory) in response to a
2329 * cd command.
2330 */
2331static const char *
2332updatepwd(const char *dir)
2333{
2334 char *new;
2335 char *p;
2336 char *cdcomppath;
2337 const char *lim;
2338
2339 cdcomppath = ststrdup(dir);
2340 STARTSTACKSTR(new);
2341 if (*dir != '/') {
2342 if (curdir == nullstr)
2343 return 0;
2344 new = stack_putstr(curdir, new);
2345 }
2346 new = makestrspace(strlen(dir) + 2, new);
2347 lim = stackblock() + 1;
2348 if (*dir != '/') {
2349 if (new[-1] != '/')
2350 USTPUTC('/', new);
2351 if (new > lim && *lim == '/')
2352 lim++;
2353 } else {
2354 USTPUTC('/', new);
2355 cdcomppath++;
2356 if (dir[1] == '/' && dir[2] != '/') {
2357 USTPUTC('/', new);
2358 cdcomppath++;
2359 lim++;
2360 }
2361 }
2362 p = strtok(cdcomppath, "/");
2363 while (p) {
2364 switch (*p) {
2365 case '.':
2366 if (p[1] == '.' && p[2] == '\0') {
2367 while (new > lim) {
2368 STUNPUTC(new);
2369 if (new[-1] == '/')
2370 break;
2371 }
2372 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002373 }
2374 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002375 break;
2376 /* fall through */
2377 default:
2378 new = stack_putstr(p, new);
2379 USTPUTC('/', new);
2380 }
2381 p = strtok(0, "/");
2382 }
2383 if (new > lim)
2384 STUNPUTC(new);
2385 *new = 0;
2386 return stackblock();
2387}
2388
2389/*
2390 * Find out what the current directory is. If we already know the current
2391 * directory, this routine returns immediately.
2392 */
2393static char *
2394getpwd(void)
2395{
Denis Vlasenko01631112007-12-16 17:20:38 +00002396 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002397 return dir ? dir : nullstr;
2398}
2399
2400static void
2401setpwd(const char *val, int setold)
2402{
2403 char *oldcur, *dir;
2404
2405 oldcur = dir = curdir;
2406
2407 if (setold) {
2408 setvar("OLDPWD", oldcur, VEXPORT);
2409 }
2410 INT_OFF;
2411 if (physdir != nullstr) {
2412 if (physdir != oldcur)
2413 free(physdir);
2414 physdir = nullstr;
2415 }
2416 if (oldcur == val || !val) {
2417 char *s = getpwd();
2418 physdir = s;
2419 if (!val)
2420 dir = s;
2421 } else
2422 dir = ckstrdup(val);
2423 if (oldcur != dir && oldcur != nullstr) {
2424 free(oldcur);
2425 }
2426 curdir = dir;
2427 INT_ON;
2428 setvar("PWD", dir, VEXPORT);
2429}
2430
2431static void hashcd(void);
2432
2433/*
2434 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2435 * know that the current directory has changed.
2436 */
2437static int
2438docd(const char *dest, int flags)
2439{
2440 const char *dir = 0;
2441 int err;
2442
2443 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2444
2445 INT_OFF;
2446 if (!(flags & CD_PHYSICAL)) {
2447 dir = updatepwd(dest);
2448 if (dir)
2449 dest = dir;
2450 }
2451 err = chdir(dest);
2452 if (err)
2453 goto out;
2454 setpwd(dir, 1);
2455 hashcd();
2456 out:
2457 INT_ON;
2458 return err;
2459}
2460
2461static int
2462cdcmd(int argc, char **argv)
2463{
2464 const char *dest;
2465 const char *path;
2466 const char *p;
2467 char c;
2468 struct stat statb;
2469 int flags;
2470
2471 flags = cdopt();
2472 dest = *argptr;
2473 if (!dest)
2474 dest = bltinlookup(homestr);
2475 else if (LONE_DASH(dest)) {
2476 dest = bltinlookup("OLDPWD");
2477 flags |= CD_PRINT;
2478 }
2479 if (!dest)
2480 dest = nullstr;
2481 if (*dest == '/')
2482 goto step7;
2483 if (*dest == '.') {
2484 c = dest[1];
2485 dotdot:
2486 switch (c) {
2487 case '\0':
2488 case '/':
2489 goto step6;
2490 case '.':
2491 c = dest[2];
2492 if (c != '.')
2493 goto dotdot;
2494 }
2495 }
2496 if (!*dest)
2497 dest = ".";
2498 path = bltinlookup("CDPATH");
2499 if (!path) {
2500 step6:
2501 step7:
2502 p = dest;
2503 goto docd;
2504 }
2505 do {
2506 c = *path;
2507 p = padvance(&path, dest);
2508 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2509 if (c && c != ':')
2510 flags |= CD_PRINT;
2511 docd:
2512 if (!docd(p, flags))
2513 goto out;
2514 break;
2515 }
2516 } while (path);
2517 ash_msg_and_raise_error("can't cd to %s", dest);
2518 /* NOTREACHED */
2519 out:
2520 if (flags & CD_PRINT)
2521 out1fmt(snlfmt, curdir);
2522 return 0;
2523}
2524
2525static int
2526pwdcmd(int argc, char **argv)
2527{
2528 int flags;
2529 const char *dir = curdir;
2530
2531 flags = cdopt();
2532 if (flags) {
2533 if (physdir == nullstr)
2534 setpwd(dir, 0);
2535 dir = physdir;
2536 }
2537 out1fmt(snlfmt, dir);
2538 return 0;
2539}
2540
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002541
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002542/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002543
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002544#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002545#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002546
Eric Andersenc470f442003-07-28 09:56:35 +00002547/* Syntax classes */
2548#define CWORD 0 /* character is nothing special */
2549#define CNL 1 /* newline character */
2550#define CBACK 2 /* a backslash character */
2551#define CSQUOTE 3 /* single quote */
2552#define CDQUOTE 4 /* double quote */
2553#define CENDQUOTE 5 /* a terminating quote */
2554#define CBQUOTE 6 /* backwards single quote */
2555#define CVAR 7 /* a dollar sign */
2556#define CENDVAR 8 /* a '}' character */
2557#define CLP 9 /* a left paren in arithmetic */
2558#define CRP 10 /* a right paren in arithmetic */
2559#define CENDFILE 11 /* end of file */
2560#define CCTL 12 /* like CWORD, except it must be escaped */
2561#define CSPCL 13 /* these terminate a word */
2562#define CIGN 14 /* character should be ignored */
2563
Denis Vlasenko131ae172007-02-18 13:00:19 +00002564#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002565#define SYNBASE 130
2566#define PEOF -130
2567#define PEOA -129
2568#define PEOA_OR_PEOF PEOA
2569#else
2570#define SYNBASE 129
2571#define PEOF -129
2572#define PEOA_OR_PEOF PEOF
2573#endif
2574
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002575/* number syntax index */
2576#define BASESYNTAX 0 /* not in quotes */
2577#define DQSYNTAX 1 /* in double quotes */
2578#define SQSYNTAX 2 /* in single quotes */
2579#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002580#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002581
Denis Vlasenko131ae172007-02-18 13:00:19 +00002582#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002583#define USE_SIT_FUNCTION
2584#endif
2585
Denis Vlasenko131ae172007-02-18 13:00:19 +00002586#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002587static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002588#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002589 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002590#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2592 { CNL, CNL, CNL, CNL }, /* 2, \n */
2593 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2594 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2595 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2596 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2597 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2598 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2599 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2600 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2601 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002602#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002603 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2604 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2605 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002606#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002607};
Eric Andersenc470f442003-07-28 09:56:35 +00002608#else
2609static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002610#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002611 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002612#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002613 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2614 { CNL, CNL, CNL }, /* 2, \n */
2615 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2616 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2617 { CVAR, CVAR, CWORD }, /* 5, $ */
2618 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2619 { CSPCL, CWORD, CWORD }, /* 7, ( */
2620 { CSPCL, CWORD, CWORD }, /* 8, ) */
2621 { CBACK, CBACK, CCTL }, /* 9, \ */
2622 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2623 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002624#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002625 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2626 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2627 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002628#endif
2629};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002631
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002632#ifdef USE_SIT_FUNCTION
2633
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002634static int
2635SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002636{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002637 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002638#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002639 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002640 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2641 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2642 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2643 11, 3 /* "}~" */
2644 };
2645#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002646 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002647 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2648 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2649 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2650 10, 2 /* "}~" */
2651 };
2652#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002653 const char *s;
2654 int indx;
2655
Eric Andersenc470f442003-07-28 09:56:35 +00002656 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002657 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002658#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002659 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002660 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002661 else
2662#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002663#define U_C(c) ((unsigned char)(c))
2664
2665 if ((unsigned char)c >= (unsigned char)(CTLESC)
2666 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2667 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002668 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002669 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002671 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002672 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002673 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002674 }
2675 return S_I_T[indx][syntax];
2676}
2677
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002678#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002679
Denis Vlasenko131ae172007-02-18 13:00:19 +00002680#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002681#define CSPCL_CIGN_CIGN_CIGN 0
2682#define CSPCL_CWORD_CWORD_CWORD 1
2683#define CNL_CNL_CNL_CNL 2
2684#define CWORD_CCTL_CCTL_CWORD 3
2685#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2686#define CVAR_CVAR_CWORD_CVAR 5
2687#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2688#define CSPCL_CWORD_CWORD_CLP 7
2689#define CSPCL_CWORD_CWORD_CRP 8
2690#define CBACK_CBACK_CCTL_CBACK 9
2691#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2692#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2693#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2694#define CWORD_CWORD_CWORD_CWORD 13
2695#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002696#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002697#define CSPCL_CWORD_CWORD_CWORD 0
2698#define CNL_CNL_CNL_CNL 1
2699#define CWORD_CCTL_CCTL_CWORD 2
2700#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2701#define CVAR_CVAR_CWORD_CVAR 4
2702#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2703#define CSPCL_CWORD_CWORD_CLP 6
2704#define CSPCL_CWORD_CWORD_CRP 7
2705#define CBACK_CBACK_CCTL_CBACK 8
2706#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2707#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2708#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2709#define CWORD_CWORD_CWORD_CWORD 12
2710#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002711#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002712
2713static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002714 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002715 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002716#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002717 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2718#endif
2719 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2721 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2722 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2723 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2724 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2725 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2726 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2727 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002728 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2857 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2858 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2880 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002881 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002882 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2884 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002886 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002887 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2888 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2889 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2892 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2893 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2894 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2895 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2907 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2908 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2909 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2911 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2939 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2940 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2944 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2972 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2973 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2974 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002975};
2976
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002977#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2978
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002979#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002980
Eric Andersen2870d962001-07-02 17:27:21 +00002981
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002982/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002983
Denis Vlasenko131ae172007-02-18 13:00:19 +00002984#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002985
2986#define ALIASINUSE 1
2987#define ALIASDEAD 2
2988
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002989struct alias {
2990 struct alias *next;
2991 char *name;
2992 char *val;
2993 int flag;
2994};
2995
Denis Vlasenko01631112007-12-16 17:20:38 +00002996
2997static struct alias **atab; // [ATABSIZE];
2998#define INIT_G_alias() do { \
2999 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3000} while (0)
3001
Eric Andersen2870d962001-07-02 17:27:21 +00003002
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003003static struct alias **
3004__lookupalias(const char *name) {
3005 unsigned int hashval;
3006 struct alias **app;
3007 const char *p;
3008 unsigned int ch;
3009
3010 p = name;
3011
3012 ch = (unsigned char)*p;
3013 hashval = ch << 4;
3014 while (ch) {
3015 hashval += ch;
3016 ch = (unsigned char)*++p;
3017 }
3018 app = &atab[hashval % ATABSIZE];
3019
3020 for (; *app; app = &(*app)->next) {
3021 if (strcmp(name, (*app)->name) == 0) {
3022 break;
3023 }
3024 }
3025
3026 return app;
3027}
3028
3029static struct alias *
3030lookupalias(const char *name, int check)
3031{
3032 struct alias *ap = *__lookupalias(name);
3033
3034 if (check && ap && (ap->flag & ALIASINUSE))
3035 return NULL;
3036 return ap;
3037}
3038
3039static struct alias *
3040freealias(struct alias *ap)
3041{
3042 struct alias *next;
3043
3044 if (ap->flag & ALIASINUSE) {
3045 ap->flag |= ALIASDEAD;
3046 return ap;
3047 }
3048
3049 next = ap->next;
3050 free(ap->name);
3051 free(ap->val);
3052 free(ap);
3053 return next;
3054}
Eric Andersencb57d552001-06-28 07:25:16 +00003055
Eric Andersenc470f442003-07-28 09:56:35 +00003056static void
3057setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003058{
3059 struct alias *ap, **app;
3060
3061 app = __lookupalias(name);
3062 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003063 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003064 if (ap) {
3065 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003066 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003067 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003068 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003069 ap->flag &= ~ALIASDEAD;
3070 } else {
3071 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003072 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003073 ap->name = ckstrdup(name);
3074 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003075 ap->flag = 0;
3076 ap->next = 0;
3077 *app = ap;
3078 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003079 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003080}
3081
Eric Andersenc470f442003-07-28 09:56:35 +00003082static int
3083unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003084{
Eric Andersencb57d552001-06-28 07:25:16 +00003085 struct alias **app;
3086
3087 app = __lookupalias(name);
3088
3089 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003090 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003091 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003092 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003093 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003094 }
3095
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003096 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003097}
3098
Eric Andersenc470f442003-07-28 09:56:35 +00003099static void
3100rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003101{
Eric Andersencb57d552001-06-28 07:25:16 +00003102 struct alias *ap, **app;
3103 int i;
3104
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003106 for (i = 0; i < ATABSIZE; i++) {
3107 app = &atab[i];
3108 for (ap = *app; ap; ap = *app) {
3109 *app = freealias(*app);
3110 if (ap == *app) {
3111 app = &ap->next;
3112 }
3113 }
3114 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003115 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003116}
3117
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003118static void
3119printalias(const struct alias *ap)
3120{
3121 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3122}
3123
Eric Andersencb57d552001-06-28 07:25:16 +00003124/*
3125 * TODO - sort output
3126 */
Eric Andersenc470f442003-07-28 09:56:35 +00003127static int
3128aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003129{
3130 char *n, *v;
3131 int ret = 0;
3132 struct alias *ap;
3133
3134 if (argc == 1) {
3135 int i;
3136
3137 for (i = 0; i < ATABSIZE; i++)
3138 for (ap = atab[i]; ap; ap = ap->next) {
3139 printalias(ap);
3140 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003141 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003142 }
3143 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003144 v = strchr(n+1, '=');
3145 if (v == NULL) { /* n+1: funny ksh stuff */
3146 ap = *__lookupalias(n);
3147 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003148 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003149 ret = 1;
3150 } else
3151 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003152 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003153 *v++ = '\0';
3154 setalias(n, v);
3155 }
3156 }
3157
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003158 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003159}
3160
Eric Andersenc470f442003-07-28 09:56:35 +00003161static int
3162unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003163{
3164 int i;
3165
3166 while ((i = nextopt("a")) != '\0') {
3167 if (i == 'a') {
3168 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003169 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003170 }
3171 }
3172 for (i = 0; *argptr; argptr++) {
3173 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003174 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003175 i = 1;
3176 }
3177 }
3178
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003179 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003180}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003181
Denis Vlasenko131ae172007-02-18 13:00:19 +00003182#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003183
Eric Andersenc470f442003-07-28 09:56:35 +00003184
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003185/* ============ jobs.c */
3186
3187/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3188#define FORK_FG 0
3189#define FORK_BG 1
3190#define FORK_NOJOB 2
3191
3192/* mode flags for showjob(s) */
3193#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3194#define SHOW_PID 0x04 /* include process pid */
3195#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3196
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003197/*
3198 * A job structure contains information about a job. A job is either a
3199 * single process or a set of processes contained in a pipeline. In the
3200 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3201 * array of pids.
3202 */
3203
3204struct procstat {
3205 pid_t pid; /* process id */
3206 int status; /* last process status from wait() */
3207 char *cmd; /* text of command being run */
3208};
3209
3210struct job {
3211 struct procstat ps0; /* status of process */
3212 struct procstat *ps; /* status or processes when more than one */
3213#if JOBS
3214 int stopstatus; /* status of a stopped job */
3215#endif
3216 uint32_t
3217 nprocs: 16, /* number of processes */
3218 state: 8,
3219#define JOBRUNNING 0 /* at least one proc running */
3220#define JOBSTOPPED 1 /* all procs are stopped */
3221#define JOBDONE 2 /* all procs are completed */
3222#if JOBS
3223 sigint: 1, /* job was killed by SIGINT */
3224 jobctl: 1, /* job running under job control */
3225#endif
3226 waited: 1, /* true if this entry has been waited for */
3227 used: 1, /* true if this entry is in used */
3228 changed: 1; /* true if status has changed */
3229 struct job *prev_job; /* previous job */
3230};
3231
3232static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003233static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003234
3235static struct job *makejob(union node *, int);
3236static int forkshell(struct job *, union node *, int);
3237static int waitforjob(struct job *);
3238
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003239#if !JOBS
3240enum { jobctl = 0 };
3241#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003242#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003243static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003244static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003245#endif
3246
3247/*
3248 * Set the signal handler for the specified signal. The routine figures
3249 * out what it should be set to.
3250 */
3251static void
3252setsignal(int signo)
3253{
3254 int action;
3255 char *t, tsig;
3256 struct sigaction act;
3257
3258 t = trap[signo];
3259 if (t == NULL)
3260 action = S_DFL;
3261 else if (*t != '\0')
3262 action = S_CATCH;
3263 else
3264 action = S_IGN;
3265 if (rootshell && action == S_DFL) {
3266 switch (signo) {
3267 case SIGINT:
3268 if (iflag || minusc || sflag == 0)
3269 action = S_CATCH;
3270 break;
3271 case SIGQUIT:
3272#if DEBUG
3273 if (debug)
3274 break;
3275#endif
3276 /* FALLTHROUGH */
3277 case SIGTERM:
3278 if (iflag)
3279 action = S_IGN;
3280 break;
3281#if JOBS
3282 case SIGTSTP:
3283 case SIGTTOU:
3284 if (mflag)
3285 action = S_IGN;
3286 break;
3287#endif
3288 }
3289 }
3290
3291 t = &sigmode[signo - 1];
3292 tsig = *t;
3293 if (tsig == 0) {
3294 /*
3295 * current setting unknown
3296 */
3297 if (sigaction(signo, 0, &act) == -1) {
3298 /*
3299 * Pretend it worked; maybe we should give a warning
3300 * here, but other shells don't. We don't alter
3301 * sigmode, so that we retry every time.
3302 */
3303 return;
3304 }
3305 if (act.sa_handler == SIG_IGN) {
3306 if (mflag
3307 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3308 ) {
3309 tsig = S_IGN; /* don't hard ignore these */
3310 } else
3311 tsig = S_HARD_IGN;
3312 } else {
3313 tsig = S_RESET; /* force to be set */
3314 }
3315 }
3316 if (tsig == S_HARD_IGN || tsig == action)
3317 return;
3318 switch (action) {
3319 case S_CATCH:
3320 act.sa_handler = onsig;
3321 break;
3322 case S_IGN:
3323 act.sa_handler = SIG_IGN;
3324 break;
3325 default:
3326 act.sa_handler = SIG_DFL;
3327 }
3328 *t = action;
3329 act.sa_flags = 0;
3330 sigfillset(&act.sa_mask);
3331 sigaction(signo, &act, 0);
3332}
3333
3334/* mode flags for set_curjob */
3335#define CUR_DELETE 2
3336#define CUR_RUNNING 1
3337#define CUR_STOPPED 0
3338
3339/* mode flags for dowait */
3340#define DOWAIT_NORMAL 0
3341#define DOWAIT_BLOCK 1
3342
3343#if JOBS
3344/* pgrp of shell on invocation */
3345static int initialpgrp;
3346static int ttyfd = -1;
3347#endif
3348/* array of jobs */
3349static struct job *jobtab;
3350/* size of array */
3351static unsigned njobs;
3352/* current job */
3353static struct job *curjob;
3354/* number of presumed living untracked jobs */
3355static int jobless;
3356
3357static void
3358set_curjob(struct job *jp, unsigned mode)
3359{
3360 struct job *jp1;
3361 struct job **jpp, **curp;
3362
3363 /* first remove from list */
3364 jpp = curp = &curjob;
3365 do {
3366 jp1 = *jpp;
3367 if (jp1 == jp)
3368 break;
3369 jpp = &jp1->prev_job;
3370 } while (1);
3371 *jpp = jp1->prev_job;
3372
3373 /* Then re-insert in correct position */
3374 jpp = curp;
3375 switch (mode) {
3376 default:
3377#if DEBUG
3378 abort();
3379#endif
3380 case CUR_DELETE:
3381 /* job being deleted */
3382 break;
3383 case CUR_RUNNING:
3384 /* newly created job or backgrounded job,
3385 put after all stopped jobs. */
3386 do {
3387 jp1 = *jpp;
3388#if JOBS
3389 if (!jp1 || jp1->state != JOBSTOPPED)
3390#endif
3391 break;
3392 jpp = &jp1->prev_job;
3393 } while (1);
3394 /* FALLTHROUGH */
3395#if JOBS
3396 case CUR_STOPPED:
3397#endif
3398 /* newly stopped job - becomes curjob */
3399 jp->prev_job = *jpp;
3400 *jpp = jp;
3401 break;
3402 }
3403}
3404
3405#if JOBS || DEBUG
3406static int
3407jobno(const struct job *jp)
3408{
3409 return jp - jobtab + 1;
3410}
3411#endif
3412
3413/*
3414 * Convert a job name to a job structure.
3415 */
3416static struct job *
3417getjob(const char *name, int getctl)
3418{
3419 struct job *jp;
3420 struct job *found;
3421 const char *err_msg = "No such job: %s";
3422 unsigned num;
3423 int c;
3424 const char *p;
3425 char *(*match)(const char *, const char *);
3426
3427 jp = curjob;
3428 p = name;
3429 if (!p)
3430 goto currentjob;
3431
3432 if (*p != '%')
3433 goto err;
3434
3435 c = *++p;
3436 if (!c)
3437 goto currentjob;
3438
3439 if (!p[1]) {
3440 if (c == '+' || c == '%') {
3441 currentjob:
3442 err_msg = "No current job";
3443 goto check;
3444 }
3445 if (c == '-') {
3446 if (jp)
3447 jp = jp->prev_job;
3448 err_msg = "No previous job";
3449 check:
3450 if (!jp)
3451 goto err;
3452 goto gotit;
3453 }
3454 }
3455
3456 if (is_number(p)) {
3457 num = atoi(p);
3458 if (num < njobs) {
3459 jp = jobtab + num - 1;
3460 if (jp->used)
3461 goto gotit;
3462 goto err;
3463 }
3464 }
3465
3466 match = prefix;
3467 if (*p == '?') {
3468 match = strstr;
3469 p++;
3470 }
3471
3472 found = 0;
3473 while (1) {
3474 if (!jp)
3475 goto err;
3476 if (match(jp->ps[0].cmd, p)) {
3477 if (found)
3478 goto err;
3479 found = jp;
3480 err_msg = "%s: ambiguous";
3481 }
3482 jp = jp->prev_job;
3483 }
3484
3485 gotit:
3486#if JOBS
3487 err_msg = "job %s not created under job control";
3488 if (getctl && jp->jobctl == 0)
3489 goto err;
3490#endif
3491 return jp;
3492 err:
3493 ash_msg_and_raise_error(err_msg, name);
3494}
3495
3496/*
3497 * Mark a job structure as unused.
3498 */
3499static void
3500freejob(struct job *jp)
3501{
3502 struct procstat *ps;
3503 int i;
3504
3505 INT_OFF;
3506 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3507 if (ps->cmd != nullstr)
3508 free(ps->cmd);
3509 }
3510 if (jp->ps != &jp->ps0)
3511 free(jp->ps);
3512 jp->used = 0;
3513 set_curjob(jp, CUR_DELETE);
3514 INT_ON;
3515}
3516
3517#if JOBS
3518static void
3519xtcsetpgrp(int fd, pid_t pgrp)
3520{
3521 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003522 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003523}
3524
3525/*
3526 * Turn job control on and off.
3527 *
3528 * Note: This code assumes that the third arg to ioctl is a character
3529 * pointer, which is true on Berkeley systems but not System V. Since
3530 * System V doesn't have job control yet, this isn't a problem now.
3531 *
3532 * Called with interrupts off.
3533 */
3534static void
3535setjobctl(int on)
3536{
3537 int fd;
3538 int pgrp;
3539
3540 if (on == jobctl || rootshell == 0)
3541 return;
3542 if (on) {
3543 int ofd;
3544 ofd = fd = open(_PATH_TTY, O_RDWR);
3545 if (fd < 0) {
3546 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3547 * That sometimes helps to acquire controlling tty.
3548 * Obviously, a workaround for bugs when someone
3549 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003550 fd = 2;
3551 while (!isatty(fd))
3552 if (--fd < 0)
3553 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003554 }
3555 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003556 if (ofd >= 0)
3557 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003558 if (fd < 0)
3559 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003560 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003561 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003562 do { /* while we are in the background */
3563 pgrp = tcgetpgrp(fd);
3564 if (pgrp < 0) {
3565 out:
3566 ash_msg("can't access tty; job control turned off");
3567 mflag = on = 0;
3568 goto close;
3569 }
3570 if (pgrp == getpgrp())
3571 break;
3572 killpg(0, SIGTTIN);
3573 } while (1);
3574 initialpgrp = pgrp;
3575
3576 setsignal(SIGTSTP);
3577 setsignal(SIGTTOU);
3578 setsignal(SIGTTIN);
3579 pgrp = rootpid;
3580 setpgid(0, pgrp);
3581 xtcsetpgrp(fd, pgrp);
3582 } else {
3583 /* turning job control off */
3584 fd = ttyfd;
3585 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003586 /* was xtcsetpgrp, but this can make exiting ash
3587 * with pty already deleted loop forever */
3588 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589 setpgid(0, pgrp);
3590 setsignal(SIGTSTP);
3591 setsignal(SIGTTOU);
3592 setsignal(SIGTTIN);
3593 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003594 if (fd >= 0)
3595 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596 fd = -1;
3597 }
3598 ttyfd = fd;
3599 jobctl = on;
3600}
3601
3602static int
3603killcmd(int argc, char **argv)
3604{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003605 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3606 int i = 1;
3607 do {
3608 if (argv[i][0] == '%') {
3609 struct job *jp = getjob(argv[i], 0);
3610 unsigned pid = jp->ps[0].pid;
3611 /* Enough space for ' -NNN<nul>' */
3612 argv[i] = alloca(sizeof(int)*3 + 3);
3613 /* kill_main has matching code to expect
3614 * leading space. Needed to not confuse
3615 * negative pids with "kill -SIGNAL_NO" syntax */
3616 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003617 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003618 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003619 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003620 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621}
3622
3623static void
3624showpipe(struct job *jp, FILE *out)
3625{
3626 struct procstat *sp;
3627 struct procstat *spend;
3628
3629 spend = jp->ps + jp->nprocs;
3630 for (sp = jp->ps + 1; sp < spend; sp++)
3631 fprintf(out, " | %s", sp->cmd);
3632 outcslow('\n', out);
3633 flush_stdout_stderr();
3634}
3635
3636
3637static int
3638restartjob(struct job *jp, int mode)
3639{
3640 struct procstat *ps;
3641 int i;
3642 int status;
3643 pid_t pgid;
3644
3645 INT_OFF;
3646 if (jp->state == JOBDONE)
3647 goto out;
3648 jp->state = JOBRUNNING;
3649 pgid = jp->ps->pid;
3650 if (mode == FORK_FG)
3651 xtcsetpgrp(ttyfd, pgid);
3652 killpg(pgid, SIGCONT);
3653 ps = jp->ps;
3654 i = jp->nprocs;
3655 do {
3656 if (WIFSTOPPED(ps->status)) {
3657 ps->status = -1;
3658 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003659 ps++;
3660 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003661 out:
3662 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3663 INT_ON;
3664 return status;
3665}
3666
3667static int
3668fg_bgcmd(int argc, char **argv)
3669{
3670 struct job *jp;
3671 FILE *out;
3672 int mode;
3673 int retval;
3674
3675 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3676 nextopt(nullstr);
3677 argv = argptr;
3678 out = stdout;
3679 do {
3680 jp = getjob(*argv, 1);
3681 if (mode == FORK_BG) {
3682 set_curjob(jp, CUR_RUNNING);
3683 fprintf(out, "[%d] ", jobno(jp));
3684 }
3685 outstr(jp->ps->cmd, out);
3686 showpipe(jp, out);
3687 retval = restartjob(jp, mode);
3688 } while (*argv && *++argv);
3689 return retval;
3690}
3691#endif
3692
3693static int
3694sprint_status(char *s, int status, int sigonly)
3695{
3696 int col;
3697 int st;
3698
3699 col = 0;
3700 if (!WIFEXITED(status)) {
3701#if JOBS
3702 if (WIFSTOPPED(status))
3703 st = WSTOPSIG(status);
3704 else
3705#endif
3706 st = WTERMSIG(status);
3707 if (sigonly) {
3708 if (st == SIGINT || st == SIGPIPE)
3709 goto out;
3710#if JOBS
3711 if (WIFSTOPPED(status))
3712 goto out;
3713#endif
3714 }
3715 st &= 0x7f;
3716 col = fmtstr(s, 32, strsignal(st));
3717 if (WCOREDUMP(status)) {
3718 col += fmtstr(s + col, 16, " (core dumped)");
3719 }
3720 } else if (!sigonly) {
3721 st = WEXITSTATUS(status);
3722 if (st)
3723 col = fmtstr(s, 16, "Done(%d)", st);
3724 else
3725 col = fmtstr(s, 16, "Done");
3726 }
3727 out:
3728 return col;
3729}
3730
3731/*
3732 * Do a wait system call. If job control is compiled in, we accept
3733 * stopped processes. If block is zero, we return a value of zero
3734 * rather than blocking.
3735 *
3736 * System V doesn't have a non-blocking wait system call. It does
3737 * have a SIGCLD signal that is sent to a process when one of it's
3738 * children dies. The obvious way to use SIGCLD would be to install
3739 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3740 * was received, and have waitproc bump another counter when it got
3741 * the status of a process. Waitproc would then know that a wait
3742 * system call would not block if the two counters were different.
3743 * This approach doesn't work because if a process has children that
3744 * have not been waited for, System V will send it a SIGCLD when it
3745 * installs a signal handler for SIGCLD. What this means is that when
3746 * a child exits, the shell will be sent SIGCLD signals continuously
3747 * until is runs out of stack space, unless it does a wait call before
3748 * restoring the signal handler. The code below takes advantage of
3749 * this (mis)feature by installing a signal handler for SIGCLD and
3750 * then checking to see whether it was called. If there are any
3751 * children to be waited for, it will be.
3752 *
3753 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3754 * waits at all. In this case, the user will not be informed when
3755 * a background process until the next time she runs a real program
3756 * (as opposed to running a builtin command or just typing return),
3757 * and the jobs command may give out of date information.
3758 */
3759static int
3760waitproc(int block, int *status)
3761{
3762 int flags = 0;
3763
3764#if JOBS
3765 if (jobctl)
3766 flags |= WUNTRACED;
3767#endif
3768 if (block == 0)
3769 flags |= WNOHANG;
Denis Vlasenkofb0eba72008-01-02 19:55:04 +00003770 return waitpid(-1, status, flags); // safe_waitpid?
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003771}
3772
3773/*
3774 * Wait for a process to terminate.
3775 */
3776static int
3777dowait(int block, struct job *job)
3778{
3779 int pid;
3780 int status;
3781 struct job *jp;
3782 struct job *thisjob;
3783 int state;
3784
3785 TRACE(("dowait(%d) called\n", block));
3786 pid = waitproc(block, &status);
3787 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3788 if (pid <= 0)
3789 return pid;
3790 INT_OFF;
3791 thisjob = NULL;
3792 for (jp = curjob; jp; jp = jp->prev_job) {
3793 struct procstat *sp;
3794 struct procstat *spend;
3795 if (jp->state == JOBDONE)
3796 continue;
3797 state = JOBDONE;
3798 spend = jp->ps + jp->nprocs;
3799 sp = jp->ps;
3800 do {
3801 if (sp->pid == pid) {
3802 TRACE(("Job %d: changing status of proc %d "
3803 "from 0x%x to 0x%x\n",
3804 jobno(jp), pid, sp->status, status));
3805 sp->status = status;
3806 thisjob = jp;
3807 }
3808 if (sp->status == -1)
3809 state = JOBRUNNING;
3810#if JOBS
3811 if (state == JOBRUNNING)
3812 continue;
3813 if (WIFSTOPPED(sp->status)) {
3814 jp->stopstatus = sp->status;
3815 state = JOBSTOPPED;
3816 }
3817#endif
3818 } while (++sp < spend);
3819 if (thisjob)
3820 goto gotjob;
3821 }
3822#if JOBS
3823 if (!WIFSTOPPED(status))
3824#endif
3825
3826 jobless--;
3827 goto out;
3828
3829 gotjob:
3830 if (state != JOBRUNNING) {
3831 thisjob->changed = 1;
3832
3833 if (thisjob->state != state) {
3834 TRACE(("Job %d: changing state from %d to %d\n",
3835 jobno(thisjob), thisjob->state, state));
3836 thisjob->state = state;
3837#if JOBS
3838 if (state == JOBSTOPPED) {
3839 set_curjob(thisjob, CUR_STOPPED);
3840 }
3841#endif
3842 }
3843 }
3844
3845 out:
3846 INT_ON;
3847
3848 if (thisjob && thisjob == job) {
3849 char s[48 + 1];
3850 int len;
3851
3852 len = sprint_status(s, status, 1);
3853 if (len) {
3854 s[len] = '\n';
3855 s[len + 1] = 0;
3856 out2str(s);
3857 }
3858 }
3859 return pid;
3860}
3861
3862#if JOBS
3863static void
3864showjob(FILE *out, struct job *jp, int mode)
3865{
3866 struct procstat *ps;
3867 struct procstat *psend;
3868 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003869 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003870 char s[80];
3871
3872 ps = jp->ps;
3873
3874 if (mode & SHOW_PGID) {
3875 /* just output process (group) id of pipeline */
3876 fprintf(out, "%d\n", ps->pid);
3877 return;
3878 }
3879
3880 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003881 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003882
3883 if (jp == curjob)
3884 s[col - 2] = '+';
3885 else if (curjob && jp == curjob->prev_job)
3886 s[col - 2] = '-';
3887
3888 if (mode & SHOW_PID)
3889 col += fmtstr(s + col, 16, "%d ", ps->pid);
3890
3891 psend = ps + jp->nprocs;
3892
3893 if (jp->state == JOBRUNNING) {
3894 strcpy(s + col, "Running");
3895 col += sizeof("Running") - 1;
3896 } else {
3897 int status = psend[-1].status;
3898 if (jp->state == JOBSTOPPED)
3899 status = jp->stopstatus;
3900 col += sprint_status(s + col, status, 0);
3901 }
3902
3903 goto start;
3904
3905 do {
3906 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003907 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003908 start:
3909 fprintf(out, "%s%*c%s",
3910 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3911 );
3912 if (!(mode & SHOW_PID)) {
3913 showpipe(jp, out);
3914 break;
3915 }
3916 if (++ps == psend) {
3917 outcslow('\n', out);
3918 break;
3919 }
3920 } while (1);
3921
3922 jp->changed = 0;
3923
3924 if (jp->state == JOBDONE) {
3925 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3926 freejob(jp);
3927 }
3928}
3929
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930/*
3931 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3932 * statuses have changed since the last call to showjobs.
3933 */
3934static void
3935showjobs(FILE *out, int mode)
3936{
3937 struct job *jp;
3938
3939 TRACE(("showjobs(%x) called\n", mode));
3940
3941 /* If not even one one job changed, there is nothing to do */
3942 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3943 continue;
3944
3945 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003946 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003947 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003948 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 }
3950}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003951
3952static int
3953jobscmd(int argc, char **argv)
3954{
3955 int mode, m;
3956
3957 mode = 0;
3958 while ((m = nextopt("lp"))) {
3959 if (m == 'l')
3960 mode = SHOW_PID;
3961 else
3962 mode = SHOW_PGID;
3963 }
3964
3965 argv = argptr;
3966 if (*argv) {
3967 do
3968 showjob(stdout, getjob(*argv,0), mode);
3969 while (*++argv);
3970 } else
3971 showjobs(stdout, mode);
3972
3973 return 0;
3974}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975#endif /* JOBS */
3976
3977static int
3978getstatus(struct job *job)
3979{
3980 int status;
3981 int retval;
3982
3983 status = job->ps[job->nprocs - 1].status;
3984 retval = WEXITSTATUS(status);
3985 if (!WIFEXITED(status)) {
3986#if JOBS
3987 retval = WSTOPSIG(status);
3988 if (!WIFSTOPPED(status))
3989#endif
3990 {
3991 /* XXX: limits number of signals */
3992 retval = WTERMSIG(status);
3993#if JOBS
3994 if (retval == SIGINT)
3995 job->sigint = 1;
3996#endif
3997 }
3998 retval += 128;
3999 }
4000 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4001 jobno(job), job->nprocs, status, retval));
4002 return retval;
4003}
4004
4005static int
4006waitcmd(int argc, char **argv)
4007{
4008 struct job *job;
4009 int retval;
4010 struct job *jp;
4011
4012 EXSIGON;
4013
4014 nextopt(nullstr);
4015 retval = 0;
4016
4017 argv = argptr;
4018 if (!*argv) {
4019 /* wait for all jobs */
4020 for (;;) {
4021 jp = curjob;
4022 while (1) {
4023 if (!jp) {
4024 /* no running procs */
4025 goto out;
4026 }
4027 if (jp->state == JOBRUNNING)
4028 break;
4029 jp->waited = 1;
4030 jp = jp->prev_job;
4031 }
4032 dowait(DOWAIT_BLOCK, 0);
4033 }
4034 }
4035
4036 retval = 127;
4037 do {
4038 if (**argv != '%') {
4039 pid_t pid = number(*argv);
4040 job = curjob;
4041 goto start;
4042 do {
4043 if (job->ps[job->nprocs - 1].pid == pid)
4044 break;
4045 job = job->prev_job;
4046 start:
4047 if (!job)
4048 goto repeat;
4049 } while (1);
4050 } else
4051 job = getjob(*argv, 0);
4052 /* loop until process terminated or stopped */
4053 while (job->state == JOBRUNNING)
4054 dowait(DOWAIT_BLOCK, 0);
4055 job->waited = 1;
4056 retval = getstatus(job);
4057 repeat:
4058 ;
4059 } while (*++argv);
4060
4061 out:
4062 return retval;
4063}
4064
4065static struct job *
4066growjobtab(void)
4067{
4068 size_t len;
4069 ptrdiff_t offset;
4070 struct job *jp, *jq;
4071
4072 len = njobs * sizeof(*jp);
4073 jq = jobtab;
4074 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4075
4076 offset = (char *)jp - (char *)jq;
4077 if (offset) {
4078 /* Relocate pointers */
4079 size_t l = len;
4080
4081 jq = (struct job *)((char *)jq + l);
4082 while (l) {
4083 l -= sizeof(*jp);
4084 jq--;
4085#define joff(p) ((struct job *)((char *)(p) + l))
4086#define jmove(p) (p) = (void *)((char *)(p) + offset)
4087 if (joff(jp)->ps == &jq->ps0)
4088 jmove(joff(jp)->ps);
4089 if (joff(jp)->prev_job)
4090 jmove(joff(jp)->prev_job);
4091 }
4092 if (curjob)
4093 jmove(curjob);
4094#undef joff
4095#undef jmove
4096 }
4097
4098 njobs += 4;
4099 jobtab = jp;
4100 jp = (struct job *)((char *)jp + len);
4101 jq = jp + 3;
4102 do {
4103 jq->used = 0;
4104 } while (--jq >= jp);
4105 return jp;
4106}
4107
4108/*
4109 * Return a new job structure.
4110 * Called with interrupts off.
4111 */
4112static struct job *
4113makejob(union node *node, int nprocs)
4114{
4115 int i;
4116 struct job *jp;
4117
4118 for (i = njobs, jp = jobtab; ; jp++) {
4119 if (--i < 0) {
4120 jp = growjobtab();
4121 break;
4122 }
4123 if (jp->used == 0)
4124 break;
4125 if (jp->state != JOBDONE || !jp->waited)
4126 continue;
4127#if JOBS
4128 if (jobctl)
4129 continue;
4130#endif
4131 freejob(jp);
4132 break;
4133 }
4134 memset(jp, 0, sizeof(*jp));
4135#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004136 /* jp->jobctl is a bitfield.
4137 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138 if (jobctl)
4139 jp->jobctl = 1;
4140#endif
4141 jp->prev_job = curjob;
4142 curjob = jp;
4143 jp->used = 1;
4144 jp->ps = &jp->ps0;
4145 if (nprocs > 1) {
4146 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4147 }
4148 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4149 jobno(jp)));
4150 return jp;
4151}
4152
4153#if JOBS
4154/*
4155 * Return a string identifying a command (to be printed by the
4156 * jobs command).
4157 */
4158static char *cmdnextc;
4159
4160static void
4161cmdputs(const char *s)
4162{
4163 const char *p, *str;
4164 char c, cc[2] = " ";
4165 char *nextc;
4166 int subtype = 0;
4167 int quoted = 0;
4168 static const char vstype[VSTYPE + 1][4] = {
4169 "", "}", "-", "+", "?", "=",
4170 "%", "%%", "#", "##"
4171 };
4172
4173 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4174 p = s;
4175 while ((c = *p++) != 0) {
4176 str = 0;
4177 switch (c) {
4178 case CTLESC:
4179 c = *p++;
4180 break;
4181 case CTLVAR:
4182 subtype = *p++;
4183 if ((subtype & VSTYPE) == VSLENGTH)
4184 str = "${#";
4185 else
4186 str = "${";
4187 if (!(subtype & VSQUOTE) == !(quoted & 1))
4188 goto dostr;
4189 quoted ^= 1;
4190 c = '"';
4191 break;
4192 case CTLENDVAR:
4193 str = "\"}" + !(quoted & 1);
4194 quoted >>= 1;
4195 subtype = 0;
4196 goto dostr;
4197 case CTLBACKQ:
4198 str = "$(...)";
4199 goto dostr;
4200 case CTLBACKQ+CTLQUOTE:
4201 str = "\"$(...)\"";
4202 goto dostr;
4203#if ENABLE_ASH_MATH_SUPPORT
4204 case CTLARI:
4205 str = "$((";
4206 goto dostr;
4207 case CTLENDARI:
4208 str = "))";
4209 goto dostr;
4210#endif
4211 case CTLQUOTEMARK:
4212 quoted ^= 1;
4213 c = '"';
4214 break;
4215 case '=':
4216 if (subtype == 0)
4217 break;
4218 if ((subtype & VSTYPE) != VSNORMAL)
4219 quoted <<= 1;
4220 str = vstype[subtype & VSTYPE];
4221 if (subtype & VSNUL)
4222 c = ':';
4223 else
4224 goto checkstr;
4225 break;
4226 case '\'':
4227 case '\\':
4228 case '"':
4229 case '$':
4230 /* These can only happen inside quotes */
4231 cc[0] = c;
4232 str = cc;
4233 c = '\\';
4234 break;
4235 default:
4236 break;
4237 }
4238 USTPUTC(c, nextc);
4239 checkstr:
4240 if (!str)
4241 continue;
4242 dostr:
4243 while ((c = *str++)) {
4244 USTPUTC(c, nextc);
4245 }
4246 }
4247 if (quoted & 1) {
4248 USTPUTC('"', nextc);
4249 }
4250 *nextc = 0;
4251 cmdnextc = nextc;
4252}
4253
4254/* cmdtxt() and cmdlist() call each other */
4255static void cmdtxt(union node *n);
4256
4257static void
4258cmdlist(union node *np, int sep)
4259{
4260 for (; np; np = np->narg.next) {
4261 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004262 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004263 cmdtxt(np);
4264 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004265 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004266 }
4267}
4268
4269static void
4270cmdtxt(union node *n)
4271{
4272 union node *np;
4273 struct nodelist *lp;
4274 const char *p;
4275 char s[2];
4276
4277 if (!n)
4278 return;
4279 switch (n->type) {
4280 default:
4281#if DEBUG
4282 abort();
4283#endif
4284 case NPIPE:
4285 lp = n->npipe.cmdlist;
4286 for (;;) {
4287 cmdtxt(lp->n);
4288 lp = lp->next;
4289 if (!lp)
4290 break;
4291 cmdputs(" | ");
4292 }
4293 break;
4294 case NSEMI:
4295 p = "; ";
4296 goto binop;
4297 case NAND:
4298 p = " && ";
4299 goto binop;
4300 case NOR:
4301 p = " || ";
4302 binop:
4303 cmdtxt(n->nbinary.ch1);
4304 cmdputs(p);
4305 n = n->nbinary.ch2;
4306 goto donode;
4307 case NREDIR:
4308 case NBACKGND:
4309 n = n->nredir.n;
4310 goto donode;
4311 case NNOT:
4312 cmdputs("!");
4313 n = n->nnot.com;
4314 donode:
4315 cmdtxt(n);
4316 break;
4317 case NIF:
4318 cmdputs("if ");
4319 cmdtxt(n->nif.test);
4320 cmdputs("; then ");
4321 n = n->nif.ifpart;
4322 if (n->nif.elsepart) {
4323 cmdtxt(n);
4324 cmdputs("; else ");
4325 n = n->nif.elsepart;
4326 }
4327 p = "; fi";
4328 goto dotail;
4329 case NSUBSHELL:
4330 cmdputs("(");
4331 n = n->nredir.n;
4332 p = ")";
4333 goto dotail;
4334 case NWHILE:
4335 p = "while ";
4336 goto until;
4337 case NUNTIL:
4338 p = "until ";
4339 until:
4340 cmdputs(p);
4341 cmdtxt(n->nbinary.ch1);
4342 n = n->nbinary.ch2;
4343 p = "; done";
4344 dodo:
4345 cmdputs("; do ");
4346 dotail:
4347 cmdtxt(n);
4348 goto dotail2;
4349 case NFOR:
4350 cmdputs("for ");
4351 cmdputs(n->nfor.var);
4352 cmdputs(" in ");
4353 cmdlist(n->nfor.args, 1);
4354 n = n->nfor.body;
4355 p = "; done";
4356 goto dodo;
4357 case NDEFUN:
4358 cmdputs(n->narg.text);
4359 p = "() { ... }";
4360 goto dotail2;
4361 case NCMD:
4362 cmdlist(n->ncmd.args, 1);
4363 cmdlist(n->ncmd.redirect, 0);
4364 break;
4365 case NARG:
4366 p = n->narg.text;
4367 dotail2:
4368 cmdputs(p);
4369 break;
4370 case NHERE:
4371 case NXHERE:
4372 p = "<<...";
4373 goto dotail2;
4374 case NCASE:
4375 cmdputs("case ");
4376 cmdputs(n->ncase.expr->narg.text);
4377 cmdputs(" in ");
4378 for (np = n->ncase.cases; np; np = np->nclist.next) {
4379 cmdtxt(np->nclist.pattern);
4380 cmdputs(") ");
4381 cmdtxt(np->nclist.body);
4382 cmdputs(";; ");
4383 }
4384 p = "esac";
4385 goto dotail2;
4386 case NTO:
4387 p = ">";
4388 goto redir;
4389 case NCLOBBER:
4390 p = ">|";
4391 goto redir;
4392 case NAPPEND:
4393 p = ">>";
4394 goto redir;
4395 case NTOFD:
4396 p = ">&";
4397 goto redir;
4398 case NFROM:
4399 p = "<";
4400 goto redir;
4401 case NFROMFD:
4402 p = "<&";
4403 goto redir;
4404 case NFROMTO:
4405 p = "<>";
4406 redir:
4407 s[0] = n->nfile.fd + '0';
4408 s[1] = '\0';
4409 cmdputs(s);
4410 cmdputs(p);
4411 if (n->type == NTOFD || n->type == NFROMFD) {
4412 s[0] = n->ndup.dupfd + '0';
4413 p = s;
4414 goto dotail2;
4415 }
4416 n = n->nfile.fname;
4417 goto donode;
4418 }
4419}
4420
4421static char *
4422commandtext(union node *n)
4423{
4424 char *name;
4425
4426 STARTSTACKSTR(cmdnextc);
4427 cmdtxt(n);
4428 name = stackblock();
4429 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4430 name, cmdnextc, cmdnextc));
4431 return ckstrdup(name);
4432}
4433#endif /* JOBS */
4434
4435/*
4436 * Fork off a subshell. If we are doing job control, give the subshell its
4437 * own process group. Jp is a job structure that the job is to be added to.
4438 * N is the command that will be evaluated by the child. Both jp and n may
4439 * be NULL. The mode parameter can be one of the following:
4440 * FORK_FG - Fork off a foreground process.
4441 * FORK_BG - Fork off a background process.
4442 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4443 * process group even if job control is on.
4444 *
4445 * When job control is turned off, background processes have their standard
4446 * input redirected to /dev/null (except for the second and later processes
4447 * in a pipeline).
4448 *
4449 * Called with interrupts off.
4450 */
4451/*
4452 * Clear traps on a fork.
4453 */
4454static void
4455clear_traps(void)
4456{
4457 char **tp;
4458
4459 for (tp = trap; tp < &trap[NSIG]; tp++) {
4460 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4461 INT_OFF;
4462 free(*tp);
4463 *tp = NULL;
4464 if (tp != &trap[0])
4465 setsignal(tp - trap);
4466 INT_ON;
4467 }
4468 }
4469}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004470
4471/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004472static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004473
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004474/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004475static void
4476forkchild(struct job *jp, union node *n, int mode)
4477{
4478 int oldlvl;
4479
4480 TRACE(("Child shell %d\n", getpid()));
4481 oldlvl = shlvl;
4482 shlvl++;
4483
4484 closescript();
4485 clear_traps();
4486#if JOBS
4487 /* do job control only in root shell */
4488 jobctl = 0;
4489 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4490 pid_t pgrp;
4491
4492 if (jp->nprocs == 0)
4493 pgrp = getpid();
4494 else
4495 pgrp = jp->ps[0].pid;
4496 /* This can fail because we are doing it in the parent also */
4497 (void)setpgid(0, pgrp);
4498 if (mode == FORK_FG)
4499 xtcsetpgrp(ttyfd, pgrp);
4500 setsignal(SIGTSTP);
4501 setsignal(SIGTTOU);
4502 } else
4503#endif
4504 if (mode == FORK_BG) {
4505 ignoresig(SIGINT);
4506 ignoresig(SIGQUIT);
4507 if (jp->nprocs == 0) {
4508 close(0);
4509 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004510 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004511 }
4512 }
4513 if (!oldlvl && iflag) {
4514 setsignal(SIGINT);
4515 setsignal(SIGQUIT);
4516 setsignal(SIGTERM);
4517 }
4518 for (jp = curjob; jp; jp = jp->prev_job)
4519 freejob(jp);
4520 jobless = 0;
4521}
4522
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004523/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524static void
4525forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4526{
4527 TRACE(("In parent shell: child = %d\n", pid));
4528 if (!jp) {
4529 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4530 jobless++;
4531 return;
4532 }
4533#if JOBS
4534 if (mode != FORK_NOJOB && jp->jobctl) {
4535 int pgrp;
4536
4537 if (jp->nprocs == 0)
4538 pgrp = pid;
4539 else
4540 pgrp = jp->ps[0].pid;
4541 /* This can fail because we are doing it in the child also */
4542 setpgid(pid, pgrp);
4543 }
4544#endif
4545 if (mode == FORK_BG) {
4546 backgndpid = pid; /* set $! */
4547 set_curjob(jp, CUR_RUNNING);
4548 }
4549 if (jp) {
4550 struct procstat *ps = &jp->ps[jp->nprocs++];
4551 ps->pid = pid;
4552 ps->status = -1;
4553 ps->cmd = nullstr;
4554#if JOBS
4555 if (jobctl && n)
4556 ps->cmd = commandtext(n);
4557#endif
4558 }
4559}
4560
4561static int
4562forkshell(struct job *jp, union node *n, int mode)
4563{
4564 int pid;
4565
4566 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4567 pid = fork();
4568 if (pid < 0) {
4569 TRACE(("Fork failed, errno=%d", errno));
4570 if (jp)
4571 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004572 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004573 }
4574 if (pid == 0)
4575 forkchild(jp, n, mode);
4576 else
4577 forkparent(jp, n, mode, pid);
4578 return pid;
4579}
4580
4581/*
4582 * Wait for job to finish.
4583 *
4584 * Under job control we have the problem that while a child process is
4585 * running interrupts generated by the user are sent to the child but not
4586 * to the shell. This means that an infinite loop started by an inter-
4587 * active user may be hard to kill. With job control turned off, an
4588 * interactive user may place an interactive program inside a loop. If
4589 * the interactive program catches interrupts, the user doesn't want
4590 * these interrupts to also abort the loop. The approach we take here
4591 * is to have the shell ignore interrupt signals while waiting for a
4592 * foreground process to terminate, and then send itself an interrupt
4593 * signal if the child process was terminated by an interrupt signal.
4594 * Unfortunately, some programs want to do a bit of cleanup and then
4595 * exit on interrupt; unless these processes terminate themselves by
4596 * sending a signal to themselves (instead of calling exit) they will
4597 * confuse this approach.
4598 *
4599 * Called with interrupts off.
4600 */
4601static int
4602waitforjob(struct job *jp)
4603{
4604 int st;
4605
4606 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4607 while (jp->state == JOBRUNNING) {
4608 dowait(DOWAIT_BLOCK, jp);
4609 }
4610 st = getstatus(jp);
4611#if JOBS
4612 if (jp->jobctl) {
4613 xtcsetpgrp(ttyfd, rootpid);
4614 /*
4615 * This is truly gross.
4616 * If we're doing job control, then we did a TIOCSPGRP which
4617 * caused us (the shell) to no longer be in the controlling
4618 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4619 * intuit from the subprocess exit status whether a SIGINT
4620 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4621 */
4622 if (jp->sigint)
4623 raise(SIGINT);
4624 }
4625 if (jp->state == JOBDONE)
4626#endif
4627 freejob(jp);
4628 return st;
4629}
4630
4631/*
4632 * return 1 if there are stopped jobs, otherwise 0
4633 */
4634static int
4635stoppedjobs(void)
4636{
4637 struct job *jp;
4638 int retval;
4639
4640 retval = 0;
4641 if (job_warning)
4642 goto out;
4643 jp = curjob;
4644 if (jp && jp->state == JOBSTOPPED) {
4645 out2str("You have stopped jobs.\n");
4646 job_warning = 2;
4647 retval++;
4648 }
4649 out:
4650 return retval;
4651}
4652
4653
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004654/* ============ redir.c
4655 *
4656 * Code for dealing with input/output redirection.
4657 */
4658
4659#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004660#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004661#ifndef PIPE_BUF
4662# define PIPESIZE 4096 /* amount of buffering in a pipe */
4663#else
4664# define PIPESIZE PIPE_BUF
4665#endif
4666
4667/*
4668 * Open a file in noclobber mode.
4669 * The code was copied from bash.
4670 */
4671static int
4672noclobberopen(const char *fname)
4673{
4674 int r, fd;
4675 struct stat finfo, finfo2;
4676
4677 /*
4678 * If the file exists and is a regular file, return an error
4679 * immediately.
4680 */
4681 r = stat(fname, &finfo);
4682 if (r == 0 && S_ISREG(finfo.st_mode)) {
4683 errno = EEXIST;
4684 return -1;
4685 }
4686
4687 /*
4688 * If the file was not present (r != 0), make sure we open it
4689 * exclusively so that if it is created before we open it, our open
4690 * will fail. Make sure that we do not truncate an existing file.
4691 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4692 * file was not a regular file, we leave O_EXCL off.
4693 */
4694 if (r != 0)
4695 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4696 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4697
4698 /* If the open failed, return the file descriptor right away. */
4699 if (fd < 0)
4700 return fd;
4701
4702 /*
4703 * OK, the open succeeded, but the file may have been changed from a
4704 * non-regular file to a regular file between the stat and the open.
4705 * We are assuming that the O_EXCL open handles the case where FILENAME
4706 * did not exist and is symlinked to an existing file between the stat
4707 * and open.
4708 */
4709
4710 /*
4711 * If we can open it and fstat the file descriptor, and neither check
4712 * revealed that it was a regular file, and the file has not been
4713 * replaced, return the file descriptor.
4714 */
4715 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4716 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4717 return fd;
4718
4719 /* The file has been replaced. badness. */
4720 close(fd);
4721 errno = EEXIST;
4722 return -1;
4723}
4724
4725/*
4726 * Handle here documents. Normally we fork off a process to write the
4727 * data to a pipe. If the document is short, we can stuff the data in
4728 * the pipe without forking.
4729 */
4730/* openhere needs this forward reference */
4731static void expandhere(union node *arg, int fd);
4732static int
4733openhere(union node *redir)
4734{
4735 int pip[2];
4736 size_t len = 0;
4737
4738 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004739 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004740 if (redir->type == NHERE) {
4741 len = strlen(redir->nhere.doc->narg.text);
4742 if (len <= PIPESIZE) {
4743 full_write(pip[1], redir->nhere.doc->narg.text, len);
4744 goto out;
4745 }
4746 }
4747 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4748 close(pip[0]);
4749 signal(SIGINT, SIG_IGN);
4750 signal(SIGQUIT, SIG_IGN);
4751 signal(SIGHUP, SIG_IGN);
4752#ifdef SIGTSTP
4753 signal(SIGTSTP, SIG_IGN);
4754#endif
4755 signal(SIGPIPE, SIG_DFL);
4756 if (redir->type == NHERE)
4757 full_write(pip[1], redir->nhere.doc->narg.text, len);
4758 else
4759 expandhere(redir->nhere.doc, pip[1]);
4760 _exit(0);
4761 }
4762 out:
4763 close(pip[1]);
4764 return pip[0];
4765}
4766
4767static int
4768openredirect(union node *redir)
4769{
4770 char *fname;
4771 int f;
4772
4773 switch (redir->nfile.type) {
4774 case NFROM:
4775 fname = redir->nfile.expfname;
4776 f = open(fname, O_RDONLY);
4777 if (f < 0)
4778 goto eopen;
4779 break;
4780 case NFROMTO:
4781 fname = redir->nfile.expfname;
4782 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4783 if (f < 0)
4784 goto ecreate;
4785 break;
4786 case NTO:
4787 /* Take care of noclobber mode. */
4788 if (Cflag) {
4789 fname = redir->nfile.expfname;
4790 f = noclobberopen(fname);
4791 if (f < 0)
4792 goto ecreate;
4793 break;
4794 }
4795 /* FALLTHROUGH */
4796 case NCLOBBER:
4797 fname = redir->nfile.expfname;
4798 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4799 if (f < 0)
4800 goto ecreate;
4801 break;
4802 case NAPPEND:
4803 fname = redir->nfile.expfname;
4804 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4805 if (f < 0)
4806 goto ecreate;
4807 break;
4808 default:
4809#if DEBUG
4810 abort();
4811#endif
4812 /* Fall through to eliminate warning. */
4813 case NTOFD:
4814 case NFROMFD:
4815 f = -1;
4816 break;
4817 case NHERE:
4818 case NXHERE:
4819 f = openhere(redir);
4820 break;
4821 }
4822
4823 return f;
4824 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004825 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004826 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004827 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004828}
4829
4830/*
4831 * Copy a file descriptor to be >= to. Returns -1
4832 * if the source file descriptor is closed, EMPTY if there are no unused
4833 * file descriptors left.
4834 */
4835static int
4836copyfd(int from, int to)
4837{
4838 int newfd;
4839
4840 newfd = fcntl(from, F_DUPFD, to);
4841 if (newfd < 0) {
4842 if (errno == EMFILE)
4843 return EMPTY;
4844 ash_msg_and_raise_error("%d: %m", from);
4845 }
4846 return newfd;
4847}
4848
4849static void
4850dupredirect(union node *redir, int f)
4851{
4852 int fd = redir->nfile.fd;
4853
4854 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4855 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4856 copyfd(redir->ndup.dupfd, fd);
4857 }
4858 return;
4859 }
4860
4861 if (f != fd) {
4862 copyfd(f, fd);
4863 close(f);
4864 }
4865}
4866
4867/*
4868 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4869 * old file descriptors are stashed away so that the redirection can be
4870 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4871 * standard output, and the standard error if it becomes a duplicate of
4872 * stdout, is saved in memory.
4873 */
4874/* flags passed to redirect */
4875#define REDIR_PUSH 01 /* save previous values of file descriptors */
4876#define REDIR_SAVEFD2 03 /* set preverrout */
4877static void
4878redirect(union node *redir, int flags)
4879{
4880 union node *n;
4881 struct redirtab *sv;
4882 int i;
4883 int fd;
4884 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004885
Denis Vlasenko01631112007-12-16 17:20:38 +00004886 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004887 if (!redir) {
4888 return;
4889 }
4890 sv = NULL;
4891 INT_OFF;
4892 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004893 sv = ckmalloc(sizeof(*sv));
4894 sv->next = redirlist;
4895 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004896 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004897 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004898 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004899 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004900 }
4901 n = redir;
4902 do {
4903 fd = n->nfile.fd;
4904 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4905 && n->ndup.dupfd == fd)
4906 continue; /* redirect from/to same file descriptor */
4907
4908 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004909 if (fd == newfd) {
4910 /* Descriptor wasn't open before redirect.
4911 * Mark it for close in the future */
4912 if (sv && sv->renamed[fd] == EMPTY)
4913 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004914 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004915 }
4916 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004917 i = fcntl(fd, F_DUPFD, 10);
4918
4919 if (i == -1) {
4920 i = errno;
4921 if (i != EBADF) {
4922 close(newfd);
4923 errno = i;
4924 ash_msg_and_raise_error("%d: %m", fd);
4925 /* NOTREACHED */
4926 }
4927 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004928 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004929 close(fd);
4930 }
4931 } else {
4932 close(fd);
4933 }
4934 dupredirect(n, newfd);
4935 } while ((n = n->nfile.next));
4936 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004937 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004938 preverrout_fd = sv->renamed[2];
4939}
4940
4941/*
4942 * Undo the effects of the last redirection.
4943 */
4944static void
4945popredir(int drop)
4946{
4947 struct redirtab *rp;
4948 int i;
4949
Denis Vlasenko01631112007-12-16 17:20:38 +00004950 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951 return;
4952 INT_OFF;
4953 rp = redirlist;
4954 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004955 if (rp->renamed[i] == CLOSED) {
4956 if (!drop)
4957 close(i);
4958 continue;
4959 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004960 if (rp->renamed[i] != EMPTY) {
4961 if (!drop) {
4962 close(i);
4963 copyfd(rp->renamed[i], i);
4964 }
4965 close(rp->renamed[i]);
4966 }
4967 }
4968 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004969 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004970 free(rp);
4971 INT_ON;
4972}
4973
4974/*
4975 * Undo all redirections. Called on error or interrupt.
4976 */
4977
4978/*
4979 * Discard all saved file descriptors.
4980 */
4981static void
4982clearredir(int drop)
4983{
4984 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004985 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004986 if (!redirlist)
4987 break;
4988 popredir(drop);
4989 }
4990}
4991
4992static int
4993redirectsafe(union node *redir, int flags)
4994{
4995 int err;
4996 volatile int saveint;
4997 struct jmploc *volatile savehandler = exception_handler;
4998 struct jmploc jmploc;
4999
5000 SAVE_INT(saveint);
5001 err = setjmp(jmploc.loc) * 2;
5002 if (!err) {
5003 exception_handler = &jmploc;
5004 redirect(redir, flags);
5005 }
5006 exception_handler = savehandler;
5007 if (err && exception != EXERROR)
5008 longjmp(exception_handler->loc, 1);
5009 RESTORE_INT(saveint);
5010 return err;
5011}
5012
5013
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005014/* ============ Routines to expand arguments to commands
5015 *
5016 * We have to deal with backquotes, shell variables, and file metacharacters.
5017 */
5018
5019/*
5020 * expandarg flags
5021 */
5022#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5023#define EXP_TILDE 0x2 /* do normal tilde expansion */
5024#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5025#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5026#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5027#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5028#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5029#define EXP_WORD 0x80 /* expand word in parameter expansion */
5030#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5031/*
5032 * _rmescape() flags
5033 */
5034#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5035#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5036#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5037#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5038#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5039
5040/*
5041 * Structure specifying which parts of the string should be searched
5042 * for IFS characters.
5043 */
5044struct ifsregion {
5045 struct ifsregion *next; /* next region in list */
5046 int begoff; /* offset of start of region */
5047 int endoff; /* offset of end of region */
5048 int nulonly; /* search for nul bytes only */
5049};
5050
5051struct arglist {
5052 struct strlist *list;
5053 struct strlist **lastp;
5054};
5055
5056/* output of current string */
5057static char *expdest;
5058/* list of back quote expressions */
5059static struct nodelist *argbackq;
5060/* first struct in list of ifs regions */
5061static struct ifsregion ifsfirst;
5062/* last struct in list */
5063static struct ifsregion *ifslastp;
5064/* holds expanded arg list */
5065static struct arglist exparg;
5066
5067/*
5068 * Our own itoa().
5069 */
5070static int
5071cvtnum(arith_t num)
5072{
5073 int len;
5074
5075 expdest = makestrspace(32, expdest);
5076#if ENABLE_ASH_MATH_SUPPORT_64
5077 len = fmtstr(expdest, 32, "%lld", (long long) num);
5078#else
5079 len = fmtstr(expdest, 32, "%ld", num);
5080#endif
5081 STADJUST(len, expdest);
5082 return len;
5083}
5084
5085static size_t
5086esclen(const char *start, const char *p)
5087{
5088 size_t esc = 0;
5089
5090 while (p > start && *--p == CTLESC) {
5091 esc++;
5092 }
5093 return esc;
5094}
5095
5096/*
5097 * Remove any CTLESC characters from a string.
5098 */
5099static char *
5100_rmescapes(char *str, int flag)
5101{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005102 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005103
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005104 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005105 unsigned inquotes;
5106 int notescaped;
5107 int globbing;
5108
5109 p = strpbrk(str, qchars);
5110 if (!p) {
5111 return str;
5112 }
5113 q = p;
5114 r = str;
5115 if (flag & RMESCAPE_ALLOC) {
5116 size_t len = p - str;
5117 size_t fulllen = len + strlen(p) + 1;
5118
5119 if (flag & RMESCAPE_GROW) {
5120 r = makestrspace(fulllen, expdest);
5121 } else if (flag & RMESCAPE_HEAP) {
5122 r = ckmalloc(fulllen);
5123 } else {
5124 r = stalloc(fulllen);
5125 }
5126 q = r;
5127 if (len > 0) {
5128 q = memcpy(q, str, len) + len;
5129 }
5130 }
5131 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5132 globbing = flag & RMESCAPE_GLOB;
5133 notescaped = globbing;
5134 while (*p) {
5135 if (*p == CTLQUOTEMARK) {
5136 inquotes = ~inquotes;
5137 p++;
5138 notescaped = globbing;
5139 continue;
5140 }
5141 if (*p == '\\') {
5142 /* naked back slash */
5143 notescaped = 0;
5144 goto copy;
5145 }
5146 if (*p == CTLESC) {
5147 p++;
5148 if (notescaped && inquotes && *p != '/') {
5149 *q++ = '\\';
5150 }
5151 }
5152 notescaped = globbing;
5153 copy:
5154 *q++ = *p++;
5155 }
5156 *q = '\0';
5157 if (flag & RMESCAPE_GROW) {
5158 expdest = r;
5159 STADJUST(q - r + 1, expdest);
5160 }
5161 return r;
5162}
5163#define rmescapes(p) _rmescapes((p), 0)
5164
5165#define pmatch(a, b) !fnmatch((a), (b), 0)
5166
5167/*
5168 * Prepare a pattern for a expmeta (internal glob(3)) call.
5169 *
5170 * Returns an stalloced string.
5171 */
5172static char *
5173preglob(const char *pattern, int quoted, int flag)
5174{
5175 flag |= RMESCAPE_GLOB;
5176 if (quoted) {
5177 flag |= RMESCAPE_QUOTED;
5178 }
5179 return _rmescapes((char *)pattern, flag);
5180}
5181
5182/*
5183 * Put a string on the stack.
5184 */
5185static void
5186memtodest(const char *p, size_t len, int syntax, int quotes)
5187{
5188 char *q = expdest;
5189
5190 q = makestrspace(len * 2, q);
5191
5192 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005193 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005194 if (!c)
5195 continue;
5196 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5197 USTPUTC(CTLESC, q);
5198 USTPUTC(c, q);
5199 }
5200
5201 expdest = q;
5202}
5203
5204static void
5205strtodest(const char *p, int syntax, int quotes)
5206{
5207 memtodest(p, strlen(p), syntax, quotes);
5208}
5209
5210/*
5211 * Record the fact that we have to scan this region of the
5212 * string for IFS characters.
5213 */
5214static void
5215recordregion(int start, int end, int nulonly)
5216{
5217 struct ifsregion *ifsp;
5218
5219 if (ifslastp == NULL) {
5220 ifsp = &ifsfirst;
5221 } else {
5222 INT_OFF;
5223 ifsp = ckmalloc(sizeof(*ifsp));
5224 ifsp->next = NULL;
5225 ifslastp->next = ifsp;
5226 INT_ON;
5227 }
5228 ifslastp = ifsp;
5229 ifslastp->begoff = start;
5230 ifslastp->endoff = end;
5231 ifslastp->nulonly = nulonly;
5232}
5233
5234static void
5235removerecordregions(int endoff)
5236{
5237 if (ifslastp == NULL)
5238 return;
5239
5240 if (ifsfirst.endoff > endoff) {
5241 while (ifsfirst.next != NULL) {
5242 struct ifsregion *ifsp;
5243 INT_OFF;
5244 ifsp = ifsfirst.next->next;
5245 free(ifsfirst.next);
5246 ifsfirst.next = ifsp;
5247 INT_ON;
5248 }
5249 if (ifsfirst.begoff > endoff)
5250 ifslastp = NULL;
5251 else {
5252 ifslastp = &ifsfirst;
5253 ifsfirst.endoff = endoff;
5254 }
5255 return;
5256 }
5257
5258 ifslastp = &ifsfirst;
5259 while (ifslastp->next && ifslastp->next->begoff < endoff)
5260 ifslastp=ifslastp->next;
5261 while (ifslastp->next != NULL) {
5262 struct ifsregion *ifsp;
5263 INT_OFF;
5264 ifsp = ifslastp->next->next;
5265 free(ifslastp->next);
5266 ifslastp->next = ifsp;
5267 INT_ON;
5268 }
5269 if (ifslastp->endoff > endoff)
5270 ifslastp->endoff = endoff;
5271}
5272
5273static char *
5274exptilde(char *startp, char *p, int flag)
5275{
5276 char c;
5277 char *name;
5278 struct passwd *pw;
5279 const char *home;
5280 int quotes = flag & (EXP_FULL | EXP_CASE);
5281 int startloc;
5282
5283 name = p + 1;
5284
5285 while ((c = *++p) != '\0') {
5286 switch (c) {
5287 case CTLESC:
5288 return startp;
5289 case CTLQUOTEMARK:
5290 return startp;
5291 case ':':
5292 if (flag & EXP_VARTILDE)
5293 goto done;
5294 break;
5295 case '/':
5296 case CTLENDVAR:
5297 goto done;
5298 }
5299 }
5300 done:
5301 *p = '\0';
5302 if (*name == '\0') {
5303 home = lookupvar(homestr);
5304 } else {
5305 pw = getpwnam(name);
5306 if (pw == NULL)
5307 goto lose;
5308 home = pw->pw_dir;
5309 }
5310 if (!home || !*home)
5311 goto lose;
5312 *p = c;
5313 startloc = expdest - (char *)stackblock();
5314 strtodest(home, SQSYNTAX, quotes);
5315 recordregion(startloc, expdest - (char *)stackblock(), 0);
5316 return p;
5317 lose:
5318 *p = c;
5319 return startp;
5320}
5321
5322/*
5323 * Execute a command inside back quotes. If it's a builtin command, we
5324 * want to save its output in a block obtained from malloc. Otherwise
5325 * we fork off a subprocess and get the output of the command via a pipe.
5326 * Should be called with interrupts off.
5327 */
5328struct backcmd { /* result of evalbackcmd */
5329 int fd; /* file descriptor to read from */
5330 char *buf; /* buffer */
5331 int nleft; /* number of chars in buffer */
5332 struct job *jp; /* job structure for command */
5333};
5334
5335/* These forward decls are needed to use "eval" code for backticks handling: */
5336static int back_exitstatus; /* exit status of backquoted command */
5337#define EV_EXIT 01 /* exit after evaluating tree */
5338static void evaltree(union node *, int);
5339
5340static void
5341evalbackcmd(union node *n, struct backcmd *result)
5342{
5343 int saveherefd;
5344
5345 result->fd = -1;
5346 result->buf = NULL;
5347 result->nleft = 0;
5348 result->jp = NULL;
5349 if (n == NULL) {
5350 goto out;
5351 }
5352
5353 saveherefd = herefd;
5354 herefd = -1;
5355
5356 {
5357 int pip[2];
5358 struct job *jp;
5359
5360 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005361 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005362 jp = makejob(n, 1);
5363 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5364 FORCE_INT_ON;
5365 close(pip[0]);
5366 if (pip[1] != 1) {
5367 close(1);
5368 copyfd(pip[1], 1);
5369 close(pip[1]);
5370 }
5371 eflag = 0;
5372 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5373 /* NOTREACHED */
5374 }
5375 close(pip[1]);
5376 result->fd = pip[0];
5377 result->jp = jp;
5378 }
5379 herefd = saveherefd;
5380 out:
5381 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5382 result->fd, result->buf, result->nleft, result->jp));
5383}
5384
5385/*
5386 * Expand stuff in backwards quotes.
5387 */
5388static void
5389expbackq(union node *cmd, int quoted, int quotes)
5390{
5391 struct backcmd in;
5392 int i;
5393 char buf[128];
5394 char *p;
5395 char *dest;
5396 int startloc;
5397 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5398 struct stackmark smark;
5399
5400 INT_OFF;
5401 setstackmark(&smark);
5402 dest = expdest;
5403 startloc = dest - (char *)stackblock();
5404 grabstackstr(dest);
5405 evalbackcmd(cmd, &in);
5406 popstackmark(&smark);
5407
5408 p = in.buf;
5409 i = in.nleft;
5410 if (i == 0)
5411 goto read;
5412 for (;;) {
5413 memtodest(p, i, syntax, quotes);
5414 read:
5415 if (in.fd < 0)
5416 break;
5417 i = safe_read(in.fd, buf, sizeof(buf));
5418 TRACE(("expbackq: read returns %d\n", i));
5419 if (i <= 0)
5420 break;
5421 p = buf;
5422 }
5423
Denis Vlasenko60818682007-09-28 22:07:23 +00005424 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005425 if (in.fd >= 0) {
5426 close(in.fd);
5427 back_exitstatus = waitforjob(in.jp);
5428 }
5429 INT_ON;
5430
5431 /* Eat all trailing newlines */
5432 dest = expdest;
5433 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5434 STUNPUTC(dest);
5435 expdest = dest;
5436
5437 if (quoted == 0)
5438 recordregion(startloc, dest - (char *)stackblock(), 0);
5439 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5440 (dest - (char *)stackblock()) - startloc,
5441 (dest - (char *)stackblock()) - startloc,
5442 stackblock() + startloc));
5443}
5444
5445#if ENABLE_ASH_MATH_SUPPORT
5446/*
5447 * Expand arithmetic expression. Backup to start of expression,
5448 * evaluate, place result in (backed up) result, adjust string position.
5449 */
5450static void
5451expari(int quotes)
5452{
5453 char *p, *start;
5454 int begoff;
5455 int flag;
5456 int len;
5457
5458 /* ifsfree(); */
5459
5460 /*
5461 * This routine is slightly over-complicated for
5462 * efficiency. Next we scan backwards looking for the
5463 * start of arithmetic.
5464 */
5465 start = stackblock();
5466 p = expdest - 1;
5467 *p = '\0';
5468 p--;
5469 do {
5470 int esc;
5471
5472 while (*p != CTLARI) {
5473 p--;
5474#if DEBUG
5475 if (p < start) {
5476 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5477 }
5478#endif
5479 }
5480
5481 esc = esclen(start, p);
5482 if (!(esc % 2)) {
5483 break;
5484 }
5485
5486 p -= esc + 1;
5487 } while (1);
5488
5489 begoff = p - start;
5490
5491 removerecordregions(begoff);
5492
5493 flag = p[1];
5494
5495 expdest = p;
5496
5497 if (quotes)
5498 rmescapes(p + 2);
5499
5500 len = cvtnum(dash_arith(p + 2));
5501
5502 if (flag != '"')
5503 recordregion(begoff, begoff + len, 0);
5504}
5505#endif
5506
5507/* argstr needs it */
5508static char *evalvar(char *p, int flag);
5509
5510/*
5511 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5512 * characters to allow for further processing. Otherwise treat
5513 * $@ like $* since no splitting will be performed.
5514 */
5515static void
5516argstr(char *p, int flag)
5517{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005518 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005519 '=',
5520 ':',
5521 CTLQUOTEMARK,
5522 CTLENDVAR,
5523 CTLESC,
5524 CTLVAR,
5525 CTLBACKQ,
5526 CTLBACKQ | CTLQUOTE,
5527#if ENABLE_ASH_MATH_SUPPORT
5528 CTLENDARI,
5529#endif
5530 0
5531 };
5532 const char *reject = spclchars;
5533 int c;
5534 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5535 int breakall = flag & EXP_WORD;
5536 int inquotes;
5537 size_t length;
5538 int startloc;
5539
5540 if (!(flag & EXP_VARTILDE)) {
5541 reject += 2;
5542 } else if (flag & EXP_VARTILDE2) {
5543 reject++;
5544 }
5545 inquotes = 0;
5546 length = 0;
5547 if (flag & EXP_TILDE) {
5548 char *q;
5549
5550 flag &= ~EXP_TILDE;
5551 tilde:
5552 q = p;
5553 if (*q == CTLESC && (flag & EXP_QWORD))
5554 q++;
5555 if (*q == '~')
5556 p = exptilde(p, q, flag);
5557 }
5558 start:
5559 startloc = expdest - (char *)stackblock();
5560 for (;;) {
5561 length += strcspn(p + length, reject);
5562 c = p[length];
5563 if (c && (!(c & 0x80)
5564#if ENABLE_ASH_MATH_SUPPORT
5565 || c == CTLENDARI
5566#endif
5567 )) {
5568 /* c == '=' || c == ':' || c == CTLENDARI */
5569 length++;
5570 }
5571 if (length > 0) {
5572 int newloc;
5573 expdest = stack_nputstr(p, length, expdest);
5574 newloc = expdest - (char *)stackblock();
5575 if (breakall && !inquotes && newloc > startloc) {
5576 recordregion(startloc, newloc, 0);
5577 }
5578 startloc = newloc;
5579 }
5580 p += length + 1;
5581 length = 0;
5582
5583 switch (c) {
5584 case '\0':
5585 goto breakloop;
5586 case '=':
5587 if (flag & EXP_VARTILDE2) {
5588 p--;
5589 continue;
5590 }
5591 flag |= EXP_VARTILDE2;
5592 reject++;
5593 /* fall through */
5594 case ':':
5595 /*
5596 * sort of a hack - expand tildes in variable
5597 * assignments (after the first '=' and after ':'s).
5598 */
5599 if (*--p == '~') {
5600 goto tilde;
5601 }
5602 continue;
5603 }
5604
5605 switch (c) {
5606 case CTLENDVAR: /* ??? */
5607 goto breakloop;
5608 case CTLQUOTEMARK:
5609 /* "$@" syntax adherence hack */
5610 if (
5611 !inquotes &&
5612 !memcmp(p, dolatstr, 4) &&
5613 (p[4] == CTLQUOTEMARK || (
5614 p[4] == CTLENDVAR &&
5615 p[5] == CTLQUOTEMARK
5616 ))
5617 ) {
5618 p = evalvar(p + 1, flag) + 1;
5619 goto start;
5620 }
5621 inquotes = !inquotes;
5622 addquote:
5623 if (quotes) {
5624 p--;
5625 length++;
5626 startloc++;
5627 }
5628 break;
5629 case CTLESC:
5630 startloc++;
5631 length++;
5632 goto addquote;
5633 case CTLVAR:
5634 p = evalvar(p, flag);
5635 goto start;
5636 case CTLBACKQ:
5637 c = 0;
5638 case CTLBACKQ|CTLQUOTE:
5639 expbackq(argbackq->n, c, quotes);
5640 argbackq = argbackq->next;
5641 goto start;
5642#if ENABLE_ASH_MATH_SUPPORT
5643 case CTLENDARI:
5644 p--;
5645 expari(quotes);
5646 goto start;
5647#endif
5648 }
5649 }
5650 breakloop:
5651 ;
5652}
5653
5654static char *
5655scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5656 int zero)
5657{
5658 char *loc;
5659 char *loc2;
5660 char c;
5661
5662 loc = startp;
5663 loc2 = rmesc;
5664 do {
5665 int match;
5666 const char *s = loc2;
5667 c = *loc2;
5668 if (zero) {
5669 *loc2 = '\0';
5670 s = rmesc;
5671 }
5672 match = pmatch(str, s);
5673 *loc2 = c;
5674 if (match)
5675 return loc;
5676 if (quotes && *loc == CTLESC)
5677 loc++;
5678 loc++;
5679 loc2++;
5680 } while (c);
5681 return 0;
5682}
5683
5684static char *
5685scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5686 int zero)
5687{
5688 int esc = 0;
5689 char *loc;
5690 char *loc2;
5691
5692 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5693 int match;
5694 char c = *loc2;
5695 const char *s = loc2;
5696 if (zero) {
5697 *loc2 = '\0';
5698 s = rmesc;
5699 }
5700 match = pmatch(str, s);
5701 *loc2 = c;
5702 if (match)
5703 return loc;
5704 loc--;
5705 if (quotes) {
5706 if (--esc < 0) {
5707 esc = esclen(startp, loc);
5708 }
5709 if (esc % 2) {
5710 esc--;
5711 loc--;
5712 }
5713 }
5714 }
5715 return 0;
5716}
5717
5718static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5719static void
5720varunset(const char *end, const char *var, const char *umsg, int varflags)
5721{
5722 const char *msg;
5723 const char *tail;
5724
5725 tail = nullstr;
5726 msg = "parameter not set";
5727 if (umsg) {
5728 if (*end == CTLENDVAR) {
5729 if (varflags & VSNUL)
5730 tail = " or null";
5731 } else
5732 msg = umsg;
5733 }
5734 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5735}
5736
5737static const char *
5738subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5739{
5740 char *startp;
5741 char *loc;
5742 int saveherefd = herefd;
5743 struct nodelist *saveargbackq = argbackq;
5744 int amount;
5745 char *rmesc, *rmescend;
5746 int zero;
5747 char *(*scan)(char *, char *, char *, char *, int , int);
5748
5749 herefd = -1;
5750 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5751 STPUTC('\0', expdest);
5752 herefd = saveherefd;
5753 argbackq = saveargbackq;
5754 startp = stackblock() + startloc;
5755
5756 switch (subtype) {
5757 case VSASSIGN:
5758 setvar(str, startp, 0);
5759 amount = startp - expdest;
5760 STADJUST(amount, expdest);
5761 return startp;
5762
5763 case VSQUESTION:
5764 varunset(p, str, startp, varflags);
5765 /* NOTREACHED */
5766 }
5767
5768 subtype -= VSTRIMRIGHT;
5769#if DEBUG
5770 if (subtype < 0 || subtype > 3)
5771 abort();
5772#endif
5773
5774 rmesc = startp;
5775 rmescend = stackblock() + strloc;
5776 if (quotes) {
5777 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5778 if (rmesc != startp) {
5779 rmescend = expdest;
5780 startp = stackblock() + startloc;
5781 }
5782 }
5783 rmescend--;
5784 str = stackblock() + strloc;
5785 preglob(str, varflags & VSQUOTE, 0);
5786
5787 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5788 zero = subtype >> 1;
5789 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5790 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5791
5792 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5793 if (loc) {
5794 if (zero) {
5795 memmove(startp, loc, str - loc);
5796 loc = startp + (str - loc) - 1;
5797 }
5798 *loc = '\0';
5799 amount = loc - expdest;
5800 STADJUST(amount, expdest);
5801 }
5802 return loc;
5803}
5804
5805/*
5806 * Add the value of a specialized variable to the stack string.
5807 */
5808static ssize_t
5809varvalue(char *name, int varflags, int flags)
5810{
5811 int num;
5812 char *p;
5813 int i;
5814 int sep = 0;
5815 int sepq = 0;
5816 ssize_t len = 0;
5817 char **ap;
5818 int syntax;
5819 int quoted = varflags & VSQUOTE;
5820 int subtype = varflags & VSTYPE;
5821 int quotes = flags & (EXP_FULL | EXP_CASE);
5822
5823 if (quoted && (flags & EXP_FULL))
5824 sep = 1 << CHAR_BIT;
5825
5826 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5827 switch (*name) {
5828 case '$':
5829 num = rootpid;
5830 goto numvar;
5831 case '?':
5832 num = exitstatus;
5833 goto numvar;
5834 case '#':
5835 num = shellparam.nparam;
5836 goto numvar;
5837 case '!':
5838 num = backgndpid;
5839 if (num == 0)
5840 return -1;
5841 numvar:
5842 len = cvtnum(num);
5843 break;
5844 case '-':
5845 p = makestrspace(NOPTS, expdest);
5846 for (i = NOPTS - 1; i >= 0; i--) {
5847 if (optlist[i]) {
5848 USTPUTC(optletters(i), p);
5849 len++;
5850 }
5851 }
5852 expdest = p;
5853 break;
5854 case '@':
5855 if (sep)
5856 goto param;
5857 /* fall through */
5858 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005859 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5861 sepq = 1;
5862 param:
5863 ap = shellparam.p;
5864 if (!ap)
5865 return -1;
5866 while ((p = *ap++)) {
5867 size_t partlen;
5868
5869 partlen = strlen(p);
5870 len += partlen;
5871
5872 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5873 memtodest(p, partlen, syntax, quotes);
5874
5875 if (*ap && sep) {
5876 char *q;
5877
5878 len++;
5879 if (subtype == VSPLUS || subtype == VSLENGTH) {
5880 continue;
5881 }
5882 q = expdest;
5883 if (sepq)
5884 STPUTC(CTLESC, q);
5885 STPUTC(sep, q);
5886 expdest = q;
5887 }
5888 }
5889 return len;
5890 case '0':
5891 case '1':
5892 case '2':
5893 case '3':
5894 case '4':
5895 case '5':
5896 case '6':
5897 case '7':
5898 case '8':
5899 case '9':
5900 num = atoi(name);
5901 if (num < 0 || num > shellparam.nparam)
5902 return -1;
5903 p = num ? shellparam.p[num - 1] : arg0;
5904 goto value;
5905 default:
5906 p = lookupvar(name);
5907 value:
5908 if (!p)
5909 return -1;
5910
5911 len = strlen(p);
5912 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5913 memtodest(p, len, syntax, quotes);
5914 return len;
5915 }
5916
5917 if (subtype == VSPLUS || subtype == VSLENGTH)
5918 STADJUST(-len, expdest);
5919 return len;
5920}
5921
5922/*
5923 * Expand a variable, and return a pointer to the next character in the
5924 * input string.
5925 */
5926static char *
5927evalvar(char *p, int flag)
5928{
5929 int subtype;
5930 int varflags;
5931 char *var;
5932 int patloc;
5933 int c;
5934 int startloc;
5935 ssize_t varlen;
5936 int easy;
5937 int quotes;
5938 int quoted;
5939
5940 quotes = flag & (EXP_FULL | EXP_CASE);
5941 varflags = *p++;
5942 subtype = varflags & VSTYPE;
5943 quoted = varflags & VSQUOTE;
5944 var = p;
5945 easy = (!quoted || (*var == '@' && shellparam.nparam));
5946 startloc = expdest - (char *)stackblock();
5947 p = strchr(p, '=') + 1;
5948
5949 again:
5950 varlen = varvalue(var, varflags, flag);
5951 if (varflags & VSNUL)
5952 varlen--;
5953
5954 if (subtype == VSPLUS) {
5955 varlen = -1 - varlen;
5956 goto vsplus;
5957 }
5958
5959 if (subtype == VSMINUS) {
5960 vsplus:
5961 if (varlen < 0) {
5962 argstr(
5963 p, flag | EXP_TILDE |
5964 (quoted ? EXP_QWORD : EXP_WORD)
5965 );
5966 goto end;
5967 }
5968 if (easy)
5969 goto record;
5970 goto end;
5971 }
5972
5973 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5974 if (varlen < 0) {
5975 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5976 varflags &= ~VSNUL;
5977 /*
5978 * Remove any recorded regions beyond
5979 * start of variable
5980 */
5981 removerecordregions(startloc);
5982 goto again;
5983 }
5984 goto end;
5985 }
5986 if (easy)
5987 goto record;
5988 goto end;
5989 }
5990
5991 if (varlen < 0 && uflag)
5992 varunset(p, var, 0, 0);
5993
5994 if (subtype == VSLENGTH) {
5995 cvtnum(varlen > 0 ? varlen : 0);
5996 goto record;
5997 }
5998
5999 if (subtype == VSNORMAL) {
6000 if (!easy)
6001 goto end;
6002 record:
6003 recordregion(startloc, expdest - (char *)stackblock(), quoted);
6004 goto end;
6005 }
6006
6007#if DEBUG
6008 switch (subtype) {
6009 case VSTRIMLEFT:
6010 case VSTRIMLEFTMAX:
6011 case VSTRIMRIGHT:
6012 case VSTRIMRIGHTMAX:
6013 break;
6014 default:
6015 abort();
6016 }
6017#endif
6018
6019 if (varlen >= 0) {
6020 /*
6021 * Terminate the string and start recording the pattern
6022 * right after it
6023 */
6024 STPUTC('\0', expdest);
6025 patloc = expdest - (char *)stackblock();
6026 if (subevalvar(p, NULL, patloc, subtype,
6027 startloc, varflags, quotes) == 0) {
6028 int amount = expdest - (
6029 (char *)stackblock() + patloc - 1
6030 );
6031 STADJUST(-amount, expdest);
6032 }
6033 /* Remove any recorded regions beyond start of variable */
6034 removerecordregions(startloc);
6035 goto record;
6036 }
6037
6038 end:
6039 if (subtype != VSNORMAL) { /* skip to end of alternative */
6040 int nesting = 1;
6041 for (;;) {
6042 c = *p++;
6043 if (c == CTLESC)
6044 p++;
6045 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6046 if (varlen >= 0)
6047 argbackq = argbackq->next;
6048 } else if (c == CTLVAR) {
6049 if ((*p++ & VSTYPE) != VSNORMAL)
6050 nesting++;
6051 } else if (c == CTLENDVAR) {
6052 if (--nesting == 0)
6053 break;
6054 }
6055 }
6056 }
6057 return p;
6058}
6059
6060/*
6061 * Break the argument string into pieces based upon IFS and add the
6062 * strings to the argument list. The regions of the string to be
6063 * searched for IFS characters have been stored by recordregion.
6064 */
6065static void
6066ifsbreakup(char *string, struct arglist *arglist)
6067{
6068 struct ifsregion *ifsp;
6069 struct strlist *sp;
6070 char *start;
6071 char *p;
6072 char *q;
6073 const char *ifs, *realifs;
6074 int ifsspc;
6075 int nulonly;
6076
6077 start = string;
6078 if (ifslastp != NULL) {
6079 ifsspc = 0;
6080 nulonly = 0;
6081 realifs = ifsset() ? ifsval() : defifs;
6082 ifsp = &ifsfirst;
6083 do {
6084 p = string + ifsp->begoff;
6085 nulonly = ifsp->nulonly;
6086 ifs = nulonly ? nullstr : realifs;
6087 ifsspc = 0;
6088 while (p < string + ifsp->endoff) {
6089 q = p;
6090 if (*p == CTLESC)
6091 p++;
6092 if (!strchr(ifs, *p)) {
6093 p++;
6094 continue;
6095 }
6096 if (!nulonly)
6097 ifsspc = (strchr(defifs, *p) != NULL);
6098 /* Ignore IFS whitespace at start */
6099 if (q == start && ifsspc) {
6100 p++;
6101 start = p;
6102 continue;
6103 }
6104 *q = '\0';
6105 sp = stalloc(sizeof(*sp));
6106 sp->text = start;
6107 *arglist->lastp = sp;
6108 arglist->lastp = &sp->next;
6109 p++;
6110 if (!nulonly) {
6111 for (;;) {
6112 if (p >= string + ifsp->endoff) {
6113 break;
6114 }
6115 q = p;
6116 if (*p == CTLESC)
6117 p++;
6118 if (strchr(ifs, *p) == NULL ) {
6119 p = q;
6120 break;
6121 } else if (strchr(defifs, *p) == NULL) {
6122 if (ifsspc) {
6123 p++;
6124 ifsspc = 0;
6125 } else {
6126 p = q;
6127 break;
6128 }
6129 } else
6130 p++;
6131 }
6132 }
6133 start = p;
6134 } /* while */
6135 ifsp = ifsp->next;
6136 } while (ifsp != NULL);
6137 if (nulonly)
6138 goto add;
6139 }
6140
6141 if (!*start)
6142 return;
6143
6144 add:
6145 sp = stalloc(sizeof(*sp));
6146 sp->text = start;
6147 *arglist->lastp = sp;
6148 arglist->lastp = &sp->next;
6149}
6150
6151static void
6152ifsfree(void)
6153{
6154 struct ifsregion *p;
6155
6156 INT_OFF;
6157 p = ifsfirst.next;
6158 do {
6159 struct ifsregion *ifsp;
6160 ifsp = p->next;
6161 free(p);
6162 p = ifsp;
6163 } while (p);
6164 ifslastp = NULL;
6165 ifsfirst.next = NULL;
6166 INT_ON;
6167}
6168
6169/*
6170 * Add a file name to the list.
6171 */
6172static void
6173addfname(const char *name)
6174{
6175 struct strlist *sp;
6176
6177 sp = stalloc(sizeof(*sp));
6178 sp->text = ststrdup(name);
6179 *exparg.lastp = sp;
6180 exparg.lastp = &sp->next;
6181}
6182
6183static char *expdir;
6184
6185/*
6186 * Do metacharacter (i.e. *, ?, [...]) expansion.
6187 */
6188static void
6189expmeta(char *enddir, char *name)
6190{
6191 char *p;
6192 const char *cp;
6193 char *start;
6194 char *endname;
6195 int metaflag;
6196 struct stat statb;
6197 DIR *dirp;
6198 struct dirent *dp;
6199 int atend;
6200 int matchdot;
6201
6202 metaflag = 0;
6203 start = name;
6204 for (p = name; *p; p++) {
6205 if (*p == '*' || *p == '?')
6206 metaflag = 1;
6207 else if (*p == '[') {
6208 char *q = p + 1;
6209 if (*q == '!')
6210 q++;
6211 for (;;) {
6212 if (*q == '\\')
6213 q++;
6214 if (*q == '/' || *q == '\0')
6215 break;
6216 if (*++q == ']') {
6217 metaflag = 1;
6218 break;
6219 }
6220 }
6221 } else if (*p == '\\')
6222 p++;
6223 else if (*p == '/') {
6224 if (metaflag)
6225 goto out;
6226 start = p + 1;
6227 }
6228 }
6229 out:
6230 if (metaflag == 0) { /* we've reached the end of the file name */
6231 if (enddir != expdir)
6232 metaflag++;
6233 p = name;
6234 do {
6235 if (*p == '\\')
6236 p++;
6237 *enddir++ = *p;
6238 } while (*p++);
6239 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6240 addfname(expdir);
6241 return;
6242 }
6243 endname = p;
6244 if (name < start) {
6245 p = name;
6246 do {
6247 if (*p == '\\')
6248 p++;
6249 *enddir++ = *p++;
6250 } while (p < start);
6251 }
6252 if (enddir == expdir) {
6253 cp = ".";
6254 } else if (enddir == expdir + 1 && *expdir == '/') {
6255 cp = "/";
6256 } else {
6257 cp = expdir;
6258 enddir[-1] = '\0';
6259 }
6260 dirp = opendir(cp);
6261 if (dirp == NULL)
6262 return;
6263 if (enddir != expdir)
6264 enddir[-1] = '/';
6265 if (*endname == 0) {
6266 atend = 1;
6267 } else {
6268 atend = 0;
6269 *endname++ = '\0';
6270 }
6271 matchdot = 0;
6272 p = start;
6273 if (*p == '\\')
6274 p++;
6275 if (*p == '.')
6276 matchdot++;
6277 while (! intpending && (dp = readdir(dirp)) != NULL) {
6278 if (dp->d_name[0] == '.' && ! matchdot)
6279 continue;
6280 if (pmatch(start, dp->d_name)) {
6281 if (atend) {
6282 strcpy(enddir, dp->d_name);
6283 addfname(expdir);
6284 } else {
6285 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6286 continue;
6287 p[-1] = '/';
6288 expmeta(p, endname);
6289 }
6290 }
6291 }
6292 closedir(dirp);
6293 if (! atend)
6294 endname[-1] = '/';
6295}
6296
6297static struct strlist *
6298msort(struct strlist *list, int len)
6299{
6300 struct strlist *p, *q = NULL;
6301 struct strlist **lpp;
6302 int half;
6303 int n;
6304
6305 if (len <= 1)
6306 return list;
6307 half = len >> 1;
6308 p = list;
6309 for (n = half; --n >= 0; ) {
6310 q = p;
6311 p = p->next;
6312 }
6313 q->next = NULL; /* terminate first half of list */
6314 q = msort(list, half); /* sort first half of list */
6315 p = msort(p, len - half); /* sort second half */
6316 lpp = &list;
6317 for (;;) {
6318#if ENABLE_LOCALE_SUPPORT
6319 if (strcoll(p->text, q->text) < 0)
6320#else
6321 if (strcmp(p->text, q->text) < 0)
6322#endif
6323 {
6324 *lpp = p;
6325 lpp = &p->next;
6326 p = *lpp;
6327 if (p == NULL) {
6328 *lpp = q;
6329 break;
6330 }
6331 } else {
6332 *lpp = q;
6333 lpp = &q->next;
6334 q = *lpp;
6335 if (q == NULL) {
6336 *lpp = p;
6337 break;
6338 }
6339 }
6340 }
6341 return list;
6342}
6343
6344/*
6345 * Sort the results of file name expansion. It calculates the number of
6346 * strings to sort and then calls msort (short for merge sort) to do the
6347 * work.
6348 */
6349static struct strlist *
6350expsort(struct strlist *str)
6351{
6352 int len;
6353 struct strlist *sp;
6354
6355 len = 0;
6356 for (sp = str; sp; sp = sp->next)
6357 len++;
6358 return msort(str, len);
6359}
6360
6361static void
6362expandmeta(struct strlist *str, int flag)
6363{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006364 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006365 '*', '?', '[', 0
6366 };
6367 /* TODO - EXP_REDIR */
6368
6369 while (str) {
6370 struct strlist **savelastp;
6371 struct strlist *sp;
6372 char *p;
6373
6374 if (fflag)
6375 goto nometa;
6376 if (!strpbrk(str->text, metachars))
6377 goto nometa;
6378 savelastp = exparg.lastp;
6379
6380 INT_OFF;
6381 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6382 {
6383 int i = strlen(str->text);
6384 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6385 }
6386
6387 expmeta(expdir, p);
6388 free(expdir);
6389 if (p != str->text)
6390 free(p);
6391 INT_ON;
6392 if (exparg.lastp == savelastp) {
6393 /*
6394 * no matches
6395 */
6396 nometa:
6397 *exparg.lastp = str;
6398 rmescapes(str->text);
6399 exparg.lastp = &str->next;
6400 } else {
6401 *exparg.lastp = NULL;
6402 *savelastp = sp = expsort(*savelastp);
6403 while (sp->next != NULL)
6404 sp = sp->next;
6405 exparg.lastp = &sp->next;
6406 }
6407 str = str->next;
6408 }
6409}
6410
6411/*
6412 * Perform variable substitution and command substitution on an argument,
6413 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6414 * perform splitting and file name expansion. When arglist is NULL, perform
6415 * here document expansion.
6416 */
6417static void
6418expandarg(union node *arg, struct arglist *arglist, int flag)
6419{
6420 struct strlist *sp;
6421 char *p;
6422
6423 argbackq = arg->narg.backquote;
6424 STARTSTACKSTR(expdest);
6425 ifsfirst.next = NULL;
6426 ifslastp = NULL;
6427 argstr(arg->narg.text, flag);
6428 p = _STPUTC('\0', expdest);
6429 expdest = p - 1;
6430 if (arglist == NULL) {
6431 return; /* here document expanded */
6432 }
6433 p = grabstackstr(p);
6434 exparg.lastp = &exparg.list;
6435 /*
6436 * TODO - EXP_REDIR
6437 */
6438 if (flag & EXP_FULL) {
6439 ifsbreakup(p, &exparg);
6440 *exparg.lastp = NULL;
6441 exparg.lastp = &exparg.list;
6442 expandmeta(exparg.list, flag);
6443 } else {
6444 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6445 rmescapes(p);
6446 sp = stalloc(sizeof(*sp));
6447 sp->text = p;
6448 *exparg.lastp = sp;
6449 exparg.lastp = &sp->next;
6450 }
6451 if (ifsfirst.next)
6452 ifsfree();
6453 *exparg.lastp = NULL;
6454 if (exparg.list) {
6455 *arglist->lastp = exparg.list;
6456 arglist->lastp = exparg.lastp;
6457 }
6458}
6459
6460/*
6461 * Expand shell variables and backquotes inside a here document.
6462 */
6463static void
6464expandhere(union node *arg, int fd)
6465{
6466 herefd = fd;
6467 expandarg(arg, (struct arglist *)NULL, 0);
6468 full_write(fd, stackblock(), expdest - (char *)stackblock());
6469}
6470
6471/*
6472 * Returns true if the pattern matches the string.
6473 */
6474static int
6475patmatch(char *pattern, const char *string)
6476{
6477 return pmatch(preglob(pattern, 0, 0), string);
6478}
6479
6480/*
6481 * See if a pattern matches in a case statement.
6482 */
6483static int
6484casematch(union node *pattern, char *val)
6485{
6486 struct stackmark smark;
6487 int result;
6488
6489 setstackmark(&smark);
6490 argbackq = pattern->narg.backquote;
6491 STARTSTACKSTR(expdest);
6492 ifslastp = NULL;
6493 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6494 STACKSTRNUL(expdest);
6495 result = patmatch(stackblock(), val);
6496 popstackmark(&smark);
6497 return result;
6498}
6499
6500
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006501/* ============ find_command */
6502
6503struct builtincmd {
6504 const char *name;
6505 int (*builtin)(int, char **);
6506 /* unsigned flags; */
6507};
6508#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6509#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6510#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6511
6512struct cmdentry {
6513 int cmdtype;
6514 union param {
6515 int index;
6516 const struct builtincmd *cmd;
6517 struct funcnode *func;
6518 } u;
6519};
6520/* values of cmdtype */
6521#define CMDUNKNOWN -1 /* no entry in table for command */
6522#define CMDNORMAL 0 /* command is an executable program */
6523#define CMDFUNCTION 1 /* command is a shell function */
6524#define CMDBUILTIN 2 /* command is a shell builtin */
6525
6526/* action to find_command() */
6527#define DO_ERR 0x01 /* prints errors */
6528#define DO_ABS 0x02 /* checks absolute paths */
6529#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6530#define DO_ALTPATH 0x08 /* using alternate path */
6531#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6532
6533static void find_command(char *, struct cmdentry *, int, const char *);
6534
6535
6536/* ============ Hashing commands */
6537
6538/*
6539 * When commands are first encountered, they are entered in a hash table.
6540 * This ensures that a full path search will not have to be done for them
6541 * on each invocation.
6542 *
6543 * We should investigate converting to a linear search, even though that
6544 * would make the command name "hash" a misnomer.
6545 */
6546
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006547#define ARB 1 /* actual size determined at run time */
6548
6549struct tblentry {
6550 struct tblentry *next; /* next entry in hash chain */
6551 union param param; /* definition of builtin function */
6552 short cmdtype; /* index identifying command */
6553 char rehash; /* if set, cd done since entry created */
6554 char cmdname[ARB]; /* name of command */
6555};
6556
Denis Vlasenko01631112007-12-16 17:20:38 +00006557static struct tblentry **cmdtable;
6558#define INIT_G_cmdtable() do { \
6559 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6560} while (0)
6561
6562static int builtinloc = -1; /* index in path of %builtin, or -1 */
6563
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006564
6565static void
6566tryexec(char *cmd, char **argv, char **envp)
6567{
6568 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006569
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006570#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006571 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006572 int a = find_applet_by_name(cmd);
6573 if (a >= 0) {
6574 if (APPLET_IS_NOEXEC(a))
6575 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006576 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006577 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006578 /* If they called chroot or otherwise made the binary no longer
6579 * executable, fall through */
6580 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006581 }
6582#endif
6583
6584 repeat:
6585#ifdef SYSV
6586 do {
6587 execve(cmd, argv, envp);
6588 } while (errno == EINTR);
6589#else
6590 execve(cmd, argv, envp);
6591#endif
6592 if (repeated++) {
6593 free(argv);
6594 } else if (errno == ENOEXEC) {
6595 char **ap;
6596 char **new;
6597
6598 for (ap = argv; *ap; ap++)
6599 ;
6600 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6601 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006602 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006603 ap += 2;
6604 argv++;
6605 while ((*ap++ = *argv++))
6606 ;
6607 argv = new;
6608 goto repeat;
6609 }
6610}
6611
6612/*
6613 * Exec a program. Never returns. If you change this routine, you may
6614 * have to change the find_command routine as well.
6615 */
6616#define environment() listvars(VEXPORT, VUNSET, 0)
6617static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6618static void
6619shellexec(char **argv, const char *path, int idx)
6620{
6621 char *cmdname;
6622 int e;
6623 char **envp;
6624 int exerrno;
6625
6626 clearredir(1);
6627 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006628 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006629#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006630 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006631#endif
6632 ) {
6633 tryexec(argv[0], argv, envp);
6634 e = errno;
6635 } else {
6636 e = ENOENT;
6637 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6638 if (--idx < 0 && pathopt == NULL) {
6639 tryexec(cmdname, argv, envp);
6640 if (errno != ENOENT && errno != ENOTDIR)
6641 e = errno;
6642 }
6643 stunalloc(cmdname);
6644 }
6645 }
6646
6647 /* Map to POSIX errors */
6648 switch (e) {
6649 case EACCES:
6650 exerrno = 126;
6651 break;
6652 case ENOENT:
6653 exerrno = 127;
6654 break;
6655 default:
6656 exerrno = 2;
6657 break;
6658 }
6659 exitstatus = exerrno;
6660 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6661 argv[0], e, suppressint ));
6662 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6663 /* NOTREACHED */
6664}
6665
6666static void
6667printentry(struct tblentry *cmdp)
6668{
6669 int idx;
6670 const char *path;
6671 char *name;
6672
6673 idx = cmdp->param.index;
6674 path = pathval();
6675 do {
6676 name = padvance(&path, cmdp->cmdname);
6677 stunalloc(name);
6678 } while (--idx >= 0);
6679 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6680}
6681
6682/*
6683 * Clear out command entries. The argument specifies the first entry in
6684 * PATH which has changed.
6685 */
6686static void
6687clearcmdentry(int firstchange)
6688{
6689 struct tblentry **tblp;
6690 struct tblentry **pp;
6691 struct tblentry *cmdp;
6692
6693 INT_OFF;
6694 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6695 pp = tblp;
6696 while ((cmdp = *pp) != NULL) {
6697 if ((cmdp->cmdtype == CMDNORMAL &&
6698 cmdp->param.index >= firstchange)
6699 || (cmdp->cmdtype == CMDBUILTIN &&
6700 builtinloc >= firstchange)
6701 ) {
6702 *pp = cmdp->next;
6703 free(cmdp);
6704 } else {
6705 pp = &cmdp->next;
6706 }
6707 }
6708 }
6709 INT_ON;
6710}
6711
6712/*
6713 * Locate a command in the command hash table. If "add" is nonzero,
6714 * add the command to the table if it is not already present. The
6715 * variable "lastcmdentry" is set to point to the address of the link
6716 * pointing to the entry, so that delete_cmd_entry can delete the
6717 * entry.
6718 *
6719 * Interrupts must be off if called with add != 0.
6720 */
6721static struct tblentry **lastcmdentry;
6722
6723static struct tblentry *
6724cmdlookup(const char *name, int add)
6725{
6726 unsigned int hashval;
6727 const char *p;
6728 struct tblentry *cmdp;
6729 struct tblentry **pp;
6730
6731 p = name;
6732 hashval = (unsigned char)*p << 4;
6733 while (*p)
6734 hashval += (unsigned char)*p++;
6735 hashval &= 0x7FFF;
6736 pp = &cmdtable[hashval % CMDTABLESIZE];
6737 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6738 if (strcmp(cmdp->cmdname, name) == 0)
6739 break;
6740 pp = &cmdp->next;
6741 }
6742 if (add && cmdp == NULL) {
6743 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6744 + strlen(name) + 1);
6745 cmdp->next = NULL;
6746 cmdp->cmdtype = CMDUNKNOWN;
6747 strcpy(cmdp->cmdname, name);
6748 }
6749 lastcmdentry = pp;
6750 return cmdp;
6751}
6752
6753/*
6754 * Delete the command entry returned on the last lookup.
6755 */
6756static void
6757delete_cmd_entry(void)
6758{
6759 struct tblentry *cmdp;
6760
6761 INT_OFF;
6762 cmdp = *lastcmdentry;
6763 *lastcmdentry = cmdp->next;
6764 if (cmdp->cmdtype == CMDFUNCTION)
6765 freefunc(cmdp->param.func);
6766 free(cmdp);
6767 INT_ON;
6768}
6769
6770/*
6771 * Add a new command entry, replacing any existing command entry for
6772 * the same name - except special builtins.
6773 */
6774static void
6775addcmdentry(char *name, struct cmdentry *entry)
6776{
6777 struct tblentry *cmdp;
6778
6779 cmdp = cmdlookup(name, 1);
6780 if (cmdp->cmdtype == CMDFUNCTION) {
6781 freefunc(cmdp->param.func);
6782 }
6783 cmdp->cmdtype = entry->cmdtype;
6784 cmdp->param = entry->u;
6785 cmdp->rehash = 0;
6786}
6787
6788static int
6789hashcmd(int argc, char **argv)
6790{
6791 struct tblentry **pp;
6792 struct tblentry *cmdp;
6793 int c;
6794 struct cmdentry entry;
6795 char *name;
6796
6797 while ((c = nextopt("r")) != '\0') {
6798 clearcmdentry(0);
6799 return 0;
6800 }
6801 if (*argptr == NULL) {
6802 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6803 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6804 if (cmdp->cmdtype == CMDNORMAL)
6805 printentry(cmdp);
6806 }
6807 }
6808 return 0;
6809 }
6810 c = 0;
6811 while ((name = *argptr) != NULL) {
6812 cmdp = cmdlookup(name, 0);
6813 if (cmdp != NULL
6814 && (cmdp->cmdtype == CMDNORMAL
6815 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6816 delete_cmd_entry();
6817 find_command(name, &entry, DO_ERR, pathval());
6818 if (entry.cmdtype == CMDUNKNOWN)
6819 c = 1;
6820 argptr++;
6821 }
6822 return c;
6823}
6824
6825/*
6826 * Called when a cd is done. Marks all commands so the next time they
6827 * are executed they will be rehashed.
6828 */
6829static void
6830hashcd(void)
6831{
6832 struct tblentry **pp;
6833 struct tblentry *cmdp;
6834
6835 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6836 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6837 if (cmdp->cmdtype == CMDNORMAL || (
6838 cmdp->cmdtype == CMDBUILTIN &&
6839 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6840 builtinloc > 0
6841 ))
6842 cmdp->rehash = 1;
6843 }
6844 }
6845}
6846
6847/*
6848 * Fix command hash table when PATH changed.
6849 * Called before PATH is changed. The argument is the new value of PATH;
6850 * pathval() still returns the old value at this point.
6851 * Called with interrupts off.
6852 */
6853static void
6854changepath(const char *newval)
6855{
6856 const char *old, *new;
6857 int idx;
6858 int firstchange;
6859 int idx_bltin;
6860
6861 old = pathval();
6862 new = newval;
6863 firstchange = 9999; /* assume no change */
6864 idx = 0;
6865 idx_bltin = -1;
6866 for (;;) {
6867 if (*old != *new) {
6868 firstchange = idx;
6869 if ((*old == '\0' && *new == ':')
6870 || (*old == ':' && *new == '\0'))
6871 firstchange++;
6872 old = new; /* ignore subsequent differences */
6873 }
6874 if (*new == '\0')
6875 break;
6876 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6877 idx_bltin = idx;
6878 if (*new == ':') {
6879 idx++;
6880 }
6881 new++, old++;
6882 }
6883 if (builtinloc < 0 && idx_bltin >= 0)
6884 builtinloc = idx_bltin; /* zap builtins */
6885 if (builtinloc >= 0 && idx_bltin < 0)
6886 firstchange = 0;
6887 clearcmdentry(firstchange);
6888 builtinloc = idx_bltin;
6889}
6890
6891#define TEOF 0
6892#define TNL 1
6893#define TREDIR 2
6894#define TWORD 3
6895#define TSEMI 4
6896#define TBACKGND 5
6897#define TAND 6
6898#define TOR 7
6899#define TPIPE 8
6900#define TLP 9
6901#define TRP 10
6902#define TENDCASE 11
6903#define TENDBQUOTE 12
6904#define TNOT 13
6905#define TCASE 14
6906#define TDO 15
6907#define TDONE 16
6908#define TELIF 17
6909#define TELSE 18
6910#define TESAC 19
6911#define TFI 20
6912#define TFOR 21
6913#define TIF 22
6914#define TIN 23
6915#define TTHEN 24
6916#define TUNTIL 25
6917#define TWHILE 26
6918#define TBEGIN 27
6919#define TEND 28
6920
6921/* first char is indicating which tokens mark the end of a list */
6922static const char *const tokname_array[] = {
6923 "\1end of file",
6924 "\0newline",
6925 "\0redirection",
6926 "\0word",
6927 "\0;",
6928 "\0&",
6929 "\0&&",
6930 "\0||",
6931 "\0|",
6932 "\0(",
6933 "\1)",
6934 "\1;;",
6935 "\1`",
6936#define KWDOFFSET 13
6937 /* the following are keywords */
6938 "\0!",
6939 "\0case",
6940 "\1do",
6941 "\1done",
6942 "\1elif",
6943 "\1else",
6944 "\1esac",
6945 "\1fi",
6946 "\0for",
6947 "\0if",
6948 "\0in",
6949 "\1then",
6950 "\0until",
6951 "\0while",
6952 "\0{",
6953 "\1}",
6954};
6955
6956static const char *
6957tokname(int tok)
6958{
6959 static char buf[16];
6960
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006961//try this:
6962//if (tok < TSEMI) return tokname_array[tok] + 1;
6963//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6964//return buf;
6965
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006966 if (tok >= TSEMI)
6967 buf[0] = '"';
6968 sprintf(buf + (tok >= TSEMI), "%s%c",
6969 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6970 return buf;
6971}
6972
6973/* Wrapper around strcmp for qsort/bsearch/... */
6974static int
6975pstrcmp(const void *a, const void *b)
6976{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006977 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006978}
6979
6980static const char *const *
6981findkwd(const char *s)
6982{
6983 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006984 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6985 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006986}
6987
6988/*
6989 * Locate and print what a word is...
6990 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006991static int
6992describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006993{
6994 struct cmdentry entry;
6995 struct tblentry *cmdp;
6996#if ENABLE_ASH_ALIAS
6997 const struct alias *ap;
6998#endif
6999 const char *path = pathval();
7000
7001 if (describe_command_verbose) {
7002 out1str(command);
7003 }
7004
7005 /* First look at the keywords */
7006 if (findkwd(command)) {
7007 out1str(describe_command_verbose ? " is a shell keyword" : command);
7008 goto out;
7009 }
7010
7011#if ENABLE_ASH_ALIAS
7012 /* Then look at the aliases */
7013 ap = lookupalias(command, 0);
7014 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007015 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007016 out1str("alias ");
7017 printalias(ap);
7018 return 0;
7019 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007020 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007021 goto out;
7022 }
7023#endif
7024 /* Then check if it is a tracked alias */
7025 cmdp = cmdlookup(command, 0);
7026 if (cmdp != NULL) {
7027 entry.cmdtype = cmdp->cmdtype;
7028 entry.u = cmdp->param;
7029 } else {
7030 /* Finally use brute force */
7031 find_command(command, &entry, DO_ABS, path);
7032 }
7033
7034 switch (entry.cmdtype) {
7035 case CMDNORMAL: {
7036 int j = entry.u.index;
7037 char *p;
7038 if (j == -1) {
7039 p = command;
7040 } else {
7041 do {
7042 p = padvance(&path, command);
7043 stunalloc(p);
7044 } while (--j >= 0);
7045 }
7046 if (describe_command_verbose) {
7047 out1fmt(" is%s %s",
7048 (cmdp ? " a tracked alias for" : nullstr), p
7049 );
7050 } else {
7051 out1str(p);
7052 }
7053 break;
7054 }
7055
7056 case CMDFUNCTION:
7057 if (describe_command_verbose) {
7058 out1str(" is a shell function");
7059 } else {
7060 out1str(command);
7061 }
7062 break;
7063
7064 case CMDBUILTIN:
7065 if (describe_command_verbose) {
7066 out1fmt(" is a %sshell builtin",
7067 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7068 "special " : nullstr
7069 );
7070 } else {
7071 out1str(command);
7072 }
7073 break;
7074
7075 default:
7076 if (describe_command_verbose) {
7077 out1str(": not found\n");
7078 }
7079 return 127;
7080 }
7081 out:
7082 outstr("\n", stdout);
7083 return 0;
7084}
7085
7086static int
7087typecmd(int argc, char **argv)
7088{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007089 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007090 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007091 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007092
Denis Vlasenko46846e22007-05-20 13:08:31 +00007093 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007094 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007095 i++;
7096 verbose = 0;
7097 }
7098 while (i < argc) {
7099 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007100 }
7101 return err;
7102}
7103
7104#if ENABLE_ASH_CMDCMD
7105static int
7106commandcmd(int argc, char **argv)
7107{
7108 int c;
7109 enum {
7110 VERIFY_BRIEF = 1,
7111 VERIFY_VERBOSE = 2,
7112 } verify = 0;
7113
7114 while ((c = nextopt("pvV")) != '\0')
7115 if (c == 'V')
7116 verify |= VERIFY_VERBOSE;
7117 else if (c == 'v')
7118 verify |= VERIFY_BRIEF;
7119#if DEBUG
7120 else if (c != 'p')
7121 abort();
7122#endif
7123 if (verify)
7124 return describe_command(*argptr, verify - VERIFY_BRIEF);
7125
7126 return 0;
7127}
7128#endif
7129
7130
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007131/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007132
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007133static int funcblocksize; /* size of structures in function */
7134static int funcstringsize; /* size of strings in node */
7135static void *funcblock; /* block to allocate function from */
7136static char *funcstring; /* block to allocate strings from */
7137
Eric Andersencb57d552001-06-28 07:25:16 +00007138/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007139#define EV_EXIT 01 /* exit after evaluating tree */
7140#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7141#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007142
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007143static const short nodesize[26] = {
7144 SHELL_ALIGN(sizeof(struct ncmd)),
7145 SHELL_ALIGN(sizeof(struct npipe)),
7146 SHELL_ALIGN(sizeof(struct nredir)),
7147 SHELL_ALIGN(sizeof(struct nredir)),
7148 SHELL_ALIGN(sizeof(struct nredir)),
7149 SHELL_ALIGN(sizeof(struct nbinary)),
7150 SHELL_ALIGN(sizeof(struct nbinary)),
7151 SHELL_ALIGN(sizeof(struct nbinary)),
7152 SHELL_ALIGN(sizeof(struct nif)),
7153 SHELL_ALIGN(sizeof(struct nbinary)),
7154 SHELL_ALIGN(sizeof(struct nbinary)),
7155 SHELL_ALIGN(sizeof(struct nfor)),
7156 SHELL_ALIGN(sizeof(struct ncase)),
7157 SHELL_ALIGN(sizeof(struct nclist)),
7158 SHELL_ALIGN(sizeof(struct narg)),
7159 SHELL_ALIGN(sizeof(struct narg)),
7160 SHELL_ALIGN(sizeof(struct nfile)),
7161 SHELL_ALIGN(sizeof(struct nfile)),
7162 SHELL_ALIGN(sizeof(struct nfile)),
7163 SHELL_ALIGN(sizeof(struct nfile)),
7164 SHELL_ALIGN(sizeof(struct nfile)),
7165 SHELL_ALIGN(sizeof(struct ndup)),
7166 SHELL_ALIGN(sizeof(struct ndup)),
7167 SHELL_ALIGN(sizeof(struct nhere)),
7168 SHELL_ALIGN(sizeof(struct nhere)),
7169 SHELL_ALIGN(sizeof(struct nnot)),
7170};
7171
7172static void calcsize(union node *n);
7173
7174static void
7175sizenodelist(struct nodelist *lp)
7176{
7177 while (lp) {
7178 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7179 calcsize(lp->n);
7180 lp = lp->next;
7181 }
7182}
7183
7184static void
7185calcsize(union node *n)
7186{
7187 if (n == NULL)
7188 return;
7189 funcblocksize += nodesize[n->type];
7190 switch (n->type) {
7191 case NCMD:
7192 calcsize(n->ncmd.redirect);
7193 calcsize(n->ncmd.args);
7194 calcsize(n->ncmd.assign);
7195 break;
7196 case NPIPE:
7197 sizenodelist(n->npipe.cmdlist);
7198 break;
7199 case NREDIR:
7200 case NBACKGND:
7201 case NSUBSHELL:
7202 calcsize(n->nredir.redirect);
7203 calcsize(n->nredir.n);
7204 break;
7205 case NAND:
7206 case NOR:
7207 case NSEMI:
7208 case NWHILE:
7209 case NUNTIL:
7210 calcsize(n->nbinary.ch2);
7211 calcsize(n->nbinary.ch1);
7212 break;
7213 case NIF:
7214 calcsize(n->nif.elsepart);
7215 calcsize(n->nif.ifpart);
7216 calcsize(n->nif.test);
7217 break;
7218 case NFOR:
7219 funcstringsize += strlen(n->nfor.var) + 1;
7220 calcsize(n->nfor.body);
7221 calcsize(n->nfor.args);
7222 break;
7223 case NCASE:
7224 calcsize(n->ncase.cases);
7225 calcsize(n->ncase.expr);
7226 break;
7227 case NCLIST:
7228 calcsize(n->nclist.body);
7229 calcsize(n->nclist.pattern);
7230 calcsize(n->nclist.next);
7231 break;
7232 case NDEFUN:
7233 case NARG:
7234 sizenodelist(n->narg.backquote);
7235 funcstringsize += strlen(n->narg.text) + 1;
7236 calcsize(n->narg.next);
7237 break;
7238 case NTO:
7239 case NCLOBBER:
7240 case NFROM:
7241 case NFROMTO:
7242 case NAPPEND:
7243 calcsize(n->nfile.fname);
7244 calcsize(n->nfile.next);
7245 break;
7246 case NTOFD:
7247 case NFROMFD:
7248 calcsize(n->ndup.vname);
7249 calcsize(n->ndup.next);
7250 break;
7251 case NHERE:
7252 case NXHERE:
7253 calcsize(n->nhere.doc);
7254 calcsize(n->nhere.next);
7255 break;
7256 case NNOT:
7257 calcsize(n->nnot.com);
7258 break;
7259 };
7260}
7261
7262static char *
7263nodeckstrdup(char *s)
7264{
7265 char *rtn = funcstring;
7266
7267 strcpy(funcstring, s);
7268 funcstring += strlen(s) + 1;
7269 return rtn;
7270}
7271
7272static union node *copynode(union node *);
7273
7274static struct nodelist *
7275copynodelist(struct nodelist *lp)
7276{
7277 struct nodelist *start;
7278 struct nodelist **lpp;
7279
7280 lpp = &start;
7281 while (lp) {
7282 *lpp = funcblock;
7283 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7284 (*lpp)->n = copynode(lp->n);
7285 lp = lp->next;
7286 lpp = &(*lpp)->next;
7287 }
7288 *lpp = NULL;
7289 return start;
7290}
7291
7292static union node *
7293copynode(union node *n)
7294{
7295 union node *new;
7296
7297 if (n == NULL)
7298 return NULL;
7299 new = funcblock;
7300 funcblock = (char *) funcblock + nodesize[n->type];
7301
7302 switch (n->type) {
7303 case NCMD:
7304 new->ncmd.redirect = copynode(n->ncmd.redirect);
7305 new->ncmd.args = copynode(n->ncmd.args);
7306 new->ncmd.assign = copynode(n->ncmd.assign);
7307 break;
7308 case NPIPE:
7309 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7310 new->npipe.backgnd = n->npipe.backgnd;
7311 break;
7312 case NREDIR:
7313 case NBACKGND:
7314 case NSUBSHELL:
7315 new->nredir.redirect = copynode(n->nredir.redirect);
7316 new->nredir.n = copynode(n->nredir.n);
7317 break;
7318 case NAND:
7319 case NOR:
7320 case NSEMI:
7321 case NWHILE:
7322 case NUNTIL:
7323 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7324 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7325 break;
7326 case NIF:
7327 new->nif.elsepart = copynode(n->nif.elsepart);
7328 new->nif.ifpart = copynode(n->nif.ifpart);
7329 new->nif.test = copynode(n->nif.test);
7330 break;
7331 case NFOR:
7332 new->nfor.var = nodeckstrdup(n->nfor.var);
7333 new->nfor.body = copynode(n->nfor.body);
7334 new->nfor.args = copynode(n->nfor.args);
7335 break;
7336 case NCASE:
7337 new->ncase.cases = copynode(n->ncase.cases);
7338 new->ncase.expr = copynode(n->ncase.expr);
7339 break;
7340 case NCLIST:
7341 new->nclist.body = copynode(n->nclist.body);
7342 new->nclist.pattern = copynode(n->nclist.pattern);
7343 new->nclist.next = copynode(n->nclist.next);
7344 break;
7345 case NDEFUN:
7346 case NARG:
7347 new->narg.backquote = copynodelist(n->narg.backquote);
7348 new->narg.text = nodeckstrdup(n->narg.text);
7349 new->narg.next = copynode(n->narg.next);
7350 break;
7351 case NTO:
7352 case NCLOBBER:
7353 case NFROM:
7354 case NFROMTO:
7355 case NAPPEND:
7356 new->nfile.fname = copynode(n->nfile.fname);
7357 new->nfile.fd = n->nfile.fd;
7358 new->nfile.next = copynode(n->nfile.next);
7359 break;
7360 case NTOFD:
7361 case NFROMFD:
7362 new->ndup.vname = copynode(n->ndup.vname);
7363 new->ndup.dupfd = n->ndup.dupfd;
7364 new->ndup.fd = n->ndup.fd;
7365 new->ndup.next = copynode(n->ndup.next);
7366 break;
7367 case NHERE:
7368 case NXHERE:
7369 new->nhere.doc = copynode(n->nhere.doc);
7370 new->nhere.fd = n->nhere.fd;
7371 new->nhere.next = copynode(n->nhere.next);
7372 break;
7373 case NNOT:
7374 new->nnot.com = copynode(n->nnot.com);
7375 break;
7376 };
7377 new->type = n->type;
7378 return new;
7379}
7380
7381/*
7382 * Make a copy of a parse tree.
7383 */
7384static struct funcnode *
7385copyfunc(union node *n)
7386{
7387 struct funcnode *f;
7388 size_t blocksize;
7389
7390 funcblocksize = offsetof(struct funcnode, n);
7391 funcstringsize = 0;
7392 calcsize(n);
7393 blocksize = funcblocksize;
7394 f = ckmalloc(blocksize + funcstringsize);
7395 funcblock = (char *) f + offsetof(struct funcnode, n);
7396 funcstring = (char *) f + blocksize;
7397 copynode(n);
7398 f->count = 0;
7399 return f;
7400}
7401
7402/*
7403 * Define a shell function.
7404 */
7405static void
7406defun(char *name, union node *func)
7407{
7408 struct cmdentry entry;
7409
7410 INT_OFF;
7411 entry.cmdtype = CMDFUNCTION;
7412 entry.u.func = copyfunc(func);
7413 addcmdentry(name, &entry);
7414 INT_ON;
7415}
7416
7417static int evalskip; /* set if we are skipping commands */
7418/* reasons for skipping commands (see comment on breakcmd routine) */
7419#define SKIPBREAK (1 << 0)
7420#define SKIPCONT (1 << 1)
7421#define SKIPFUNC (1 << 2)
7422#define SKIPFILE (1 << 3)
7423#define SKIPEVAL (1 << 4)
7424static int skipcount; /* number of levels to skip */
7425static int funcnest; /* depth of function calls */
7426
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007427/* forward decl way out to parsing code - dotrap needs it */
7428static int evalstring(char *s, int mask);
7429
7430/*
7431 * Called to execute a trap. Perhaps we should avoid entering new trap
7432 * handlers while we are executing a trap handler.
7433 */
7434static int
7435dotrap(void)
7436{
7437 char *p;
7438 char *q;
7439 int i;
7440 int savestatus;
7441 int skip = 0;
7442
7443 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007444 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007445 xbarrier();
7446
7447 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7448 if (!*q)
7449 continue;
7450 *q = '\0';
7451
7452 p = trap[i + 1];
7453 if (!p)
7454 continue;
7455 skip = evalstring(p, SKIPEVAL);
7456 exitstatus = savestatus;
7457 if (skip)
7458 break;
7459 }
7460
7461 return skip;
7462}
7463
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007464/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007465static void evalloop(union node *, int);
7466static void evalfor(union node *, int);
7467static void evalcase(union node *, int);
7468static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007469static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007470static void evalpipe(union node *, int);
7471static void evalcommand(union node *, int);
7472static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007473static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007474
Eric Andersen62483552001-07-10 06:09:16 +00007475/*
Eric Andersenc470f442003-07-28 09:56:35 +00007476 * Evaluate a parse tree. The value is left in the global variable
7477 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007478 */
Eric Andersenc470f442003-07-28 09:56:35 +00007479static void
7480evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007481{
Eric Andersenc470f442003-07-28 09:56:35 +00007482 int checkexit = 0;
7483 void (*evalfn)(union node *, int);
7484 unsigned isor;
7485 int status;
7486 if (n == NULL) {
7487 TRACE(("evaltree(NULL) called\n"));
7488 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007489 }
Eric Andersenc470f442003-07-28 09:56:35 +00007490 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007491 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007492 switch (n->type) {
7493 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007494#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007495 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007496 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007497 break;
7498#endif
7499 case NNOT:
7500 evaltree(n->nnot.com, EV_TESTED);
7501 status = !exitstatus;
7502 goto setstatus;
7503 case NREDIR:
7504 expredir(n->nredir.redirect);
7505 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7506 if (!status) {
7507 evaltree(n->nredir.n, flags & EV_TESTED);
7508 status = exitstatus;
7509 }
7510 popredir(0);
7511 goto setstatus;
7512 case NCMD:
7513 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007514 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007515 if (eflag && !(flags & EV_TESTED))
7516 checkexit = ~0;
7517 goto calleval;
7518 case NFOR:
7519 evalfn = evalfor;
7520 goto calleval;
7521 case NWHILE:
7522 case NUNTIL:
7523 evalfn = evalloop;
7524 goto calleval;
7525 case NSUBSHELL:
7526 case NBACKGND:
7527 evalfn = evalsubshell;
7528 goto calleval;
7529 case NPIPE:
7530 evalfn = evalpipe;
7531 goto checkexit;
7532 case NCASE:
7533 evalfn = evalcase;
7534 goto calleval;
7535 case NAND:
7536 case NOR:
7537 case NSEMI:
7538#if NAND + 1 != NOR
7539#error NAND + 1 != NOR
7540#endif
7541#if NOR + 1 != NSEMI
7542#error NOR + 1 != NSEMI
7543#endif
7544 isor = n->type - NAND;
7545 evaltree(
7546 n->nbinary.ch1,
7547 (flags | ((isor >> 1) - 1)) & EV_TESTED
7548 );
7549 if (!exitstatus == isor)
7550 break;
7551 if (!evalskip) {
7552 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007553 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007554 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007555 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007556 evalfn(n, flags);
7557 break;
7558 }
7559 break;
7560 case NIF:
7561 evaltree(n->nif.test, EV_TESTED);
7562 if (evalskip)
7563 break;
7564 if (exitstatus == 0) {
7565 n = n->nif.ifpart;
7566 goto evaln;
7567 } else if (n->nif.elsepart) {
7568 n = n->nif.elsepart;
7569 goto evaln;
7570 }
7571 goto success;
7572 case NDEFUN:
7573 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007574 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007575 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007576 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007577 exitstatus = status;
7578 break;
7579 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007580 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007581 if ((checkexit & exitstatus))
7582 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007583 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007584 goto exexit;
7585
7586 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007587 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007588 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007589 }
Eric Andersen62483552001-07-10 06:09:16 +00007590}
7591
Eric Andersenc470f442003-07-28 09:56:35 +00007592#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7593static
7594#endif
7595void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7596
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007597static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007598
7599static void
7600evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007601{
7602 int status;
7603
7604 loopnest++;
7605 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007606 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007607 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007608 int i;
7609
Eric Andersencb57d552001-06-28 07:25:16 +00007610 evaltree(n->nbinary.ch1, EV_TESTED);
7611 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007612 skipping:
7613 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007614 evalskip = 0;
7615 continue;
7616 }
7617 if (evalskip == SKIPBREAK && --skipcount <= 0)
7618 evalskip = 0;
7619 break;
7620 }
Eric Andersenc470f442003-07-28 09:56:35 +00007621 i = exitstatus;
7622 if (n->type != NWHILE)
7623 i = !i;
7624 if (i != 0)
7625 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007626 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007627 status = exitstatus;
7628 if (evalskip)
7629 goto skipping;
7630 }
7631 loopnest--;
7632 exitstatus = status;
7633}
7634
Eric Andersenc470f442003-07-28 09:56:35 +00007635static void
7636evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007637{
7638 struct arglist arglist;
7639 union node *argp;
7640 struct strlist *sp;
7641 struct stackmark smark;
7642
7643 setstackmark(&smark);
7644 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007645 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007646 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007647 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007648 if (evalskip)
7649 goto out;
7650 }
7651 *arglist.lastp = NULL;
7652
7653 exitstatus = 0;
7654 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007655 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007656 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007657 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007658 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007659 if (evalskip) {
7660 if (evalskip == SKIPCONT && --skipcount <= 0) {
7661 evalskip = 0;
7662 continue;
7663 }
7664 if (evalskip == SKIPBREAK && --skipcount <= 0)
7665 evalskip = 0;
7666 break;
7667 }
7668 }
7669 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007670 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007671 popstackmark(&smark);
7672}
7673
Eric Andersenc470f442003-07-28 09:56:35 +00007674static void
7675evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007676{
7677 union node *cp;
7678 union node *patp;
7679 struct arglist arglist;
7680 struct stackmark smark;
7681
7682 setstackmark(&smark);
7683 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007684 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007685 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007686 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7687 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007688 if (casematch(patp, arglist.list->text)) {
7689 if (evalskip == 0) {
7690 evaltree(cp->nclist.body, flags);
7691 }
7692 goto out;
7693 }
7694 }
7695 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007696 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007697 popstackmark(&smark);
7698}
7699
Eric Andersenc470f442003-07-28 09:56:35 +00007700/*
7701 * Kick off a subshell to evaluate a tree.
7702 */
Eric Andersenc470f442003-07-28 09:56:35 +00007703static void
7704evalsubshell(union node *n, int flags)
7705{
7706 struct job *jp;
7707 int backgnd = (n->type == NBACKGND);
7708 int status;
7709
7710 expredir(n->nredir.redirect);
7711 if (!backgnd && flags & EV_EXIT && !trap[0])
7712 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007713 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007714 jp = makejob(n, 1);
7715 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007716 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007717 flags |= EV_EXIT;
7718 if (backgnd)
7719 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007720 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007721 redirect(n->nredir.redirect, 0);
7722 evaltreenr(n->nredir.n, flags);
7723 /* never returns */
7724 }
7725 status = 0;
7726 if (! backgnd)
7727 status = waitforjob(jp);
7728 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007729 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007730}
7731
Eric Andersenc470f442003-07-28 09:56:35 +00007732/*
7733 * Compute the names of the files in a redirection list.
7734 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007735static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007736static void
7737expredir(union node *n)
7738{
7739 union node *redir;
7740
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007741 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007742 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007743
7744 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007745 fn.lastp = &fn.list;
7746 switch (redir->type) {
7747 case NFROMTO:
7748 case NFROM:
7749 case NTO:
7750 case NCLOBBER:
7751 case NAPPEND:
7752 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7753 redir->nfile.expfname = fn.list->text;
7754 break;
7755 case NFROMFD:
7756 case NTOFD:
7757 if (redir->ndup.vname) {
7758 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007759 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007760 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007761 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007762 }
7763 break;
7764 }
7765 }
7766}
7767
Eric Andersencb57d552001-06-28 07:25:16 +00007768/*
Eric Andersencb57d552001-06-28 07:25:16 +00007769 * Evaluate a pipeline. All the processes in the pipeline are children
7770 * of the process creating the pipeline. (This differs from some versions
7771 * of the shell, which make the last process in a pipeline the parent
7772 * of all the rest.)
7773 */
Eric Andersenc470f442003-07-28 09:56:35 +00007774static void
7775evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007776{
7777 struct job *jp;
7778 struct nodelist *lp;
7779 int pipelen;
7780 int prevfd;
7781 int pip[2];
7782
Eric Andersenc470f442003-07-28 09:56:35 +00007783 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007784 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007785 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007786 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007787 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007788 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007789 jp = makejob(n, pipelen);
7790 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007791 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007792 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007793 pip[1] = -1;
7794 if (lp->next) {
7795 if (pipe(pip) < 0) {
7796 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007797 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007798 }
7799 }
7800 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007801 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007802 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007803 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007804 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007805 if (prevfd > 0) {
7806 dup2(prevfd, 0);
7807 close(prevfd);
7808 }
7809 if (pip[1] > 1) {
7810 dup2(pip[1], 1);
7811 close(pip[1]);
7812 }
Eric Andersenc470f442003-07-28 09:56:35 +00007813 evaltreenr(lp->n, flags);
7814 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007815 }
7816 if (prevfd >= 0)
7817 close(prevfd);
7818 prevfd = pip[0];
7819 close(pip[1]);
7820 }
Eric Andersencb57d552001-06-28 07:25:16 +00007821 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007822 exitstatus = waitforjob(jp);
7823 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007824 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007825 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007826}
7827
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007828/*
7829 * Controls whether the shell is interactive or not.
7830 */
7831static void
7832setinteractive(int on)
7833{
7834 static int is_interactive;
7835
7836 if (++on == is_interactive)
7837 return;
7838 is_interactive = on;
7839 setsignal(SIGINT);
7840 setsignal(SIGQUIT);
7841 setsignal(SIGTERM);
7842#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7843 if (is_interactive > 1) {
7844 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007845 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007846
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007847 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007848 out1fmt(
7849 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007850 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007851 "Enter 'help' for a list of built-in commands."
7852 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007853 bb_banner);
7854 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007855 }
7856 }
7857#endif
7858}
7859
7860#if ENABLE_FEATURE_EDITING_VI
7861#define setvimode(on) do { \
7862 if (on) line_input_state->flags |= VI_MODE; \
7863 else line_input_state->flags &= ~VI_MODE; \
7864} while (0)
7865#else
7866#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7867#endif
7868
7869static void
7870optschanged(void)
7871{
7872#if DEBUG
7873 opentrace();
7874#endif
7875 setinteractive(iflag);
7876 setjobctl(mflag);
7877 setvimode(viflag);
7878}
7879
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007880static struct localvar *localvars;
7881
7882/*
7883 * Called after a function returns.
7884 * Interrupts must be off.
7885 */
7886static void
7887poplocalvars(void)
7888{
7889 struct localvar *lvp;
7890 struct var *vp;
7891
7892 while ((lvp = localvars) != NULL) {
7893 localvars = lvp->next;
7894 vp = lvp->vp;
7895 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7896 if (vp == NULL) { /* $- saved */
7897 memcpy(optlist, lvp->text, sizeof(optlist));
7898 free((char*)lvp->text);
7899 optschanged();
7900 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7901 unsetvar(vp->text);
7902 } else {
7903 if (vp->func)
7904 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7905 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7906 free((char*)vp->text);
7907 vp->flags = lvp->flags;
7908 vp->text = lvp->text;
7909 }
7910 free(lvp);
7911 }
7912}
7913
7914static int
7915evalfun(struct funcnode *func, int argc, char **argv, int flags)
7916{
7917 volatile struct shparam saveparam;
7918 struct localvar *volatile savelocalvars;
7919 struct jmploc *volatile savehandler;
7920 struct jmploc jmploc;
7921 int e;
7922
7923 saveparam = shellparam;
7924 savelocalvars = localvars;
7925 e = setjmp(jmploc.loc);
7926 if (e) {
7927 goto funcdone;
7928 }
7929 INT_OFF;
7930 savehandler = exception_handler;
7931 exception_handler = &jmploc;
7932 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007933 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007934 func->count++;
7935 funcnest++;
7936 INT_ON;
7937 shellparam.nparam = argc - 1;
7938 shellparam.p = argv + 1;
7939#if ENABLE_ASH_GETOPTS
7940 shellparam.optind = 1;
7941 shellparam.optoff = -1;
7942#endif
7943 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007944 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007945 INT_OFF;
7946 funcnest--;
7947 freefunc(func);
7948 poplocalvars();
7949 localvars = savelocalvars;
7950 freeparam(&shellparam);
7951 shellparam = saveparam;
7952 exception_handler = savehandler;
7953 INT_ON;
7954 evalskip &= ~SKIPFUNC;
7955 return e;
7956}
7957
Denis Vlasenko131ae172007-02-18 13:00:19 +00007958#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007959static char **
7960parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007961{
7962 char *cp, c;
7963
7964 for (;;) {
7965 cp = *++argv;
7966 if (!cp)
7967 return 0;
7968 if (*cp++ != '-')
7969 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007970 c = *cp++;
7971 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007972 break;
7973 if (c == '-' && !*cp) {
7974 argv++;
7975 break;
7976 }
7977 do {
7978 switch (c) {
7979 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007980 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007981 break;
7982 default:
7983 /* run 'typecmd' for other options */
7984 return 0;
7985 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007986 c = *cp++;
7987 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007988 }
7989 return argv;
7990}
7991#endif
7992
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007993/*
7994 * Make a variable a local variable. When a variable is made local, it's
7995 * value and flags are saved in a localvar structure. The saved values
7996 * will be restored when the shell function returns. We handle the name
7997 * "-" as a special case.
7998 */
7999static void
8000mklocal(char *name)
8001{
8002 struct localvar *lvp;
8003 struct var **vpp;
8004 struct var *vp;
8005
8006 INT_OFF;
8007 lvp = ckmalloc(sizeof(struct localvar));
8008 if (LONE_DASH(name)) {
8009 char *p;
8010 p = ckmalloc(sizeof(optlist));
8011 lvp->text = memcpy(p, optlist, sizeof(optlist));
8012 vp = NULL;
8013 } else {
8014 char *eq;
8015
8016 vpp = hashvar(name);
8017 vp = *findvar(vpp, name);
8018 eq = strchr(name, '=');
8019 if (vp == NULL) {
8020 if (eq)
8021 setvareq(name, VSTRFIXED);
8022 else
8023 setvar(name, NULL, VSTRFIXED);
8024 vp = *vpp; /* the new variable */
8025 lvp->flags = VUNSET;
8026 } else {
8027 lvp->text = vp->text;
8028 lvp->flags = vp->flags;
8029 vp->flags |= VSTRFIXED|VTEXTFIXED;
8030 if (eq)
8031 setvareq(name, 0);
8032 }
8033 }
8034 lvp->vp = vp;
8035 lvp->next = localvars;
8036 localvars = lvp;
8037 INT_ON;
8038}
8039
8040/*
8041 * The "local" command.
8042 */
8043static int
8044localcmd(int argc, char **argv)
8045{
8046 char *name;
8047
8048 argv = argptr;
8049 while ((name = *argv++) != NULL) {
8050 mklocal(name);
8051 }
8052 return 0;
8053}
8054
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008055static int
8056falsecmd(int argc, char **argv)
8057{
8058 return 1;
8059}
8060
8061static int
8062truecmd(int argc, char **argv)
8063{
8064 return 0;
8065}
8066
8067static int
8068execcmd(int argc, char **argv)
8069{
8070 if (argc > 1) {
8071 iflag = 0; /* exit on error */
8072 mflag = 0;
8073 optschanged();
8074 shellexec(argv + 1, pathval(), 0);
8075 }
8076 return 0;
8077}
8078
8079/*
8080 * The return command.
8081 */
8082static int
8083returncmd(int argc, char **argv)
8084{
8085 /*
8086 * If called outside a function, do what ksh does;
8087 * skip the rest of the file.
8088 */
8089 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8090 return argv[1] ? number(argv[1]) : exitstatus;
8091}
8092
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008093/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008094static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008095static int dotcmd(int, char **);
8096static int evalcmd(int, char **);
8097#if ENABLE_ASH_BUILTIN_ECHO
8098static int echocmd(int, char **);
8099#endif
8100#if ENABLE_ASH_BUILTIN_TEST
8101static int testcmd(int, char **);
8102#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008103static int exitcmd(int, char **);
8104static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008105#if ENABLE_ASH_GETOPTS
8106static int getoptscmd(int, char **);
8107#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008108#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8109static int helpcmd(int argc, char **argv);
8110#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008111#if ENABLE_ASH_MATH_SUPPORT
8112static int letcmd(int, char **);
8113#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008114static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008115static int setcmd(int, char **);
8116static int shiftcmd(int, char **);
8117static int timescmd(int, char **);
8118static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008119static int umaskcmd(int, char **);
8120static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008121static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008122
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008123#define BUILTIN_NOSPEC "0"
8124#define BUILTIN_SPECIAL "1"
8125#define BUILTIN_REGULAR "2"
8126#define BUILTIN_SPEC_REG "3"
8127#define BUILTIN_ASSIGN "4"
8128#define BUILTIN_SPEC_ASSG "5"
8129#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008130#define BUILTIN_SPEC_REG_ASSG "7"
8131
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008132/* make sure to keep these in proper order since it is searched via bsearch() */
8133static const struct builtincmd builtintab[] = {
8134 { BUILTIN_SPEC_REG ".", dotcmd },
8135 { BUILTIN_SPEC_REG ":", truecmd },
8136#if ENABLE_ASH_BUILTIN_TEST
8137 { BUILTIN_REGULAR "[", testcmd },
8138 { BUILTIN_REGULAR "[[", testcmd },
8139#endif
8140#if ENABLE_ASH_ALIAS
8141 { BUILTIN_REG_ASSG "alias", aliascmd },
8142#endif
8143#if JOBS
8144 { BUILTIN_REGULAR "bg", fg_bgcmd },
8145#endif
8146 { BUILTIN_SPEC_REG "break", breakcmd },
8147 { BUILTIN_REGULAR "cd", cdcmd },
8148 { BUILTIN_NOSPEC "chdir", cdcmd },
8149#if ENABLE_ASH_CMDCMD
8150 { BUILTIN_REGULAR "command", commandcmd },
8151#endif
8152 { BUILTIN_SPEC_REG "continue", breakcmd },
8153#if ENABLE_ASH_BUILTIN_ECHO
8154 { BUILTIN_REGULAR "echo", echocmd },
8155#endif
8156 { BUILTIN_SPEC_REG "eval", evalcmd },
8157 { BUILTIN_SPEC_REG "exec", execcmd },
8158 { BUILTIN_SPEC_REG "exit", exitcmd },
8159 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8160 { BUILTIN_REGULAR "false", falsecmd },
8161#if JOBS
8162 { BUILTIN_REGULAR "fg", fg_bgcmd },
8163#endif
8164#if ENABLE_ASH_GETOPTS
8165 { BUILTIN_REGULAR "getopts", getoptscmd },
8166#endif
8167 { BUILTIN_NOSPEC "hash", hashcmd },
8168#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8169 { BUILTIN_NOSPEC "help", helpcmd },
8170#endif
8171#if JOBS
8172 { BUILTIN_REGULAR "jobs", jobscmd },
8173 { BUILTIN_REGULAR "kill", killcmd },
8174#endif
8175#if ENABLE_ASH_MATH_SUPPORT
8176 { BUILTIN_NOSPEC "let", letcmd },
8177#endif
8178 { BUILTIN_ASSIGN "local", localcmd },
8179 { BUILTIN_NOSPEC "pwd", pwdcmd },
8180 { BUILTIN_REGULAR "read", readcmd },
8181 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8182 { BUILTIN_SPEC_REG "return", returncmd },
8183 { BUILTIN_SPEC_REG "set", setcmd },
8184 { BUILTIN_SPEC_REG "shift", shiftcmd },
8185 { BUILTIN_SPEC_REG "source", dotcmd },
8186#if ENABLE_ASH_BUILTIN_TEST
8187 { BUILTIN_REGULAR "test", testcmd },
8188#endif
8189 { BUILTIN_SPEC_REG "times", timescmd },
8190 { BUILTIN_SPEC_REG "trap", trapcmd },
8191 { BUILTIN_REGULAR "true", truecmd },
8192 { BUILTIN_NOSPEC "type", typecmd },
8193 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8194 { BUILTIN_REGULAR "umask", umaskcmd },
8195#if ENABLE_ASH_ALIAS
8196 { BUILTIN_REGULAR "unalias", unaliascmd },
8197#endif
8198 { BUILTIN_SPEC_REG "unset", unsetcmd },
8199 { BUILTIN_REGULAR "wait", waitcmd },
8200};
8201
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008202
8203#define COMMANDCMD (builtintab + 5 + \
8204 2 * ENABLE_ASH_BUILTIN_TEST + \
8205 ENABLE_ASH_ALIAS + \
8206 ENABLE_ASH_JOB_CONTROL)
8207#define EXECCMD (builtintab + 7 + \
8208 2 * ENABLE_ASH_BUILTIN_TEST + \
8209 ENABLE_ASH_ALIAS + \
8210 ENABLE_ASH_JOB_CONTROL + \
8211 ENABLE_ASH_CMDCMD + \
8212 ENABLE_ASH_BUILTIN_ECHO)
8213
8214/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008215 * Search the table of builtin commands.
8216 */
8217static struct builtincmd *
8218find_builtin(const char *name)
8219{
8220 struct builtincmd *bp;
8221
8222 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008223 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008224 pstrcmp
8225 );
8226 return bp;
8227}
8228
8229/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008230 * Execute a simple command.
8231 */
8232static int back_exitstatus; /* exit status of backquoted command */
8233static int
8234isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008235{
8236 const char *q = endofname(p);
8237 if (p == q)
8238 return 0;
8239 return *q == '=';
8240}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008241static int
8242bltincmd(int argc, char **argv)
8243{
8244 /* Preserve exitstatus of a previous possible redirection
8245 * as POSIX mandates */
8246 return back_exitstatus;
8247}
Eric Andersenc470f442003-07-28 09:56:35 +00008248static void
8249evalcommand(union node *cmd, int flags)
8250{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008251 static const struct builtincmd bltin = {
8252 "\0\0", bltincmd
8253 };
Eric Andersenc470f442003-07-28 09:56:35 +00008254 struct stackmark smark;
8255 union node *argp;
8256 struct arglist arglist;
8257 struct arglist varlist;
8258 char **argv;
8259 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008260 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008261 struct cmdentry cmdentry;
8262 struct job *jp;
8263 char *lastarg;
8264 const char *path;
8265 int spclbltin;
8266 int cmd_is_exec;
8267 int status;
8268 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008269 struct builtincmd *bcmd;
8270 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008271
8272 /* First expand the arguments. */
8273 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8274 setstackmark(&smark);
8275 back_exitstatus = 0;
8276
8277 cmdentry.cmdtype = CMDBUILTIN;
8278 cmdentry.u.cmd = &bltin;
8279 varlist.lastp = &varlist.list;
8280 *varlist.lastp = NULL;
8281 arglist.lastp = &arglist.list;
8282 *arglist.lastp = NULL;
8283
8284 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008285 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008286 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8287 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8288 }
8289
Eric Andersenc470f442003-07-28 09:56:35 +00008290 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8291 struct strlist **spp;
8292
8293 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008294 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008295 expandarg(argp, &arglist, EXP_VARTILDE);
8296 else
8297 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8298
Eric Andersenc470f442003-07-28 09:56:35 +00008299 for (sp = *spp; sp; sp = sp->next)
8300 argc++;
8301 }
8302
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008303 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008304 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008305 TRACE(("evalcommand arg: %s\n", sp->text));
8306 *nargv++ = sp->text;
8307 }
8308 *nargv = NULL;
8309
8310 lastarg = NULL;
8311 if (iflag && funcnest == 0 && argc > 0)
8312 lastarg = nargv[-1];
8313
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008314 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008315 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008316 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008317
8318 path = vpath.text;
8319 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8320 struct strlist **spp;
8321 char *p;
8322
8323 spp = varlist.lastp;
8324 expandarg(argp, &varlist, EXP_VARTILDE);
8325
8326 /*
8327 * Modify the command lookup path, if a PATH= assignment
8328 * is present
8329 */
8330 p = (*spp)->text;
8331 if (varequal(p, path))
8332 path = p;
8333 }
8334
8335 /* Print the command if xflag is set. */
8336 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008337 int n;
8338 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008339
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008340 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008341 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008342
8343 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008344 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008345 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008346 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008347 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008348 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008349 p--;
8350 }
8351 }
8352 sp = arglist.list;
8353 }
Rob Landley53437472006-07-16 08:14:35 +00008354 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008355 }
8356
8357 cmd_is_exec = 0;
8358 spclbltin = -1;
8359
8360 /* Now locate the command. */
8361 if (argc) {
8362 const char *oldpath;
8363 int cmd_flag = DO_ERR;
8364
8365 path += 5;
8366 oldpath = path;
8367 for (;;) {
8368 find_command(argv[0], &cmdentry, cmd_flag, path);
8369 if (cmdentry.cmdtype == CMDUNKNOWN) {
8370 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008371 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008372 goto bail;
8373 }
8374
8375 /* implement bltin and command here */
8376 if (cmdentry.cmdtype != CMDBUILTIN)
8377 break;
8378 if (spclbltin < 0)
8379 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8380 if (cmdentry.u.cmd == EXECCMD)
8381 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008382#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008383 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008384 path = oldpath;
8385 nargv = parse_command_args(argv, &path);
8386 if (!nargv)
8387 break;
8388 argc -= nargv - argv;
8389 argv = nargv;
8390 cmd_flag |= DO_NOFUNC;
8391 } else
8392#endif
8393 break;
8394 }
8395 }
8396
8397 if (status) {
8398 /* We have a redirection error. */
8399 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008400 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008401 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008402 exitstatus = status;
8403 goto out;
8404 }
8405
8406 /* Execute the command. */
8407 switch (cmdentry.cmdtype) {
8408 default:
8409 /* Fork off a child process if necessary. */
8410 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008411 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008412 jp = makejob(cmd, 1);
8413 if (forkshell(jp, cmd, FORK_FG) != 0) {
8414 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008415 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008416 break;
8417 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008418 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008419 }
8420 listsetvar(varlist.list, VEXPORT|VSTACK);
8421 shellexec(argv, path, cmdentry.u.index);
8422 /* NOTREACHED */
8423
8424 case CMDBUILTIN:
8425 cmdenviron = varlist.list;
8426 if (cmdenviron) {
8427 struct strlist *list = cmdenviron;
8428 int i = VNOSET;
8429 if (spclbltin > 0 || argc == 0) {
8430 i = 0;
8431 if (cmd_is_exec && argc > 1)
8432 i = VEXPORT;
8433 }
8434 listsetvar(list, i);
8435 }
8436 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8437 int exit_status;
8438 int i, j;
8439
8440 i = exception;
8441 if (i == EXEXIT)
8442 goto raise;
8443
8444 exit_status = 2;
8445 j = 0;
8446 if (i == EXINT)
8447 j = SIGINT;
8448 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008449 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008450 if (j)
8451 exit_status = j + 128;
8452 exitstatus = exit_status;
8453
8454 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008455 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008456 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008457 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008458 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008459 }
8460 break;
8461
8462 case CMDFUNCTION:
8463 listsetvar(varlist.list, 0);
8464 if (evalfun(cmdentry.u.func, argc, argv, flags))
8465 goto raise;
8466 break;
8467 }
8468
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008469 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008470 popredir(cmd_is_exec);
8471 if (lastarg)
8472 /* dsl: I think this is intended to be used to support
8473 * '_' in 'vi' command mode during line editing...
8474 * However I implemented that within libedit itself.
8475 */
8476 setvar("_", lastarg, 0);
8477 popstackmark(&smark);
8478}
8479
8480static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008481evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8482{
Eric Andersenc470f442003-07-28 09:56:35 +00008483 char *volatile savecmdname;
8484 struct jmploc *volatile savehandler;
8485 struct jmploc jmploc;
8486 int i;
8487
8488 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008489 i = setjmp(jmploc.loc);
8490 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008491 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008492 savehandler = exception_handler;
8493 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008494 commandname = argv[0];
8495 argptr = argv + 1;
8496 optptr = NULL; /* initialize nextopt */
8497 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008498 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008499 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008500 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008501 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008502 commandname = savecmdname;
8503 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008504 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008505
8506 return i;
8507}
8508
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008509static int
8510goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008511{
8512 return !*endofname(p);
8513}
8514
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008515
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008516/*
8517 * Search for a command. This is called before we fork so that the
8518 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008519 * the child. The check for "goodname" is an overly conservative
8520 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008521 */
Eric Andersenc470f442003-07-28 09:56:35 +00008522static void
8523prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008524{
8525 struct cmdentry entry;
8526
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008527 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8528 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008529}
8530
Eric Andersencb57d552001-06-28 07:25:16 +00008531
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008532/* ============ Builtin commands
8533 *
8534 * Builtin commands whose functions are closely tied to evaluation
8535 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008536 */
8537
8538/*
Eric Andersencb57d552001-06-28 07:25:16 +00008539 * Handle break and continue commands. Break, continue, and return are
8540 * all handled by setting the evalskip flag. The evaluation routines
8541 * above all check this flag, and if it is set they start skipping
8542 * commands rather than executing them. The variable skipcount is
8543 * the number of loops to break/continue, or the number of function
8544 * levels to return. (The latter is always 1.) It should probably
8545 * be an error to break out of more loops than exist, but it isn't
8546 * in the standard shell so we don't make it one here.
8547 */
Eric Andersenc470f442003-07-28 09:56:35 +00008548static int
8549breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008550{
8551 int n = argc > 1 ? number(argv[1]) : 1;
8552
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008553 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008554 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008555 if (n > loopnest)
8556 n = loopnest;
8557 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008558 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008559 skipcount = n;
8560 }
8561 return 0;
8562}
8563
Eric Andersenc470f442003-07-28 09:56:35 +00008564
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008565/* ============ input.c
8566 *
Eric Andersen90898442003-08-06 11:20:52 +00008567 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008568 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008569
Eric Andersenc470f442003-07-28 09:56:35 +00008570#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008571
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008572enum {
8573 INPUT_PUSH_FILE = 1,
8574 INPUT_NOFILE_OK = 2,
8575};
Eric Andersencb57d552001-06-28 07:25:16 +00008576
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008577static int plinno = 1; /* input line number */
8578/* number of characters left in input buffer */
8579static int parsenleft; /* copy of parsefile->nleft */
8580static int parselleft; /* copy of parsefile->lleft */
8581/* next character in input buffer */
8582static char *parsenextc; /* copy of parsefile->nextc */
8583
8584static int checkkwd;
8585/* values of checkkwd variable */
8586#define CHKALIAS 0x1
8587#define CHKKWD 0x2
8588#define CHKNL 0x4
8589
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008590static void
8591popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008592{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008593 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008594
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008595 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008596#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008597 if (sp->ap) {
8598 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8599 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008600 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008601 if (sp->string != sp->ap->val) {
8602 free(sp->string);
8603 }
8604 sp->ap->flag &= ~ALIASINUSE;
8605 if (sp->ap->flag & ALIASDEAD) {
8606 unalias(sp->ap->name);
8607 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008608 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008609#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008610 parsenextc = sp->prevstring;
8611 parsenleft = sp->prevnleft;
8612/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8613 parsefile->strpush = sp->prev;
8614 if (sp != &(parsefile->basestrpush))
8615 free(sp);
8616 INT_ON;
8617}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008618
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008619static int
8620preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008621{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008622 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008623 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008624 parsenextc = buf;
8625
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008626 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008627#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008628 if (!iflag || parsefile->fd)
8629 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8630 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008631#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008632 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008633#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008634 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8635 if (nr == 0) {
8636 /* Ctrl+C pressed */
8637 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008638 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008639 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008640 raise(SIGINT);
8641 return 1;
8642 }
Eric Andersenc470f442003-07-28 09:56:35 +00008643 goto retry;
8644 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008645 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008646 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008647 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008648 }
Eric Andersencb57d552001-06-28 07:25:16 +00008649 }
8650#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008651 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008652#endif
8653
8654 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008655 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008656 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008657 if (flags >= 0 && (flags & O_NONBLOCK)) {
8658 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008659 if (fcntl(0, F_SETFL, flags) >= 0) {
8660 out2str("sh: turning off NDELAY mode\n");
8661 goto retry;
8662 }
8663 }
8664 }
8665 }
8666 return nr;
8667}
8668
8669/*
8670 * Refill the input buffer and return the next input character:
8671 *
8672 * 1) If a string was pushed back on the input, pop it;
8673 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8674 * from a string so we can't refill the buffer, return EOF.
8675 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8676 * 4) Process input up to the next newline, deleting nul characters.
8677 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008678static int
Eric Andersenc470f442003-07-28 09:56:35 +00008679preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008680{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008681 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008682 int more;
8683 char savec;
8684
8685 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008686#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008687 if (parsenleft == -1 && parsefile->strpush->ap &&
8688 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008689 return PEOA;
8690 }
Eric Andersen2870d962001-07-02 17:27:21 +00008691#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008692 popstring();
8693 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008694 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008695 }
8696 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8697 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008698 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008699
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008700 more = parselleft;
8701 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008702 again:
8703 more = preadfd();
8704 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008705 parselleft = parsenleft = EOF_NLEFT;
8706 return PEOF;
8707 }
8708 }
8709
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008710 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008711
8712 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008713 for (;;) {
8714 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008715
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008716 more--;
8717 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008718
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008719 if (!c)
8720 memmove(q, q + 1, more);
8721 else {
8722 q++;
8723 if (c == '\n') {
8724 parsenleft = q - parsenextc - 1;
8725 break;
8726 }
Eric Andersencb57d552001-06-28 07:25:16 +00008727 }
8728
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008729 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008730 parsenleft = q - parsenextc - 1;
8731 if (parsenleft < 0)
8732 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008733 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008734 }
8735 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008736 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008737
8738 savec = *q;
8739 *q = '\0';
8740
8741 if (vflag) {
8742 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008743 }
8744
8745 *q = savec;
8746
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008747 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008748}
8749
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008750#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008751static int
8752pgetc(void)
8753{
8754 return pgetc_as_macro();
8755}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008756
8757#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8758#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008759#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008760#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008761#endif
8762
8763/*
8764 * Same as pgetc(), but ignores PEOA.
8765 */
8766#if ENABLE_ASH_ALIAS
8767static int
8768pgetc2(void)
8769{
8770 int c;
8771
8772 do {
8773 c = pgetc_macro();
8774 } while (c == PEOA);
8775 return c;
8776}
8777#else
8778static int
8779pgetc2(void)
8780{
8781 return pgetc_macro();
8782}
8783#endif
8784
8785/*
8786 * Read a line from the script.
8787 */
8788static char *
8789pfgets(char *line, int len)
8790{
8791 char *p = line;
8792 int nleft = len;
8793 int c;
8794
8795 while (--nleft > 0) {
8796 c = pgetc2();
8797 if (c == PEOF) {
8798 if (p == line)
8799 return NULL;
8800 break;
8801 }
8802 *p++ = c;
8803 if (c == '\n')
8804 break;
8805 }
8806 *p = '\0';
8807 return line;
8808}
8809
Eric Andersenc470f442003-07-28 09:56:35 +00008810/*
8811 * Undo the last call to pgetc. Only one character may be pushed back.
8812 * PEOF may be pushed back.
8813 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008814static void
Eric Andersenc470f442003-07-28 09:56:35 +00008815pungetc(void)
8816{
8817 parsenleft++;
8818 parsenextc--;
8819}
Eric Andersencb57d552001-06-28 07:25:16 +00008820
8821/*
8822 * Push a string back onto the input at this current parsefile level.
8823 * We handle aliases this way.
8824 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008825static void
Eric Andersenc470f442003-07-28 09:56:35 +00008826pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008827{
Eric Andersencb57d552001-06-28 07:25:16 +00008828 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008829 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008830
Eric Andersenc470f442003-07-28 09:56:35 +00008831 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008832 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008833/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8834 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008835 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008836 sp->prev = parsefile->strpush;
8837 parsefile->strpush = sp;
8838 } else
8839 sp = parsefile->strpush = &(parsefile->basestrpush);
8840 sp->prevstring = parsenextc;
8841 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008842#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008843 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008844 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008845 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008846 sp->string = s;
8847 }
Eric Andersen2870d962001-07-02 17:27:21 +00008848#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008849 parsenextc = s;
8850 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008851 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008852}
8853
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008854/*
8855 * To handle the "." command, a stack of input files is used. Pushfile
8856 * adds a new entry to the stack and popfile restores the previous level.
8857 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008858static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008859pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008860{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008861 struct parsefile *pf;
8862
8863 parsefile->nleft = parsenleft;
8864 parsefile->lleft = parselleft;
8865 parsefile->nextc = parsenextc;
8866 parsefile->linno = plinno;
8867 pf = ckmalloc(sizeof(*pf));
8868 pf->prev = parsefile;
8869 pf->fd = -1;
8870 pf->strpush = NULL;
8871 pf->basestrpush.prev = NULL;
8872 parsefile = pf;
8873}
8874
8875static void
8876popfile(void)
8877{
8878 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008879
Denis Vlasenkob012b102007-02-19 22:43:01 +00008880 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008881 if (pf->fd >= 0)
8882 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008883 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008884 while (pf->strpush)
8885 popstring();
8886 parsefile = pf->prev;
8887 free(pf);
8888 parsenleft = parsefile->nleft;
8889 parselleft = parsefile->lleft;
8890 parsenextc = parsefile->nextc;
8891 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008892 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008893}
8894
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008895/*
8896 * Return to top level.
8897 */
8898static void
8899popallfiles(void)
8900{
8901 while (parsefile != &basepf)
8902 popfile();
8903}
8904
8905/*
8906 * Close the file(s) that the shell is reading commands from. Called
8907 * after a fork is done.
8908 */
8909static void
8910closescript(void)
8911{
8912 popallfiles();
8913 if (parsefile->fd > 0) {
8914 close(parsefile->fd);
8915 parsefile->fd = 0;
8916 }
8917}
8918
8919/*
8920 * Like setinputfile, but takes an open file descriptor. Call this with
8921 * interrupts off.
8922 */
8923static void
8924setinputfd(int fd, int push)
8925{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008926 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008927 if (push) {
8928 pushfile();
8929 parsefile->buf = 0;
8930 }
8931 parsefile->fd = fd;
8932 if (parsefile->buf == NULL)
8933 parsefile->buf = ckmalloc(IBUFSIZ);
8934 parselleft = parsenleft = 0;
8935 plinno = 1;
8936}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008937
Eric Andersenc470f442003-07-28 09:56:35 +00008938/*
8939 * Set the input to take input from a file. If push is set, push the
8940 * old input onto the stack first.
8941 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008942static int
8943setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008944{
8945 int fd;
8946 int fd2;
8947
Denis Vlasenkob012b102007-02-19 22:43:01 +00008948 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008949 fd = open(fname, O_RDONLY);
8950 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008951 if (flags & INPUT_NOFILE_OK)
8952 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008953 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008954 }
Eric Andersenc470f442003-07-28 09:56:35 +00008955 if (fd < 10) {
8956 fd2 = copyfd(fd, 10);
8957 close(fd);
8958 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008959 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008960 fd = fd2;
8961 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008962 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008963 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008964 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008965 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008966}
8967
Eric Andersencb57d552001-06-28 07:25:16 +00008968/*
8969 * Like setinputfile, but takes input from a string.
8970 */
Eric Andersenc470f442003-07-28 09:56:35 +00008971static void
8972setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008973{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008974 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008975 pushfile();
8976 parsenextc = string;
8977 parsenleft = strlen(string);
8978 parsefile->buf = NULL;
8979 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008980 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008981}
8982
8983
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008984/* ============ mail.c
8985 *
8986 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008987 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008988
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008989#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008990
Eric Andersencb57d552001-06-28 07:25:16 +00008991#define MAXMBOXES 10
8992
Eric Andersenc470f442003-07-28 09:56:35 +00008993/* times of mailboxes */
8994static time_t mailtime[MAXMBOXES];
8995/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008996static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008997
Eric Andersencb57d552001-06-28 07:25:16 +00008998/*
Eric Andersenc470f442003-07-28 09:56:35 +00008999 * Print appropriate message(s) if mail has arrived.
9000 * If mail_var_path_changed is set,
9001 * then the value of MAIL has mail_var_path_changed,
9002 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009003 */
Eric Andersenc470f442003-07-28 09:56:35 +00009004static void
9005chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009006{
Eric Andersencb57d552001-06-28 07:25:16 +00009007 const char *mpath;
9008 char *p;
9009 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009010 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009011 struct stackmark smark;
9012 struct stat statb;
9013
Eric Andersencb57d552001-06-28 07:25:16 +00009014 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009015 mpath = mpathset() ? mpathval() : mailval();
9016 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009017 p = padvance(&mpath, nullstr);
9018 if (p == NULL)
9019 break;
9020 if (*p == '\0')
9021 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009022 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009023#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009024 if (q[-1] != '/')
9025 abort();
9026#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009027 q[-1] = '\0'; /* delete trailing '/' */
9028 if (stat(p, &statb) < 0) {
9029 *mtp = 0;
9030 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009031 }
Eric Andersenc470f442003-07-28 09:56:35 +00009032 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9033 fprintf(
9034 stderr, snlfmt,
9035 pathopt ? pathopt : "you have mail"
9036 );
9037 }
9038 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009039 }
Eric Andersenc470f442003-07-28 09:56:35 +00009040 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009041 popstackmark(&smark);
9042}
Eric Andersencb57d552001-06-28 07:25:16 +00009043
Eric Andersenc470f442003-07-28 09:56:35 +00009044static void
9045changemail(const char *val)
9046{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009047 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009048}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009049
Denis Vlasenko131ae172007-02-18 13:00:19 +00009050#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009051
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009052
9053/* ============ ??? */
9054
Eric Andersencb57d552001-06-28 07:25:16 +00009055/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009056 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009057 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009058static void
9059setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009060{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009061 char **newparam;
9062 char **ap;
9063 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009064
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009065 for (nparam = 0; argv[nparam]; nparam++);
9066 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9067 while (*argv) {
9068 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009069 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009070 *ap = NULL;
9071 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009072 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009073 shellparam.nparam = nparam;
9074 shellparam.p = newparam;
9075#if ENABLE_ASH_GETOPTS
9076 shellparam.optind = 1;
9077 shellparam.optoff = -1;
9078#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009079}
9080
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009081/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009082 * Process shell options. The global variable argptr contains a pointer
9083 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009084 */
9085static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009086minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009087{
9088 int i;
9089
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009090 if (name) {
9091 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009092 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009093 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009094 return;
9095 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009096 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009097 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009098 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009099 out1str("Current option settings\n");
9100 for (i = 0; i < NOPTS; i++)
9101 out1fmt("%-16s%s\n", optnames(i),
9102 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009103}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009104static void
9105setoption(int flag, int val)
9106{
9107 int i;
9108
9109 for (i = 0; i < NOPTS; i++) {
9110 if (optletters(i) == flag) {
9111 optlist[i] = val;
9112 return;
9113 }
9114 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009115 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009116 /* NOTREACHED */
9117}
Eric Andersenc470f442003-07-28 09:56:35 +00009118static void
9119options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009120{
9121 char *p;
9122 int val;
9123 int c;
9124
9125 if (cmdline)
9126 minusc = NULL;
9127 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009128 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009129 if (c != '-' && c != '+')
9130 break;
9131 argptr++;
9132 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009133 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009134 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009135 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009136 if (!cmdline) {
9137 /* "-" means turn off -x and -v */
9138 if (p[0] == '\0')
9139 xflag = vflag = 0;
9140 /* "--" means reset params */
9141 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009142 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009143 }
Eric Andersenc470f442003-07-28 09:56:35 +00009144 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009145 }
Eric Andersencb57d552001-06-28 07:25:16 +00009146 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009147 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009148 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009149 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009150 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009151 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009152 } else if (c == 'o') {
9153 minus_o(*argptr, val);
9154 if (*argptr)
9155 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009156 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9157 isloginsh = 1;
9158 /* bash does not accept +-login, we also won't */
9159 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009160 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009161 isloginsh = 1;
9162 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009163 } else {
9164 setoption(c, val);
9165 }
9166 }
9167 }
9168}
9169
Eric Andersencb57d552001-06-28 07:25:16 +00009170/*
Eric Andersencb57d552001-06-28 07:25:16 +00009171 * The shift builtin command.
9172 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009173static int
Eric Andersenc470f442003-07-28 09:56:35 +00009174shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009175{
9176 int n;
9177 char **ap1, **ap2;
9178
9179 n = 1;
9180 if (argc > 1)
9181 n = number(argv[1]);
9182 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009183 ash_msg_and_raise_error("can't shift that many");
9184 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009185 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009186 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009187 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009188 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009189 }
9190 ap2 = shellparam.p;
9191 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009192#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009193 shellparam.optind = 1;
9194 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009195#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009196 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 return 0;
9198}
9199
Eric Andersencb57d552001-06-28 07:25:16 +00009200/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009201 * POSIX requires that 'set' (but not export or readonly) output the
9202 * variables in lexicographic order - by the locale's collating order (sigh).
9203 * Maybe we could keep them in an ordered balanced binary tree
9204 * instead of hashed lists.
9205 * For now just roll 'em through qsort for printing...
9206 */
9207static int
9208showvars(const char *sep_prefix, int on, int off)
9209{
9210 const char *sep;
9211 char **ep, **epend;
9212
9213 ep = listvars(on, off, &epend);
9214 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9215
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009216 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009217
9218 for (; ep < epend; ep++) {
9219 const char *p;
9220 const char *q;
9221
9222 p = strchrnul(*ep, '=');
9223 q = nullstr;
9224 if (*p)
9225 q = single_quote(++p);
9226 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9227 }
9228 return 0;
9229}
9230
9231/*
Eric Andersencb57d552001-06-28 07:25:16 +00009232 * The set command builtin.
9233 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009234static int
Eric Andersenc470f442003-07-28 09:56:35 +00009235setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009236{
9237 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009238 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009239 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009240 options(0);
9241 optschanged();
9242 if (*argptr != NULL) {
9243 setparam(argptr);
9244 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009245 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009246 return 0;
9247}
9248
Denis Vlasenko131ae172007-02-18 13:00:19 +00009249#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009250/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009251static void
9252change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009253{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009254 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009255 /* "get", generate */
9256 char buf[16];
9257
9258 rseed = rseed * 1103515245 + 12345;
9259 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9260 /* set without recursion */
9261 setvar(vrandom.text, buf, VNOFUNC);
9262 vrandom.flags &= ~VNOFUNC;
9263 } else {
9264 /* set/reset */
9265 rseed = strtoul(value, (char **)NULL, 10);
9266 }
Eric Andersenef02f822004-03-11 13:34:24 +00009267}
Eric Andersen16767e22004-03-16 05:14:10 +00009268#endif
9269
Denis Vlasenko131ae172007-02-18 13:00:19 +00009270#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009271static int
Eric Andersenc470f442003-07-28 09:56:35 +00009272getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009273{
9274 char *p, *q;
9275 char c = '?';
9276 int done = 0;
9277 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009278 char s[12];
9279 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009280
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009281 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009282 return 1;
9283 optnext = optfirst + *param_optind - 1;
9284
9285 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009286 p = NULL;
9287 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009288 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009289 if (p == NULL || *p == '\0') {
9290 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009291 p = *optnext;
9292 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009293 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009294 p = NULL;
9295 done = 1;
9296 goto out;
9297 }
9298 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009299 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009300 goto atend;
9301 }
9302
9303 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009304 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009305 if (*q == '\0') {
9306 if (optstr[0] == ':') {
9307 s[0] = c;
9308 s[1] = '\0';
9309 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009310 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009311 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009312 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009313 }
9314 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009315 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009316 }
9317 if (*++q == ':')
9318 q++;
9319 }
9320
9321 if (*++q == ':') {
9322 if (*p == '\0' && (p = *optnext) == NULL) {
9323 if (optstr[0] == ':') {
9324 s[0] = c;
9325 s[1] = '\0';
9326 err |= setvarsafe("OPTARG", s, 0);
9327 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009328 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009329 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009330 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009331 c = '?';
9332 }
Eric Andersenc470f442003-07-28 09:56:35 +00009333 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009334 }
9335
9336 if (p == *optnext)
9337 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009339 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009340 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009341 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009342 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009343 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009344 *param_optind = optnext - optfirst + 1;
9345 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009346 err |= setvarsafe("OPTIND", s, VNOFUNC);
9347 s[0] = c;
9348 s[1] = '\0';
9349 err |= setvarsafe(optvar, s, 0);
9350 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009351 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009352 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009353 flush_stdout_stderr();
9354 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009355 }
9356 return done;
9357}
Eric Andersenc470f442003-07-28 09:56:35 +00009358
9359/*
9360 * The getopts builtin. Shellparam.optnext points to the next argument
9361 * to be processed. Shellparam.optptr points to the next character to
9362 * be processed in the current argument. If shellparam.optnext is NULL,
9363 * then it's the first time getopts has been called.
9364 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009365static int
Eric Andersenc470f442003-07-28 09:56:35 +00009366getoptscmd(int argc, char **argv)
9367{
9368 char **optbase;
9369
9370 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009371 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009372 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009373 optbase = shellparam.p;
9374 if (shellparam.optind > shellparam.nparam + 1) {
9375 shellparam.optind = 1;
9376 shellparam.optoff = -1;
9377 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009378 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009379 optbase = &argv[3];
9380 if (shellparam.optind > argc - 2) {
9381 shellparam.optind = 1;
9382 shellparam.optoff = -1;
9383 }
9384 }
9385
9386 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009387 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009388}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009389#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009390
Eric Andersencb57d552001-06-28 07:25:16 +00009391
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009392/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009393
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009394/*
9395 * NEOF is returned by parsecmd when it encounters an end of file. It
9396 * must be distinct from NULL, so we use the address of a variable that
9397 * happens to be handy.
9398 */
9399static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009400#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009401static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009402static int lasttoken; /* last token read */
9403static char *wordtext; /* text of last word returned by readtoken */
9404static struct nodelist *backquotelist;
9405static union node *redirnode;
9406static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009407static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009408
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009409static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9410static void
9411raise_error_syntax(const char *msg)
9412{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009413 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009414 /* NOTREACHED */
9415}
9416
9417/*
9418 * Called when an unexpected token is read during the parse. The argument
9419 * is the token that is expected, or -1 if more than one type of token can
9420 * occur at this point.
9421 */
9422static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9423static void
9424raise_error_unexpected_syntax(int token)
9425{
9426 char msg[64];
9427 int l;
9428
9429 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9430 if (token >= 0)
9431 sprintf(msg + l, " (expecting %s)", tokname(token));
9432 raise_error_syntax(msg);
9433 /* NOTREACHED */
9434}
Eric Andersencb57d552001-06-28 07:25:16 +00009435
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009436#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009437
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009438struct heredoc {
9439 struct heredoc *next; /* next here document in list */
9440 union node *here; /* redirection node */
9441 char *eofmark; /* string indicating end of input */
9442 int striptabs; /* if set, strip leading tabs */
9443};
Eric Andersencb57d552001-06-28 07:25:16 +00009444
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009445static struct heredoc *heredoclist; /* list of here documents to read */
9446
9447/* parsing is heavily cross-recursive, need these forward decls */
9448static union node *andor(void);
9449static union node *pipeline(void);
9450static union node *parse_command(void);
9451static void parseheredoc(void);
9452static char peektoken(void);
9453static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009454
Eric Andersenc470f442003-07-28 09:56:35 +00009455static union node *
9456list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009457{
9458 union node *n1, *n2, *n3;
9459 int tok;
9460
Eric Andersenc470f442003-07-28 09:56:35 +00009461 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9462 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009463 return NULL;
9464 n1 = NULL;
9465 for (;;) {
9466 n2 = andor();
9467 tok = readtoken();
9468 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009469 if (n2->type == NPIPE) {
9470 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009471 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009472 if (n2->type != NREDIR) {
9473 n3 = stalloc(sizeof(struct nredir));
9474 n3->nredir.n = n2;
9475 n3->nredir.redirect = NULL;
9476 n2 = n3;
9477 }
9478 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009479 }
9480 }
9481 if (n1 == NULL) {
9482 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009483 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009484 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009485 n3->type = NSEMI;
9486 n3->nbinary.ch1 = n1;
9487 n3->nbinary.ch2 = n2;
9488 n1 = n3;
9489 }
9490 switch (tok) {
9491 case TBACKGND:
9492 case TSEMI:
9493 tok = readtoken();
9494 /* fall through */
9495 case TNL:
9496 if (tok == TNL) {
9497 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009498 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009499 return n1;
9500 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009501 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009502 }
Eric Andersenc470f442003-07-28 09:56:35 +00009503 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009504 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009505 return n1;
9506 break;
9507 case TEOF:
9508 if (heredoclist)
9509 parseheredoc();
9510 else
Eric Andersenc470f442003-07-28 09:56:35 +00009511 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009512 return n1;
9513 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009514 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009515 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009516 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009517 return n1;
9518 }
9519 }
9520}
9521
Eric Andersenc470f442003-07-28 09:56:35 +00009522static union node *
9523andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009524{
Eric Andersencb57d552001-06-28 07:25:16 +00009525 union node *n1, *n2, *n3;
9526 int t;
9527
Eric Andersencb57d552001-06-28 07:25:16 +00009528 n1 = pipeline();
9529 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009530 t = readtoken();
9531 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009532 t = NAND;
9533 } else if (t == TOR) {
9534 t = NOR;
9535 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009536 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009537 return n1;
9538 }
Eric Andersenc470f442003-07-28 09:56:35 +00009539 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009540 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009541 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009542 n3->type = t;
9543 n3->nbinary.ch1 = n1;
9544 n3->nbinary.ch2 = n2;
9545 n1 = n3;
9546 }
9547}
9548
Eric Andersenc470f442003-07-28 09:56:35 +00009549static union node *
9550pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009551{
Eric Andersencb57d552001-06-28 07:25:16 +00009552 union node *n1, *n2, *pipenode;
9553 struct nodelist *lp, *prev;
9554 int negate;
9555
9556 negate = 0;
9557 TRACE(("pipeline: entered\n"));
9558 if (readtoken() == TNOT) {
9559 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009560 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009561 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009562 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009563 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009564 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009565 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009566 pipenode->type = NPIPE;
9567 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009568 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009569 pipenode->npipe.cmdlist = lp;
9570 lp->n = n1;
9571 do {
9572 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009573 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009574 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009575 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009576 prev->next = lp;
9577 } while (readtoken() == TPIPE);
9578 lp->next = NULL;
9579 n1 = pipenode;
9580 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009581 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009582 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009583 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009584 n2->type = NNOT;
9585 n2->nnot.com = n1;
9586 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009587 }
9588 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009589}
9590
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009591static union node *
9592makename(void)
9593{
9594 union node *n;
9595
9596 n = stalloc(sizeof(struct narg));
9597 n->type = NARG;
9598 n->narg.next = NULL;
9599 n->narg.text = wordtext;
9600 n->narg.backquote = backquotelist;
9601 return n;
9602}
9603
9604static void
9605fixredir(union node *n, const char *text, int err)
9606{
9607 TRACE(("Fix redir %s %d\n", text, err));
9608 if (!err)
9609 n->ndup.vname = NULL;
9610
9611 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009612 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009613 else if (LONE_DASH(text))
9614 n->ndup.dupfd = -1;
9615 else {
9616 if (err)
9617 raise_error_syntax("Bad fd number");
9618 n->ndup.vname = makename();
9619 }
9620}
9621
9622/*
9623 * Returns true if the text contains nothing to expand (no dollar signs
9624 * or backquotes).
9625 */
9626static int
9627noexpand(char *text)
9628{
9629 char *p;
9630 char c;
9631
9632 p = text;
9633 while ((c = *p++) != '\0') {
9634 if (c == CTLQUOTEMARK)
9635 continue;
9636 if (c == CTLESC)
9637 p++;
9638 else if (SIT(c, BASESYNTAX) == CCTL)
9639 return 0;
9640 }
9641 return 1;
9642}
9643
9644static void
9645parsefname(void)
9646{
9647 union node *n = redirnode;
9648
9649 if (readtoken() != TWORD)
9650 raise_error_unexpected_syntax(-1);
9651 if (n->type == NHERE) {
9652 struct heredoc *here = heredoc;
9653 struct heredoc *p;
9654 int i;
9655
9656 if (quoteflag == 0)
9657 n->type = NXHERE;
9658 TRACE(("Here document %d\n", n->type));
9659 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9660 raise_error_syntax("Illegal eof marker for << redirection");
9661 rmescapes(wordtext);
9662 here->eofmark = wordtext;
9663 here->next = NULL;
9664 if (heredoclist == NULL)
9665 heredoclist = here;
9666 else {
9667 for (p = heredoclist; p->next; p = p->next);
9668 p->next = here;
9669 }
9670 } else if (n->type == NTOFD || n->type == NFROMFD) {
9671 fixredir(n, wordtext, 0);
9672 } else {
9673 n->nfile.fname = makename();
9674 }
9675}
Eric Andersencb57d552001-06-28 07:25:16 +00009676
Eric Andersenc470f442003-07-28 09:56:35 +00009677static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009678simplecmd(void)
9679{
9680 union node *args, **app;
9681 union node *n = NULL;
9682 union node *vars, **vpp;
9683 union node **rpp, *redir;
9684 int savecheckkwd;
9685
9686 args = NULL;
9687 app = &args;
9688 vars = NULL;
9689 vpp = &vars;
9690 redir = NULL;
9691 rpp = &redir;
9692
9693 savecheckkwd = CHKALIAS;
9694 for (;;) {
9695 checkkwd = savecheckkwd;
9696 switch (readtoken()) {
9697 case TWORD:
9698 n = stalloc(sizeof(struct narg));
9699 n->type = NARG;
9700 n->narg.text = wordtext;
9701 n->narg.backquote = backquotelist;
9702 if (savecheckkwd && isassignment(wordtext)) {
9703 *vpp = n;
9704 vpp = &n->narg.next;
9705 } else {
9706 *app = n;
9707 app = &n->narg.next;
9708 savecheckkwd = 0;
9709 }
9710 break;
9711 case TREDIR:
9712 *rpp = n = redirnode;
9713 rpp = &n->nfile.next;
9714 parsefname(); /* read name of redirection file */
9715 break;
9716 case TLP:
9717 if (args && app == &args->narg.next
9718 && !vars && !redir
9719 ) {
9720 struct builtincmd *bcmd;
9721 const char *name;
9722
9723 /* We have a function */
9724 if (readtoken() != TRP)
9725 raise_error_unexpected_syntax(TRP);
9726 name = n->narg.text;
9727 if (!goodname(name)
9728 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9729 ) {
9730 raise_error_syntax("Bad function name");
9731 }
9732 n->type = NDEFUN;
9733 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9734 n->narg.next = parse_command();
9735 return n;
9736 }
9737 /* fall through */
9738 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009739 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009740 goto out;
9741 }
9742 }
9743 out:
9744 *app = NULL;
9745 *vpp = NULL;
9746 *rpp = NULL;
9747 n = stalloc(sizeof(struct ncmd));
9748 n->type = NCMD;
9749 n->ncmd.args = args;
9750 n->ncmd.assign = vars;
9751 n->ncmd.redirect = redir;
9752 return n;
9753}
9754
9755static union node *
9756parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009757{
Eric Andersencb57d552001-06-28 07:25:16 +00009758 union node *n1, *n2;
9759 union node *ap, **app;
9760 union node *cp, **cpp;
9761 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009762 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009763 int t;
9764
9765 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009766 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009767
Eric Andersencb57d552001-06-28 07:25:16 +00009768 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009769 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009770 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009771 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009772 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009773 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009774 n1->type = NIF;
9775 n1->nif.test = list(0);
9776 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009777 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009778 n1->nif.ifpart = list(0);
9779 n2 = n1;
9780 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009781 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009782 n2 = n2->nif.elsepart;
9783 n2->type = NIF;
9784 n2->nif.test = list(0);
9785 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009786 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009787 n2->nif.ifpart = list(0);
9788 }
9789 if (lasttoken == TELSE)
9790 n2->nif.elsepart = list(0);
9791 else {
9792 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009793 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009794 }
Eric Andersenc470f442003-07-28 09:56:35 +00009795 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009796 break;
9797 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009798 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009799 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009800 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009801 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009802 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009803 got = readtoken();
9804 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009805 TRACE(("expecting DO got %s %s\n", tokname(got),
9806 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009807 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009808 }
9809 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009810 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009811 break;
9812 }
9813 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009814 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009815 raise_error_syntax("Bad for loop variable");
9816 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009817 n1->type = NFOR;
9818 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009819 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009820 if (readtoken() == TIN) {
9821 app = &ap;
9822 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009823 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009824 n2->type = NARG;
9825 n2->narg.text = wordtext;
9826 n2->narg.backquote = backquotelist;
9827 *app = n2;
9828 app = &n2->narg.next;
9829 }
9830 *app = NULL;
9831 n1->nfor.args = ap;
9832 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009833 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009834 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009835 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009836 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009837 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009838 n2->narg.backquote = NULL;
9839 n2->narg.next = NULL;
9840 n1->nfor.args = n2;
9841 /*
9842 * Newline or semicolon here is optional (but note
9843 * that the original Bourne shell only allowed NL).
9844 */
9845 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009846 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009847 }
Eric Andersenc470f442003-07-28 09:56:35 +00009848 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009849 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009850 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009851 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009852 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009853 break;
9854 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009855 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009856 n1->type = NCASE;
9857 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009858 raise_error_unexpected_syntax(TWORD);
9859 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009860 n2->type = NARG;
9861 n2->narg.text = wordtext;
9862 n2->narg.backquote = backquotelist;
9863 n2->narg.next = NULL;
9864 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009865 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009866 } while (readtoken() == TNL);
9867 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009868 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009869 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009870 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009871 checkkwd = CHKNL | CHKKWD;
9872 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009873 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009874 if (lasttoken == TLP)
9875 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009876 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009877 cp->type = NCLIST;
9878 app = &cp->nclist.pattern;
9879 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009880 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009881 ap->type = NARG;
9882 ap->narg.text = wordtext;
9883 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009884 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009885 break;
9886 app = &ap->narg.next;
9887 readtoken();
9888 }
9889 ap->narg.next = NULL;
9890 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009891 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009892 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009893
Eric Andersenc470f442003-07-28 09:56:35 +00009894 cpp = &cp->nclist.next;
9895
9896 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897 t = readtoken();
9898 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009899 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009900 raise_error_unexpected_syntax(TENDCASE);
9901 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009902 }
Eric Andersenc470f442003-07-28 09:56:35 +00009903 }
Eric Andersencb57d552001-06-28 07:25:16 +00009904 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009905 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009906 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009907 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009908 n1->type = NSUBSHELL;
9909 n1->nredir.n = list(0);
9910 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009911 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009912 break;
9913 case TBEGIN:
9914 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009915 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009916 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009917 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009918 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009919 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009920 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009921 }
9922
Eric Andersenc470f442003-07-28 09:56:35 +00009923 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009924 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009925
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009926 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009927 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009928 checkkwd = CHKKWD | CHKALIAS;
9929 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009930 while (readtoken() == TREDIR) {
9931 *rpp = n2 = redirnode;
9932 rpp = &n2->nfile.next;
9933 parsefname();
9934 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009935 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009936 *rpp = NULL;
9937 if (redir) {
9938 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009939 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009940 n2->type = NREDIR;
9941 n2->nredir.n = n1;
9942 n1 = n2;
9943 }
9944 n1->nredir.redirect = redir;
9945 }
Eric Andersencb57d552001-06-28 07:25:16 +00009946 return n1;
9947}
9948
Eric Andersencb57d552001-06-28 07:25:16 +00009949/*
9950 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9951 * is not NULL, read a here document. In the latter case, eofmark is the
9952 * word which marks the end of the document and striptabs is true if
9953 * leading tabs should be stripped from the document. The argument firstc
9954 * is the first character of the input token or document.
9955 *
9956 * Because C does not have internal subroutines, I have simulated them
9957 * using goto's to implement the subroutine linkage. The following macros
9958 * will run code that appears at the end of readtoken1.
9959 */
9960
Eric Andersen2870d962001-07-02 17:27:21 +00009961#define CHECKEND() {goto checkend; checkend_return:;}
9962#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9963#define PARSESUB() {goto parsesub; parsesub_return:;}
9964#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9965#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9966#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009967
9968static int
Eric Andersenc470f442003-07-28 09:56:35 +00009969readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009970{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009971 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009972 int c = firstc;
9973 char *out;
9974 int len;
9975 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009976 struct nodelist *bqlist;
9977 smallint quotef;
9978 smallint dblquote;
9979 smallint oldstyle;
9980 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009981#if ENABLE_ASH_EXPAND_PRMT
9982 smallint pssyntax; /* we are expanding a prompt string */
9983#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009984 int varnest; /* levels of variables expansion */
9985 int arinest; /* levels of arithmetic expansion */
9986 int parenlevel; /* levels of parens in arithmetic */
9987 int dqvarnest; /* levels of variables expansion within double quotes */
9988
Eric Andersencb57d552001-06-28 07:25:16 +00009989#if __GNUC__
9990 /* Avoid longjmp clobbering */
9991 (void) &out;
9992 (void) &quotef;
9993 (void) &dblquote;
9994 (void) &varnest;
9995 (void) &arinest;
9996 (void) &parenlevel;
9997 (void) &dqvarnest;
9998 (void) &oldstyle;
9999 (void) &prevsyntax;
10000 (void) &syntax;
10001#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010002 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010003 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010004 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010005 oldstyle = 0;
10006 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010007#if ENABLE_ASH_EXPAND_PRMT
10008 pssyntax = (syntax == PSSYNTAX);
10009 if (pssyntax)
10010 syntax = DQSYNTAX;
10011#endif
10012 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010013 varnest = 0;
10014 arinest = 0;
10015 parenlevel = 0;
10016 dqvarnest = 0;
10017
10018 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010019 loop: { /* for each line, until end of word */
10020 CHECKEND(); /* set c to PEOF if at end of here document */
10021 for (;;) { /* until end of line or end of word */
10022 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010023 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010024 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010025 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010026 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010027 USTPUTC(c, out);
10028 plinno++;
10029 if (doprompt)
10030 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010031 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010032 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010033 case CWORD:
10034 USTPUTC(c, out);
10035 break;
10036 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010037 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010038 USTPUTC(CTLESC, out);
10039 USTPUTC(c, out);
10040 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010041 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010042 c = pgetc2();
10043 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010044 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010045 USTPUTC('\\', out);
10046 pungetc();
10047 } else if (c == '\n') {
10048 if (doprompt)
10049 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010050 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010051#if ENABLE_ASH_EXPAND_PRMT
10052 if (c == '$' && pssyntax) {
10053 USTPUTC(CTLESC, out);
10054 USTPUTC('\\', out);
10055 }
10056#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010057 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010058 c != '\\' && c != '`' &&
10059 c != '$' && (
10060 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010061 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010062 ) {
10063 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010064 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010065 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010066 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010067 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010068 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010069 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010070 }
10071 break;
10072 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010073 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010074 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010075 if (eofmark == NULL) {
10076 USTPUTC(CTLQUOTEMARK, out);
10077 }
Eric Andersencb57d552001-06-28 07:25:16 +000010078 break;
10079 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010080 syntax = DQSYNTAX;
10081 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010082 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010083 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010084 if (eofmark != NULL && arinest == 0
10085 && varnest == 0
10086 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010087 USTPUTC(c, out);
10088 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010089 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010090 syntax = BASESYNTAX;
10091 dblquote = 0;
10092 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010093 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010094 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010095 }
10096 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010097 case CVAR: /* '$' */
10098 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010099 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010100 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010101 if (varnest > 0) {
10102 varnest--;
10103 if (dqvarnest > 0) {
10104 dqvarnest--;
10105 }
10106 USTPUTC(CTLENDVAR, out);
10107 } else {
10108 USTPUTC(c, out);
10109 }
10110 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010111#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010112 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010113 parenlevel++;
10114 USTPUTC(c, out);
10115 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010116 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010117 if (parenlevel > 0) {
10118 USTPUTC(c, out);
10119 --parenlevel;
10120 } else {
10121 if (pgetc() == ')') {
10122 if (--arinest == 0) {
10123 USTPUTC(CTLENDARI, out);
10124 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010125 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010126 } else
10127 USTPUTC(')', out);
10128 } else {
10129 /*
10130 * unbalanced parens
10131 * (don't 2nd guess - no error)
10132 */
10133 pungetc();
10134 USTPUTC(')', out);
10135 }
10136 }
10137 break;
10138#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010139 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010140 PARSEBACKQOLD();
10141 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010142 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010143 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010144 case CIGN:
10145 break;
10146 default:
10147 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010148 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010149#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010150 if (c != PEOA)
10151#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010152 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010153
Eric Andersencb57d552001-06-28 07:25:16 +000010154 }
10155 c = pgetc_macro();
10156 }
10157 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010158 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010159#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010160 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010161 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010162#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010163 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010164 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010165 if (varnest != 0) {
10166 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010167 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010168 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010169 }
10170 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010171 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010172 out = stackblock();
10173 if (eofmark == NULL) {
10174 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010175 && quotef == 0
10176 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010177 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010178 PARSEREDIR();
10179 return lasttoken = TREDIR;
10180 } else {
10181 pungetc();
10182 }
10183 }
10184 quoteflag = quotef;
10185 backquotelist = bqlist;
10186 grabstackblock(len);
10187 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010188 lasttoken = TWORD;
10189 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010190/* end of readtoken routine */
10191
Eric Andersencb57d552001-06-28 07:25:16 +000010192/*
10193 * Check to see whether we are at the end of the here document. When this
10194 * is called, c is set to the first character of the next input line. If
10195 * we are at the end of the here document, this routine sets the c to PEOF.
10196 */
Eric Andersenc470f442003-07-28 09:56:35 +000010197checkend: {
10198 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010199#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010200 if (c == PEOA) {
10201 c = pgetc2();
10202 }
10203#endif
10204 if (striptabs) {
10205 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010206 c = pgetc2();
10207 }
Eric Andersenc470f442003-07-28 09:56:35 +000010208 }
10209 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010210 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010211 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010212
Eric Andersenc470f442003-07-28 09:56:35 +000010213 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010214 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010215 if (*p == '\n' && *q == '\0') {
10216 c = PEOF;
10217 plinno++;
10218 needprompt = doprompt;
10219 } else {
10220 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010221 }
10222 }
10223 }
10224 }
Eric Andersenc470f442003-07-28 09:56:35 +000010225 goto checkend_return;
10226}
Eric Andersencb57d552001-06-28 07:25:16 +000010227
Eric Andersencb57d552001-06-28 07:25:16 +000010228/*
10229 * Parse a redirection operator. The variable "out" points to a string
10230 * specifying the fd to be redirected. The variable "c" contains the
10231 * first character of the redirection operator.
10232 */
Eric Andersenc470f442003-07-28 09:56:35 +000010233parseredir: {
10234 char fd = *out;
10235 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010236
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010237 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010238 if (c == '>') {
10239 np->nfile.fd = 1;
10240 c = pgetc();
10241 if (c == '>')
10242 np->type = NAPPEND;
10243 else if (c == '|')
10244 np->type = NCLOBBER;
10245 else if (c == '&')
10246 np->type = NTOFD;
10247 else {
10248 np->type = NTO;
10249 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010250 }
Eric Andersenc470f442003-07-28 09:56:35 +000010251 } else { /* c == '<' */
10252 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010253 c = pgetc();
10254 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010255 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010256 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010257 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010258 np->nfile.fd = 0;
10259 }
10260 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010261 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010262 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010263 c = pgetc();
10264 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010265 heredoc->striptabs = 1;
10266 } else {
10267 heredoc->striptabs = 0;
10268 pungetc();
10269 }
10270 break;
10271
10272 case '&':
10273 np->type = NFROMFD;
10274 break;
10275
10276 case '>':
10277 np->type = NFROMTO;
10278 break;
10279
10280 default:
10281 np->type = NFROM;
10282 pungetc();
10283 break;
10284 }
Eric Andersencb57d552001-06-28 07:25:16 +000010285 }
Eric Andersenc470f442003-07-28 09:56:35 +000010286 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010287 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010288 redirnode = np;
10289 goto parseredir_return;
10290}
Eric Andersencb57d552001-06-28 07:25:16 +000010291
Eric Andersencb57d552001-06-28 07:25:16 +000010292/*
10293 * Parse a substitution. At this point, we have read the dollar sign
10294 * and nothing else.
10295 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010296
10297/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10298 * (assuming ascii char codes, as the original implementation did) */
10299#define is_special(c) \
10300 ((((unsigned int)c) - 33 < 32) \
10301 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010302parsesub: {
10303 int subtype;
10304 int typeloc;
10305 int flags;
10306 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010307 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010308
Eric Andersenc470f442003-07-28 09:56:35 +000010309 c = pgetc();
10310 if (
10311 c <= PEOA_OR_PEOF ||
10312 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10313 ) {
10314 USTPUTC('$', out);
10315 pungetc();
10316 } else if (c == '(') { /* $(command) or $((arith)) */
10317 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010318#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010319 PARSEARITH();
10320#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010321 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010322#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010323 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010324 pungetc();
10325 PARSEBACKQNEW();
10326 }
10327 } else {
10328 USTPUTC(CTLVAR, out);
10329 typeloc = out - (char *)stackblock();
10330 USTPUTC(VSNORMAL, out);
10331 subtype = VSNORMAL;
10332 if (c == '{') {
10333 c = pgetc();
10334 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010335 c = pgetc();
10336 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010337 c = '#';
10338 else
10339 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010340 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010341 subtype = 0;
10342 }
10343 if (c > PEOA_OR_PEOF && is_name(c)) {
10344 do {
10345 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010346 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010347 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010348 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010349 do {
10350 STPUTC(c, out);
10351 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010352 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010353 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010354 USTPUTC(c, out);
10355 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010356 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010357 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010358
Eric Andersenc470f442003-07-28 09:56:35 +000010359 STPUTC('=', out);
10360 flags = 0;
10361 if (subtype == 0) {
10362 switch (c) {
10363 case ':':
10364 flags = VSNUL;
10365 c = pgetc();
10366 /*FALLTHROUGH*/
10367 default:
10368 p = strchr(types, c);
10369 if (p == NULL)
10370 goto badsub;
10371 subtype = p - types + VSNORMAL;
10372 break;
10373 case '%':
10374 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010375 {
10376 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010377 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010378 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010379 c = pgetc();
10380 if (c == cc)
10381 subtype++;
10382 else
10383 pungetc();
10384 break;
10385 }
10386 }
Eric Andersenc470f442003-07-28 09:56:35 +000010387 } else {
10388 pungetc();
10389 }
10390 if (dblquote || arinest)
10391 flags |= VSQUOTE;
10392 *((char *)stackblock() + typeloc) = subtype | flags;
10393 if (subtype != VSNORMAL) {
10394 varnest++;
10395 if (dblquote || arinest) {
10396 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010397 }
10398 }
10399 }
Eric Andersenc470f442003-07-28 09:56:35 +000010400 goto parsesub_return;
10401}
Eric Andersencb57d552001-06-28 07:25:16 +000010402
Eric Andersencb57d552001-06-28 07:25:16 +000010403/*
10404 * Called to parse command substitutions. Newstyle is set if the command
10405 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10406 * list of commands (passed by reference), and savelen is the number of
10407 * characters on the top of the stack which must be preserved.
10408 */
Eric Andersenc470f442003-07-28 09:56:35 +000010409parsebackq: {
10410 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010411 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010412 union node *n;
10413 char *volatile str;
10414 struct jmploc jmploc;
10415 struct jmploc *volatile savehandler;
10416 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010417 smallint saveprompt = 0;
10418
Eric Andersencb57d552001-06-28 07:25:16 +000010419#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010420 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010421#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010422 savepbq = parsebackquote;
10423 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010424 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010425 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010426 exception_handler = savehandler;
10427 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010428 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010429 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010430 str = NULL;
10431 savelen = out - (char *)stackblock();
10432 if (savelen > 0) {
10433 str = ckmalloc(savelen);
10434 memcpy(str, stackblock(), savelen);
10435 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010436 savehandler = exception_handler;
10437 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010438 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010439 if (oldstyle) {
10440 /* We must read until the closing backquote, giving special
10441 treatment to some slashes, and then push the string and
10442 reread it as input, interpreting it normally. */
10443 char *pout;
10444 int pc;
10445 size_t psavelen;
10446 char *pstr;
10447
10448
10449 STARTSTACKSTR(pout);
10450 for (;;) {
10451 if (needprompt) {
10452 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010453 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010454 pc = pgetc();
10455 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010456 case '`':
10457 goto done;
10458
10459 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010460 pc = pgetc();
10461 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010462 plinno++;
10463 if (doprompt)
10464 setprompt(2);
10465 /*
10466 * If eating a newline, avoid putting
10467 * the newline into the new character
10468 * stream (via the STPUTC after the
10469 * switch).
10470 */
10471 continue;
10472 }
10473 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010474 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010475 STPUTC('\\', pout);
10476 if (pc > PEOA_OR_PEOF) {
10477 break;
10478 }
10479 /* fall through */
10480
10481 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010482#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010483 case PEOA:
10484#endif
10485 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010486 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010487
10488 case '\n':
10489 plinno++;
10490 needprompt = doprompt;
10491 break;
10492
10493 default:
10494 break;
10495 }
10496 STPUTC(pc, pout);
10497 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010498 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010499 STPUTC('\0', pout);
10500 psavelen = pout - (char *)stackblock();
10501 if (psavelen > 0) {
10502 pstr = grabstackstr(pout);
10503 setinputstring(pstr);
10504 }
10505 }
10506 nlpp = &bqlist;
10507 while (*nlpp)
10508 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010509 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010510 (*nlpp)->next = NULL;
10511 parsebackquote = oldstyle;
10512
10513 if (oldstyle) {
10514 saveprompt = doprompt;
10515 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 }
10517
Eric Andersenc470f442003-07-28 09:56:35 +000010518 n = list(2);
10519
10520 if (oldstyle)
10521 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010522 else if (readtoken() != TRP)
10523 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010524
10525 (*nlpp)->n = n;
10526 if (oldstyle) {
10527 /*
10528 * Start reading from old file again, ignoring any pushed back
10529 * tokens left from the backquote parsing
10530 */
10531 popfile();
10532 tokpushback = 0;
10533 }
10534 while (stackblocksize() <= savelen)
10535 growstackblock();
10536 STARTSTACKSTR(out);
10537 if (str) {
10538 memcpy(out, str, savelen);
10539 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010540 INT_OFF;
10541 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010542 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010543 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010544 }
10545 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010546 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010547 if (arinest || dblquote)
10548 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10549 else
10550 USTPUTC(CTLBACKQ, out);
10551 if (oldstyle)
10552 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010553 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010554}
10555
Denis Vlasenko131ae172007-02-18 13:00:19 +000010556#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010557/*
10558 * Parse an arithmetic expansion (indicate start of one and set state)
10559 */
Eric Andersenc470f442003-07-28 09:56:35 +000010560parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010561 if (++arinest == 1) {
10562 prevsyntax = syntax;
10563 syntax = ARISYNTAX;
10564 USTPUTC(CTLARI, out);
10565 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010566 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010567 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010568 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010569 } else {
10570 /*
10571 * we collapse embedded arithmetic expansion to
10572 * parenthesis, which should be equivalent
10573 */
10574 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010575 }
Eric Andersenc470f442003-07-28 09:56:35 +000010576 goto parsearith_return;
10577}
10578#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010579
Eric Andersenc470f442003-07-28 09:56:35 +000010580} /* end of readtoken */
10581
Eric Andersencb57d552001-06-28 07:25:16 +000010582/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010583 * Read the next input token.
10584 * If the token is a word, we set backquotelist to the list of cmds in
10585 * backquotes. We set quoteflag to true if any part of the word was
10586 * quoted.
10587 * If the token is TREDIR, then we set redirnode to a structure containing
10588 * the redirection.
10589 * In all cases, the variable startlinno is set to the number of the line
10590 * on which the token starts.
10591 *
10592 * [Change comment: here documents and internal procedures]
10593 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10594 * word parsing code into a separate routine. In this case, readtoken
10595 * doesn't need to have any internal procedures, but parseword does.
10596 * We could also make parseoperator in essence the main routine, and
10597 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010598 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010599#define NEW_xxreadtoken
10600#ifdef NEW_xxreadtoken
10601/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010602static const char xxreadtoken_chars[7] ALIGN1 = {
10603 '\n', '(', ')', '&', '|', ';', 0
10604};
Eric Andersencb57d552001-06-28 07:25:16 +000010605
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010606static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010607 TNL, TLP, TRP, /* only single occurrence allowed */
10608 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10609 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010610 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010611};
10612
10613#define xxreadtoken_doubles \
10614 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10615#define xxreadtoken_singles \
10616 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10617
10618static int
10619xxreadtoken(void)
10620{
10621 int c;
10622
10623 if (tokpushback) {
10624 tokpushback = 0;
10625 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010626 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010627 if (needprompt) {
10628 setprompt(2);
10629 }
10630 startlinno = plinno;
10631 for (;;) { /* until token or start of word found */
10632 c = pgetc_macro();
10633
10634 if ((c != ' ') && (c != '\t')
10635#if ENABLE_ASH_ALIAS
10636 && (c != PEOA)
10637#endif
10638 ) {
10639 if (c == '#') {
10640 while ((c = pgetc()) != '\n' && c != PEOF);
10641 pungetc();
10642 } else if (c == '\\') {
10643 if (pgetc() != '\n') {
10644 pungetc();
10645 goto READTOKEN1;
10646 }
10647 startlinno = ++plinno;
10648 if (doprompt)
10649 setprompt(2);
10650 } else {
10651 const char *p
10652 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10653
10654 if (c != PEOF) {
10655 if (c == '\n') {
10656 plinno++;
10657 needprompt = doprompt;
10658 }
10659
10660 p = strchr(xxreadtoken_chars, c);
10661 if (p == NULL) {
10662 READTOKEN1:
10663 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10664 }
10665
10666 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10667 if (pgetc() == *p) { /* double occurrence? */
10668 p += xxreadtoken_doubles + 1;
10669 } else {
10670 pungetc();
10671 }
10672 }
10673 }
10674 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10675 }
10676 }
10677 } /* for */
10678}
10679#else
10680#define RETURN(token) return lasttoken = token
10681static int
10682xxreadtoken(void)
10683{
10684 int c;
10685
10686 if (tokpushback) {
10687 tokpushback = 0;
10688 return lasttoken;
10689 }
10690 if (needprompt) {
10691 setprompt(2);
10692 }
10693 startlinno = plinno;
10694 for (;;) { /* until token or start of word found */
10695 c = pgetc_macro();
10696 switch (c) {
10697 case ' ': case '\t':
10698#if ENABLE_ASH_ALIAS
10699 case PEOA:
10700#endif
10701 continue;
10702 case '#':
10703 while ((c = pgetc()) != '\n' && c != PEOF);
10704 pungetc();
10705 continue;
10706 case '\\':
10707 if (pgetc() == '\n') {
10708 startlinno = ++plinno;
10709 if (doprompt)
10710 setprompt(2);
10711 continue;
10712 }
10713 pungetc();
10714 goto breakloop;
10715 case '\n':
10716 plinno++;
10717 needprompt = doprompt;
10718 RETURN(TNL);
10719 case PEOF:
10720 RETURN(TEOF);
10721 case '&':
10722 if (pgetc() == '&')
10723 RETURN(TAND);
10724 pungetc();
10725 RETURN(TBACKGND);
10726 case '|':
10727 if (pgetc() == '|')
10728 RETURN(TOR);
10729 pungetc();
10730 RETURN(TPIPE);
10731 case ';':
10732 if (pgetc() == ';')
10733 RETURN(TENDCASE);
10734 pungetc();
10735 RETURN(TSEMI);
10736 case '(':
10737 RETURN(TLP);
10738 case ')':
10739 RETURN(TRP);
10740 default:
10741 goto breakloop;
10742 }
10743 }
10744 breakloop:
10745 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10746#undef RETURN
10747}
10748#endif /* NEW_xxreadtoken */
10749
10750static int
10751readtoken(void)
10752{
10753 int t;
10754#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010755 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010756#endif
10757
10758#if ENABLE_ASH_ALIAS
10759 top:
10760#endif
10761
10762 t = xxreadtoken();
10763
10764 /*
10765 * eat newlines
10766 */
10767 if (checkkwd & CHKNL) {
10768 while (t == TNL) {
10769 parseheredoc();
10770 t = xxreadtoken();
10771 }
10772 }
10773
10774 if (t != TWORD || quoteflag) {
10775 goto out;
10776 }
10777
10778 /*
10779 * check for keywords
10780 */
10781 if (checkkwd & CHKKWD) {
10782 const char *const *pp;
10783
10784 pp = findkwd(wordtext);
10785 if (pp) {
10786 lasttoken = t = pp - tokname_array;
10787 TRACE(("keyword %s recognized\n", tokname(t)));
10788 goto out;
10789 }
10790 }
10791
10792 if (checkkwd & CHKALIAS) {
10793#if ENABLE_ASH_ALIAS
10794 struct alias *ap;
10795 ap = lookupalias(wordtext, 1);
10796 if (ap != NULL) {
10797 if (*ap->val) {
10798 pushstring(ap->val, ap);
10799 }
10800 goto top;
10801 }
10802#endif
10803 }
10804 out:
10805 checkkwd = 0;
10806#if DEBUG
10807 if (!alreadyseen)
10808 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10809 else
10810 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10811#endif
10812 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010813}
10814
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010815static char
10816peektoken(void)
10817{
10818 int t;
10819
10820 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010821 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010822 return tokname_array[t][0];
10823}
Eric Andersencb57d552001-06-28 07:25:16 +000010824
10825/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10827 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010828 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010829static union node *
10830parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010831{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010832 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010833
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010834 tokpushback = 0;
10835 doprompt = interact;
10836 if (doprompt)
10837 setprompt(doprompt);
10838 needprompt = 0;
10839 t = readtoken();
10840 if (t == TEOF)
10841 return NEOF;
10842 if (t == TNL)
10843 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010844 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010845 return list(1);
10846}
10847
10848/*
10849 * Input any here documents.
10850 */
10851static void
10852parseheredoc(void)
10853{
10854 struct heredoc *here;
10855 union node *n;
10856
10857 here = heredoclist;
10858 heredoclist = 0;
10859
10860 while (here) {
10861 if (needprompt) {
10862 setprompt(2);
10863 }
10864 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10865 here->eofmark, here->striptabs);
10866 n = stalloc(sizeof(struct narg));
10867 n->narg.type = NARG;
10868 n->narg.next = NULL;
10869 n->narg.text = wordtext;
10870 n->narg.backquote = backquotelist;
10871 here->here->nhere.doc = n;
10872 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010873 }
Eric Andersencb57d552001-06-28 07:25:16 +000010874}
10875
10876
10877/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010878 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010879 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010880#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010881static const char *
10882expandstr(const char *ps)
10883{
10884 union node n;
10885
10886 /* XXX Fix (char *) cast. */
10887 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010888 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010889 popfile();
10890
10891 n.narg.type = NARG;
10892 n.narg.next = NULL;
10893 n.narg.text = wordtext;
10894 n.narg.backquote = backquotelist;
10895
10896 expandarg(&n, NULL, 0);
10897 return stackblock();
10898}
10899#endif
10900
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010901/*
10902 * Execute a command or commands contained in a string.
10903 */
10904static int
10905evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010906{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010907 union node *n;
10908 struct stackmark smark;
10909 int skip;
10910
10911 setinputstring(s);
10912 setstackmark(&smark);
10913
10914 skip = 0;
10915 while ((n = parsecmd(0)) != NEOF) {
10916 evaltree(n, 0);
10917 popstackmark(&smark);
10918 skip = evalskip;
10919 if (skip)
10920 break;
10921 }
10922 popfile();
10923
10924 skip &= mask;
10925 evalskip = skip;
10926 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010927}
10928
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010929/*
10930 * The eval command.
10931 */
10932static int
10933evalcmd(int argc, char **argv)
10934{
10935 char *p;
10936 char *concat;
10937 char **ap;
10938
10939 if (argc > 1) {
10940 p = argv[1];
10941 if (argc > 2) {
10942 STARTSTACKSTR(concat);
10943 ap = argv + 2;
10944 for (;;) {
10945 concat = stack_putstr(p, concat);
10946 p = *ap++;
10947 if (p == NULL)
10948 break;
10949 STPUTC(' ', concat);
10950 }
10951 STPUTC('\0', concat);
10952 p = grabstackstr(concat);
10953 }
10954 evalstring(p, ~SKIPEVAL);
10955
10956 }
10957 return exitstatus;
10958}
10959
10960/*
10961 * Read and execute commands. "Top" is nonzero for the top level command
10962 * loop; it turns on prompting if the shell is interactive.
10963 */
10964static int
10965cmdloop(int top)
10966{
10967 union node *n;
10968 struct stackmark smark;
10969 int inter;
10970 int numeof = 0;
10971
10972 TRACE(("cmdloop(%d) called\n", top));
10973 for (;;) {
10974 int skip;
10975
10976 setstackmark(&smark);
10977#if JOBS
10978 if (jobctl)
10979 showjobs(stderr, SHOW_CHANGED);
10980#endif
10981 inter = 0;
10982 if (iflag && top) {
10983 inter++;
10984#if ENABLE_ASH_MAIL
10985 chkmail();
10986#endif
10987 }
10988 n = parsecmd(inter);
10989 /* showtree(n); DEBUG */
10990 if (n == NEOF) {
10991 if (!top || numeof >= 50)
10992 break;
10993 if (!stoppedjobs()) {
10994 if (!Iflag)
10995 break;
10996 out2str("\nUse \"exit\" to leave shell.\n");
10997 }
10998 numeof++;
10999 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011000 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11001 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011002 numeof = 0;
11003 evaltree(n, 0);
11004 }
11005 popstackmark(&smark);
11006 skip = evalskip;
11007
11008 if (skip) {
11009 evalskip = 0;
11010 return skip & SKIPEVAL;
11011 }
11012 }
11013 return 0;
11014}
11015
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011016/*
11017 * Take commands from a file. To be compatible we should do a path
11018 * search for the file, which is necessary to find sub-commands.
11019 */
11020static char *
11021find_dot_file(char *name)
11022{
11023 char *fullname;
11024 const char *path = pathval();
11025 struct stat statb;
11026
11027 /* don't try this for absolute or relative paths */
11028 if (strchr(name, '/'))
11029 return name;
11030
11031 while ((fullname = padvance(&path, name)) != NULL) {
11032 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11033 /*
11034 * Don't bother freeing here, since it will
11035 * be freed by the caller.
11036 */
11037 return fullname;
11038 }
11039 stunalloc(fullname);
11040 }
11041
11042 /* not found in the PATH */
11043 ash_msg_and_raise_error("%s: not found", name);
11044 /* NOTREACHED */
11045}
11046
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011047static int
11048dotcmd(int argc, char **argv)
11049{
11050 struct strlist *sp;
11051 volatile struct shparam saveparam;
11052 int status = 0;
11053
11054 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011055 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011056
11057 if (argc >= 2) { /* That's what SVR2 does */
11058 char *fullname;
11059
11060 fullname = find_dot_file(argv[1]);
11061
11062 if (argc > 2) {
11063 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011064 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011065 shellparam.nparam = argc - 2;
11066 shellparam.p = argv + 2;
11067 };
11068
11069 setinputfile(fullname, INPUT_PUSH_FILE);
11070 commandname = fullname;
11071 cmdloop(0);
11072 popfile();
11073
11074 if (argc > 2) {
11075 freeparam(&shellparam);
11076 shellparam = saveparam;
11077 };
11078 status = exitstatus;
11079 }
11080 return status;
11081}
11082
11083static int
11084exitcmd(int argc, char **argv)
11085{
11086 if (stoppedjobs())
11087 return 0;
11088 if (argc > 1)
11089 exitstatus = number(argv[1]);
11090 raise_exception(EXEXIT);
11091 /* NOTREACHED */
11092}
11093
11094#if ENABLE_ASH_BUILTIN_ECHO
11095static int
11096echocmd(int argc, char **argv)
11097{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011098 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011099}
11100#endif
11101
11102#if ENABLE_ASH_BUILTIN_TEST
11103static int
11104testcmd(int argc, char **argv)
11105{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011106 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011107}
11108#endif
11109
11110/*
11111 * Read a file containing shell functions.
11112 */
11113static void
11114readcmdfile(char *name)
11115{
11116 setinputfile(name, INPUT_PUSH_FILE);
11117 cmdloop(0);
11118 popfile();
11119}
11120
11121
Denis Vlasenkocc571512007-02-23 21:10:35 +000011122/* ============ find_command inplementation */
11123
11124/*
11125 * Resolve a command name. If you change this routine, you may have to
11126 * change the shellexec routine as well.
11127 */
11128static void
11129find_command(char *name, struct cmdentry *entry, int act, const char *path)
11130{
11131 struct tblentry *cmdp;
11132 int idx;
11133 int prev;
11134 char *fullname;
11135 struct stat statb;
11136 int e;
11137 int updatetbl;
11138 struct builtincmd *bcmd;
11139
11140 /* If name contains a slash, don't use PATH or hash table */
11141 if (strchr(name, '/') != NULL) {
11142 entry->u.index = -1;
11143 if (act & DO_ABS) {
11144 while (stat(name, &statb) < 0) {
11145#ifdef SYSV
11146 if (errno == EINTR)
11147 continue;
11148#endif
11149 entry->cmdtype = CMDUNKNOWN;
11150 return;
11151 }
11152 }
11153 entry->cmdtype = CMDNORMAL;
11154 return;
11155 }
11156
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011157/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011158
11159 updatetbl = (path == pathval());
11160 if (!updatetbl) {
11161 act |= DO_ALTPATH;
11162 if (strstr(path, "%builtin") != NULL)
11163 act |= DO_ALTBLTIN;
11164 }
11165
11166 /* If name is in the table, check answer will be ok */
11167 cmdp = cmdlookup(name, 0);
11168 if (cmdp != NULL) {
11169 int bit;
11170
11171 switch (cmdp->cmdtype) {
11172 default:
11173#if DEBUG
11174 abort();
11175#endif
11176 case CMDNORMAL:
11177 bit = DO_ALTPATH;
11178 break;
11179 case CMDFUNCTION:
11180 bit = DO_NOFUNC;
11181 break;
11182 case CMDBUILTIN:
11183 bit = DO_ALTBLTIN;
11184 break;
11185 }
11186 if (act & bit) {
11187 updatetbl = 0;
11188 cmdp = NULL;
11189 } else if (cmdp->rehash == 0)
11190 /* if not invalidated by cd, we're done */
11191 goto success;
11192 }
11193
11194 /* If %builtin not in path, check for builtin next */
11195 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011196 if (bcmd) {
11197 if (IS_BUILTIN_REGULAR(bcmd))
11198 goto builtin_success;
11199 if (act & DO_ALTPATH) {
11200 if (!(act & DO_ALTBLTIN))
11201 goto builtin_success;
11202 } else if (builtinloc <= 0) {
11203 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011204 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011205 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011206
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011207#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011208 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011209 entry->cmdtype = CMDNORMAL;
11210 entry->u.index = -1;
11211 return;
11212 }
11213#endif
11214
Denis Vlasenkocc571512007-02-23 21:10:35 +000011215 /* We have to search path. */
11216 prev = -1; /* where to start */
11217 if (cmdp && cmdp->rehash) { /* doing a rehash */
11218 if (cmdp->cmdtype == CMDBUILTIN)
11219 prev = builtinloc;
11220 else
11221 prev = cmdp->param.index;
11222 }
11223
11224 e = ENOENT;
11225 idx = -1;
11226 loop:
11227 while ((fullname = padvance(&path, name)) != NULL) {
11228 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011229 /* NB: code below will still use fullname
11230 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011231 idx++;
11232 if (pathopt) {
11233 if (prefix(pathopt, "builtin")) {
11234 if (bcmd)
11235 goto builtin_success;
11236 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011237 } else if (!(act & DO_NOFUNC)
11238 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011239 /* handled below */
11240 } else {
11241 /* ignore unimplemented options */
11242 continue;
11243 }
11244 }
11245 /* if rehash, don't redo absolute path names */
11246 if (fullname[0] == '/' && idx <= prev) {
11247 if (idx < prev)
11248 continue;
11249 TRACE(("searchexec \"%s\": no change\n", name));
11250 goto success;
11251 }
11252 while (stat(fullname, &statb) < 0) {
11253#ifdef SYSV
11254 if (errno == EINTR)
11255 continue;
11256#endif
11257 if (errno != ENOENT && errno != ENOTDIR)
11258 e = errno;
11259 goto loop;
11260 }
11261 e = EACCES; /* if we fail, this will be the error */
11262 if (!S_ISREG(statb.st_mode))
11263 continue;
11264 if (pathopt) { /* this is a %func directory */
11265 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011266 /* NB: stalloc will return space pointed by fullname
11267 * (because we don't have any intervening allocations
11268 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011269 readcmdfile(fullname);
11270 cmdp = cmdlookup(name, 0);
11271 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11272 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11273 stunalloc(fullname);
11274 goto success;
11275 }
11276 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11277 if (!updatetbl) {
11278 entry->cmdtype = CMDNORMAL;
11279 entry->u.index = idx;
11280 return;
11281 }
11282 INT_OFF;
11283 cmdp = cmdlookup(name, 1);
11284 cmdp->cmdtype = CMDNORMAL;
11285 cmdp->param.index = idx;
11286 INT_ON;
11287 goto success;
11288 }
11289
11290 /* We failed. If there was an entry for this command, delete it */
11291 if (cmdp && updatetbl)
11292 delete_cmd_entry();
11293 if (act & DO_ERR)
11294 ash_msg("%s: %s", name, errmsg(e, "not found"));
11295 entry->cmdtype = CMDUNKNOWN;
11296 return;
11297
11298 builtin_success:
11299 if (!updatetbl) {
11300 entry->cmdtype = CMDBUILTIN;
11301 entry->u.cmd = bcmd;
11302 return;
11303 }
11304 INT_OFF;
11305 cmdp = cmdlookup(name, 1);
11306 cmdp->cmdtype = CMDBUILTIN;
11307 cmdp->param.cmd = bcmd;
11308 INT_ON;
11309 success:
11310 cmdp->rehash = 0;
11311 entry->cmdtype = cmdp->cmdtype;
11312 entry->u = cmdp->param;
11313}
11314
11315
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011316/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011317
Eric Andersencb57d552001-06-28 07:25:16 +000011318/*
Eric Andersencb57d552001-06-28 07:25:16 +000011319 * The trap builtin.
11320 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011321static int
Eric Andersenc470f442003-07-28 09:56:35 +000011322trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011323{
11324 char *action;
11325 char **ap;
11326 int signo;
11327
Eric Andersenc470f442003-07-28 09:56:35 +000011328 nextopt(nullstr);
11329 ap = argptr;
11330 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011331 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011332 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011333 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011334
Rob Landleyc9c1a412006-07-12 19:17:55 +000011335 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011336 out1fmt("trap -- %s %s\n",
11337 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011338 }
11339 }
11340 return 0;
11341 }
Eric Andersenc470f442003-07-28 09:56:35 +000011342 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011343 action = NULL;
11344 else
11345 action = *ap++;
11346 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011347 signo = get_signum(*ap);
11348 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011349 ash_msg_and_raise_error("%s: bad trap", *ap);
11350 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011351 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011352 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011353 action = NULL;
11354 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011355 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011356 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011357 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011358 trap[signo] = action;
11359 if (signo != 0)
11360 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011361 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011362 ap++;
11363 }
11364 return 0;
11365}
11366
Eric Andersenc470f442003-07-28 09:56:35 +000011367
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011368/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011369
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011370#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011371/*
11372 * Lists available builtins
11373 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011374static int
11375helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011376{
11377 int col, i;
11378
11379 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011380 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011381 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011382 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011383 if (col > 60) {
11384 out1fmt("\n");
11385 col = 0;
11386 }
11387 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011388#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011389 {
11390 const char *a = applet_names;
11391 while (*a) {
11392 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11393 if (col > 60) {
11394 out1fmt("\n");
11395 col = 0;
11396 }
11397 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011398 }
11399 }
11400#endif
11401 out1fmt("\n\n");
11402 return EXIT_SUCCESS;
11403}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011404#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011405
Eric Andersencb57d552001-06-28 07:25:16 +000011406/*
Eric Andersencb57d552001-06-28 07:25:16 +000011407 * The export and readonly commands.
11408 */
Eric Andersenc470f442003-07-28 09:56:35 +000011409static int
11410exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011411{
11412 struct var *vp;
11413 char *name;
11414 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011415 char **aptr;
11416 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011417
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011418 if (nextopt("p") != 'p') {
11419 aptr = argptr;
11420 name = *aptr;
11421 if (name) {
11422 do {
11423 p = strchr(name, '=');
11424 if (p != NULL) {
11425 p++;
11426 } else {
11427 vp = *findvar(hashvar(name), name);
11428 if (vp) {
11429 vp->flags |= flag;
11430 continue;
11431 }
Eric Andersencb57d552001-06-28 07:25:16 +000011432 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011433 setvar(name, p, flag);
11434 } while ((name = *++aptr) != NULL);
11435 return 0;
11436 }
Eric Andersencb57d552001-06-28 07:25:16 +000011437 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011438 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011439 return 0;
11440}
11441
Eric Andersencb57d552001-06-28 07:25:16 +000011442/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011443 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011444 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011445static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011446unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011447{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011448 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011449
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011450 cmdp = cmdlookup(name, 0);
11451 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11452 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011453}
11454
Eric Andersencb57d552001-06-28 07:25:16 +000011455/*
Eric Andersencb57d552001-06-28 07:25:16 +000011456 * The unset builtin command. We unset the function before we unset the
11457 * variable to allow a function to be unset when there is a readonly variable
11458 * with the same name.
11459 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011460static int
Eric Andersenc470f442003-07-28 09:56:35 +000011461unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011462{
11463 char **ap;
11464 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011465 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011466 int ret = 0;
11467
11468 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011469 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011470 }
Eric Andersencb57d552001-06-28 07:25:16 +000011471
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011472 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011473 if (flag != 'f') {
11474 i = unsetvar(*ap);
11475 ret |= i;
11476 if (!(i & 2))
11477 continue;
11478 }
11479 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011480 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011481 }
Eric Andersenc470f442003-07-28 09:56:35 +000011482 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011483}
11484
11485
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011486/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011487
Eric Andersenc470f442003-07-28 09:56:35 +000011488#include <sys/times.h>
11489
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011490static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011491 ' ', offsetof(struct tms, tms_utime),
11492 '\n', offsetof(struct tms, tms_stime),
11493 ' ', offsetof(struct tms, tms_cutime),
11494 '\n', offsetof(struct tms, tms_cstime),
11495 0
11496};
Eric Andersencb57d552001-06-28 07:25:16 +000011497
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011498static int
11499timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011500{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011501 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011502 const unsigned char *p;
11503 struct tms buf;
11504
11505 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011506 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011507
11508 p = timescmd_str;
11509 do {
11510 t = *(clock_t *)(((char *) &buf) + p[1]);
11511 s = t / clk_tck;
11512 out1fmt("%ldm%ld.%.3lds%c",
11513 s/60, s%60,
11514 ((t - s * clk_tck) * 1000) / clk_tck,
11515 p[0]);
11516 } while (*(p += 2));
11517
Eric Andersencb57d552001-06-28 07:25:16 +000011518 return 0;
11519}
11520
Denis Vlasenko131ae172007-02-18 13:00:19 +000011521#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011522static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011523dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011524{
Eric Andersened9ecf72004-06-22 08:29:45 +000011525 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011526 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011527
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011529 result = arith(s, &errcode);
11530 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011531 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011532 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011533 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011534 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011535 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011536 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011537 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011538 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011539 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011540
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011541 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011542}
Eric Andersenc470f442003-07-28 09:56:35 +000011543
Eric Andersenc470f442003-07-28 09:56:35 +000011544/*
Eric Andersen90898442003-08-06 11:20:52 +000011545 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11546 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11547 *
11548 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011549 */
11550static int
Eric Andersen90898442003-08-06 11:20:52 +000011551letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011552{
Eric Andersenc470f442003-07-28 09:56:35 +000011553 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011554 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011555
Eric Andersen90898442003-08-06 11:20:52 +000011556 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011557 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011558 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011559 for (ap = argv + 1; *ap; ap++) {
11560 i = dash_arith(*ap);
11561 }
Eric Andersenc470f442003-07-28 09:56:35 +000011562
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011563 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011564}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011565#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011566
Eric Andersenc470f442003-07-28 09:56:35 +000011567
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011568/* ============ miscbltin.c
11569 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011570 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011571 */
11572
11573#undef rflag
11574
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011575#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011576typedef enum __rlimit_resource rlim_t;
11577#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011578
Eric Andersenc470f442003-07-28 09:56:35 +000011579/*
11580 * The read builtin. The -e option causes backslashes to escape the
11581 * following character.
11582 *
11583 * This uses unbuffered input, which may be avoidable in some cases.
11584 */
Eric Andersenc470f442003-07-28 09:56:35 +000011585static int
11586readcmd(int argc, char **argv)
11587{
11588 char **ap;
11589 int backslash;
11590 char c;
11591 int rflag;
11592 char *prompt;
11593 const char *ifs;
11594 char *p;
11595 int startword;
11596 int status;
11597 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011598#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011599 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011600 int nchars = 0;
11601 int silent = 0;
11602 struct termios tty, old_tty;
11603#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011604#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011605 fd_set set;
11606 struct timeval ts;
11607
11608 ts.tv_sec = ts.tv_usec = 0;
11609#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011610
11611 rflag = 0;
11612 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011613#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011614 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011615#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011616 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011617#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011618 while ((i = nextopt("p:rt:")) != '\0')
11619#else
11620 while ((i = nextopt("p:r")) != '\0')
11621#endif
11622 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011623 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011624 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011625 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011626 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011627#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011628 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011629 nchars = bb_strtou(optionarg, NULL, 10);
11630 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011631 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011632 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011633 break;
11634 case 's':
11635 silent = 1;
11636 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011637#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011638#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011639 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011640 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011641 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011642 /* EINVAL means number is ok, but not terminated by NUL */
11643 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011644 char *p2;
11645 if (*++p) {
11646 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011647 ts.tv_usec = bb_strtou(p, &p2, 10);
11648 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011649 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011650 scale = p2 - p;
11651 /* normalize to usec */
11652 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011653 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011654 while (scale++ < 6)
11655 ts.tv_usec *= 10;
11656 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011657 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011658 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011659 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011660 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011661 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011662 }
Paul Fox02eb9342005-09-07 16:56:02 +000011663 break;
11664#endif
11665 case 'r':
11666 rflag = 1;
11667 break;
11668 default:
11669 break;
11670 }
Eric Andersenc470f442003-07-28 09:56:35 +000011671 }
11672 if (prompt && isatty(0)) {
11673 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011674 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011675 ap = argptr;
11676 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011677 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011678 ifs = bltinlookup("IFS");
11679 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011680 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011681#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011682 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011683 if (tcgetattr(0, &tty) != 0) {
11684 /* Not a tty */
11685 n_flag = 0;
11686 silent = 0;
11687 } else {
11688 old_tty = tty;
11689 if (n_flag) {
11690 tty.c_lflag &= ~ICANON;
11691 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11692 }
11693 if (silent) {
11694 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11695 }
11696 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011697 }
Paul Fox02eb9342005-09-07 16:56:02 +000011698 }
11699#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011700#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011701 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011702 FD_ZERO(&set);
11703 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011704
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011705 /* poll-based wait produces bigger code, using select */
11706 i = select(1, &set, NULL, NULL, &ts);
11707 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011708#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011709 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011710 tcsetattr(0, TCSANOW, &old_tty);
11711#endif
11712 return 1;
11713 }
11714 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011715#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011716 status = 0;
11717 startword = 1;
11718 backslash = 0;
11719 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011720 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011721 if (read(0, &c, 1) != 1) {
11722 status = 1;
11723 break;
11724 }
11725 if (c == '\0')
11726 continue;
11727 if (backslash) {
11728 backslash = 0;
11729 if (c != '\n')
11730 goto put;
11731 continue;
11732 }
11733 if (!rflag && c == '\\') {
11734 backslash++;
11735 continue;
11736 }
11737 if (c == '\n')
11738 break;
11739 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11740 continue;
11741 }
11742 startword = 0;
11743 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11744 STACKSTRNUL(p);
11745 setvar(*ap, stackblock(), 0);
11746 ap++;
11747 startword = 1;
11748 STARTSTACKSTR(p);
11749 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011750 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011751 STPUTC(c, p);
11752 }
11753 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011754/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011755#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011756 while (!n_flag || --nchars);
11757#else
11758 while (1);
11759#endif
11760
11761#if ENABLE_ASH_READ_NCHARS
11762 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011763 tcsetattr(0, TCSANOW, &old_tty);
11764#endif
11765
Eric Andersenc470f442003-07-28 09:56:35 +000011766 STACKSTRNUL(p);
11767 /* Remove trailing blanks */
11768 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11769 *p = '\0';
11770 setvar(*ap, stackblock(), 0);
11771 while (*++ap != NULL)
11772 setvar(*ap, nullstr, 0);
11773 return status;
11774}
11775
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011776static int
11777umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011778{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011779 static const char permuser[3] ALIGN1 = "ugo";
11780 static const char permmode[3] ALIGN1 = "rwx";
11781 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011782 S_IRUSR, S_IWUSR, S_IXUSR,
11783 S_IRGRP, S_IWGRP, S_IXGRP,
11784 S_IROTH, S_IWOTH, S_IXOTH
11785 };
11786
11787 char *ap;
11788 mode_t mask;
11789 int i;
11790 int symbolic_mode = 0;
11791
11792 while (nextopt("S") != '\0') {
11793 symbolic_mode = 1;
11794 }
11795
Denis Vlasenkob012b102007-02-19 22:43:01 +000011796 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011797 mask = umask(0);
11798 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011799 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011800
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011801 ap = *argptr;
11802 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011803 if (symbolic_mode) {
11804 char buf[18];
11805 char *p = buf;
11806
11807 for (i = 0; i < 3; i++) {
11808 int j;
11809
11810 *p++ = permuser[i];
11811 *p++ = '=';
11812 for (j = 0; j < 3; j++) {
11813 if ((mask & permmask[3 * i + j]) == 0) {
11814 *p++ = permmode[j];
11815 }
11816 }
11817 *p++ = ',';
11818 }
11819 *--p = 0;
11820 puts(buf);
11821 } else {
11822 out1fmt("%.4o\n", mask);
11823 }
11824 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011825 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011826 mask = 0;
11827 do {
11828 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011829 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011830 mask = (mask << 3) + (*ap - '0');
11831 } while (*++ap != '\0');
11832 umask(mask);
11833 } else {
11834 mask = ~mask & 0777;
11835 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011836 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011837 }
11838 umask(~mask & 0777);
11839 }
11840 }
11841 return 0;
11842}
11843
11844/*
11845 * ulimit builtin
11846 *
11847 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11848 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11849 * ash by J.T. Conklin.
11850 *
11851 * Public domain.
11852 */
11853
11854struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011855 uint8_t cmd; /* RLIMIT_xxx fit into it */
11856 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011857 char option;
11858};
11859
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011860static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011861#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011862 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011863#endif
11864#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011865 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011866#endif
11867#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011868 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011869#endif
11870#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011871 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011872#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011873#ifdef RLIMIT_CORE
11874 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011875#endif
11876#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011877 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011878#endif
11879#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011880 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011881#endif
11882#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011883 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011884#endif
11885#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011886 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011887#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011888#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011889 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011890#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011891#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011892 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011893#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011894};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011895static const char limits_name[] =
11896#ifdef RLIMIT_CPU
11897 "time(seconds)" "\0"
11898#endif
11899#ifdef RLIMIT_FSIZE
11900 "file(blocks)" "\0"
11901#endif
11902#ifdef RLIMIT_DATA
11903 "data(kb)" "\0"
11904#endif
11905#ifdef RLIMIT_STACK
11906 "stack(kb)" "\0"
11907#endif
11908#ifdef RLIMIT_CORE
11909 "coredump(blocks)" "\0"
11910#endif
11911#ifdef RLIMIT_RSS
11912 "memory(kb)" "\0"
11913#endif
11914#ifdef RLIMIT_MEMLOCK
11915 "locked memory(kb)" "\0"
11916#endif
11917#ifdef RLIMIT_NPROC
11918 "process" "\0"
11919#endif
11920#ifdef RLIMIT_NOFILE
11921 "nofiles" "\0"
11922#endif
11923#ifdef RLIMIT_AS
11924 "vmemory(kb)" "\0"
11925#endif
11926#ifdef RLIMIT_LOCKS
11927 "locks" "\0"
11928#endif
11929;
Eric Andersenc470f442003-07-28 09:56:35 +000011930
Glenn L McGrath76620622004-01-13 10:19:37 +000011931enum limtype { SOFT = 0x1, HARD = 0x2 };
11932
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011933static void
11934printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011935 const struct limits *l)
11936{
11937 rlim_t val;
11938
11939 val = limit->rlim_max;
11940 if (how & SOFT)
11941 val = limit->rlim_cur;
11942
11943 if (val == RLIM_INFINITY)
11944 out1fmt("unlimited\n");
11945 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011946 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011947 out1fmt("%lld\n", (long long) val);
11948 }
11949}
11950
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011951static int
Eric Andersenc470f442003-07-28 09:56:35 +000011952ulimitcmd(int argc, char **argv)
11953{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011954 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011955 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011956 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011957 const struct limits *l;
11958 int set, all = 0;
11959 int optc, what;
11960 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011961
11962 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011963 while ((optc = nextopt("HSa"
11964#ifdef RLIMIT_CPU
11965 "t"
11966#endif
11967#ifdef RLIMIT_FSIZE
11968 "f"
11969#endif
11970#ifdef RLIMIT_DATA
11971 "d"
11972#endif
11973#ifdef RLIMIT_STACK
11974 "s"
11975#endif
11976#ifdef RLIMIT_CORE
11977 "c"
11978#endif
11979#ifdef RLIMIT_RSS
11980 "m"
11981#endif
11982#ifdef RLIMIT_MEMLOCK
11983 "l"
11984#endif
11985#ifdef RLIMIT_NPROC
11986 "p"
11987#endif
11988#ifdef RLIMIT_NOFILE
11989 "n"
11990#endif
11991#ifdef RLIMIT_AS
11992 "v"
11993#endif
11994#ifdef RLIMIT_LOCKS
11995 "w"
11996#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011997 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011998 switch (optc) {
11999 case 'H':
12000 how = HARD;
12001 break;
12002 case 'S':
12003 how = SOFT;
12004 break;
12005 case 'a':
12006 all = 1;
12007 break;
12008 default:
12009 what = optc;
12010 }
12011
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012012 for (l = limits_tbl; l->option != what; l++)
12013 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012014
12015 set = *argptr ? 1 : 0;
12016 if (set) {
12017 char *p = *argptr;
12018
12019 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012020 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012021 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012022 val = RLIM_INFINITY;
12023 else {
12024 val = (rlim_t) 0;
12025
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012026 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012027 val = (val * 10) + (long)(c - '0');
12028 if (val < (rlim_t) 0)
12029 break;
12030 }
12031 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012032 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012033 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012034 }
12035 }
12036 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012037 const char *lname = limits_name;
12038 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012039 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012040 out1fmt("%-20s ", lname);
12041 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012042 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012043 }
12044 return 0;
12045 }
12046
12047 getrlimit(l->cmd, &limit);
12048 if (set) {
12049 if (how & HARD)
12050 limit.rlim_max = val;
12051 if (how & SOFT)
12052 limit.rlim_cur = val;
12053 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012054 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012055 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012056 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012057 }
12058 return 0;
12059}
12060
Eric Andersen90898442003-08-06 11:20:52 +000012061
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012062/* ============ Math support */
12063
Denis Vlasenko131ae172007-02-18 13:00:19 +000012064#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012065
12066/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12067
12068 Permission is hereby granted, free of charge, to any person obtaining
12069 a copy of this software and associated documentation files (the
12070 "Software"), to deal in the Software without restriction, including
12071 without limitation the rights to use, copy, modify, merge, publish,
12072 distribute, sublicense, and/or sell copies of the Software, and to
12073 permit persons to whom the Software is furnished to do so, subject to
12074 the following conditions:
12075
12076 The above copyright notice and this permission notice shall be
12077 included in all copies or substantial portions of the Software.
12078
12079 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12080 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12081 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12082 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12083 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12084 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12085 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12086*/
12087
12088/* This is my infix parser/evaluator. It is optimized for size, intended
12089 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012090 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012091 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012092 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012093 * be that which POSIX specifies for shells. */
12094
12095/* The code uses a simple two-stack algorithm. See
12096 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012097 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012098 * this is based (this code differs in that it applies operators immediately
12099 * to the stack instead of adding them to a queue to end up with an
12100 * expression). */
12101
12102/* To use the routine, call it with an expression string and error return
12103 * pointer */
12104
12105/*
12106 * Aug 24, 2001 Manuel Novoa III
12107 *
12108 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12109 *
12110 * 1) In arith_apply():
12111 * a) Cached values of *numptr and &(numptr[-1]).
12112 * b) Removed redundant test for zero denominator.
12113 *
12114 * 2) In arith():
12115 * a) Eliminated redundant code for processing operator tokens by moving
12116 * to a table-based implementation. Also folded handling of parens
12117 * into the table.
12118 * b) Combined all 3 loops which called arith_apply to reduce generated
12119 * code size at the cost of speed.
12120 *
12121 * 3) The following expressions were treated as valid by the original code:
12122 * 1() , 0! , 1 ( *3 ) .
12123 * These bugs have been fixed by internally enclosing the expression in
12124 * parens and then checking that all binary ops and right parens are
12125 * preceded by a valid expression (NUM_TOKEN).
12126 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012127 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012128 * ctype's isspace() if it is used by another busybox applet or if additional
12129 * whitespace chars should be considered. Look below the "#include"s for a
12130 * precompiler test.
12131 */
12132
12133/*
12134 * Aug 26, 2001 Manuel Novoa III
12135 *
12136 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12137 *
12138 * Merge in Aaron's comments previously posted to the busybox list,
12139 * modified slightly to take account of my changes to the code.
12140 *
12141 */
12142
12143/*
12144 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12145 *
12146 * - allow access to variable,
12147 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12148 * - realize assign syntax (VAR=expr, +=, *= etc)
12149 * - realize exponentiation (** operator)
12150 * - realize comma separated - expr, expr
12151 * - realise ++expr --expr expr++ expr--
12152 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012153 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012154 * - was restored loses XOR operator
12155 * - remove one goto label, added three ;-)
12156 * - protect $((num num)) as true zero expr (Manuel`s error)
12157 * - always use special isspace(), see comment from bash ;-)
12158 */
12159
Eric Andersen90898442003-08-06 11:20:52 +000012160#define arith_isspace(arithval) \
12161 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12162
Eric Andersen90898442003-08-06 11:20:52 +000012163typedef unsigned char operator;
12164
12165/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012166 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012167 * precedence. The ID portion is so that multiple operators can have the
12168 * same precedence, ensuring that the leftmost one is evaluated first.
12169 * Consider * and /. */
12170
12171#define tok_decl(prec,id) (((id)<<5)|(prec))
12172#define PREC(op) ((op) & 0x1F)
12173
12174#define TOK_LPAREN tok_decl(0,0)
12175
12176#define TOK_COMMA tok_decl(1,0)
12177
12178#define TOK_ASSIGN tok_decl(2,0)
12179#define TOK_AND_ASSIGN tok_decl(2,1)
12180#define TOK_OR_ASSIGN tok_decl(2,2)
12181#define TOK_XOR_ASSIGN tok_decl(2,3)
12182#define TOK_PLUS_ASSIGN tok_decl(2,4)
12183#define TOK_MINUS_ASSIGN tok_decl(2,5)
12184#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12185#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12186
12187#define TOK_MUL_ASSIGN tok_decl(3,0)
12188#define TOK_DIV_ASSIGN tok_decl(3,1)
12189#define TOK_REM_ASSIGN tok_decl(3,2)
12190
12191/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012192#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012193
12194/* conditional is right associativity too */
12195#define TOK_CONDITIONAL tok_decl(4,0)
12196#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12197
12198#define TOK_OR tok_decl(5,0)
12199
12200#define TOK_AND tok_decl(6,0)
12201
12202#define TOK_BOR tok_decl(7,0)
12203
12204#define TOK_BXOR tok_decl(8,0)
12205
12206#define TOK_BAND tok_decl(9,0)
12207
12208#define TOK_EQ tok_decl(10,0)
12209#define TOK_NE tok_decl(10,1)
12210
12211#define TOK_LT tok_decl(11,0)
12212#define TOK_GT tok_decl(11,1)
12213#define TOK_GE tok_decl(11,2)
12214#define TOK_LE tok_decl(11,3)
12215
12216#define TOK_LSHIFT tok_decl(12,0)
12217#define TOK_RSHIFT tok_decl(12,1)
12218
12219#define TOK_ADD tok_decl(13,0)
12220#define TOK_SUB tok_decl(13,1)
12221
12222#define TOK_MUL tok_decl(14,0)
12223#define TOK_DIV tok_decl(14,1)
12224#define TOK_REM tok_decl(14,2)
12225
12226/* exponent is right associativity */
12227#define TOK_EXPONENT tok_decl(15,1)
12228
12229/* For now unary operators. */
12230#define UNARYPREC 16
12231#define TOK_BNOT tok_decl(UNARYPREC,0)
12232#define TOK_NOT tok_decl(UNARYPREC,1)
12233
12234#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12235#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12236
12237#define PREC_PRE (UNARYPREC+2)
12238
12239#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12240#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12241
12242#define PREC_POST (UNARYPREC+3)
12243
12244#define TOK_POST_INC tok_decl(PREC_POST, 0)
12245#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12246
12247#define SPEC_PREC (UNARYPREC+4)
12248
12249#define TOK_NUM tok_decl(SPEC_PREC, 0)
12250#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12251
12252#define NUMPTR (*numstackptr)
12253
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012254static int
12255tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012256{
12257 operator prec = PREC(op);
12258
12259 convert_prec_is_assing(prec);
12260 return (prec == PREC(TOK_ASSIGN) ||
12261 prec == PREC_PRE || prec == PREC_POST);
12262}
12263
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012264static int
12265is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012266{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012267 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12268 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012269}
12270
Eric Andersen90898442003-08-06 11:20:52 +000012271typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012272 arith_t val;
12273 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012274 char contidional_second_val_initialized;
12275 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012276 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012277} v_n_t;
12278
Eric Andersen90898442003-08-06 11:20:52 +000012279typedef struct CHK_VAR_RECURSIVE_LOOPED {
12280 const char *var;
12281 struct CHK_VAR_RECURSIVE_LOOPED *next;
12282} chk_var_recursive_looped_t;
12283
12284static chk_var_recursive_looped_t *prev_chk_var_recursive;
12285
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012286static int
12287arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012288{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012289 if (t->var) {
12290 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012291
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012292 if (p) {
12293 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012294
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012295 /* recursive try as expression */
12296 chk_var_recursive_looped_t *cur;
12297 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012298
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012299 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12300 if (strcmp(cur->var, t->var) == 0) {
12301 /* expression recursion loop detected */
12302 return -5;
12303 }
12304 }
12305 /* save current lookuped var name */
12306 cur = prev_chk_var_recursive;
12307 cur_save.var = t->var;
12308 cur_save.next = cur;
12309 prev_chk_var_recursive = &cur_save;
12310
12311 t->val = arith (p, &errcode);
12312 /* restore previous ptr after recursiving */
12313 prev_chk_var_recursive = cur;
12314 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012315 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012316 /* allow undefined var as 0 */
12317 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012318 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012319 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012320}
12321
12322/* "applying" a token means performing it on the top elements on the integer
12323 * stack. For a unary operator it will only change the top element, but a
12324 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012325static int
12326arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012327{
Eric Andersen90898442003-08-06 11:20:52 +000012328 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012329 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012330 int ret_arith_lookup_val;
12331
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012332 /* There is no operator that can work without arguments */
12333 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012334 numptr_m1 = NUMPTR - 1;
12335
12336 /* check operand is var with noninteger value */
12337 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012338 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012339 return ret_arith_lookup_val;
12340
12341 rez = numptr_m1->val;
12342 if (op == TOK_UMINUS)
12343 rez *= -1;
12344 else if (op == TOK_NOT)
12345 rez = !rez;
12346 else if (op == TOK_BNOT)
12347 rez = ~rez;
12348 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12349 rez++;
12350 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12351 rez--;
12352 else if (op != TOK_UPLUS) {
12353 /* Binary operators */
12354
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012355 /* check and binary operators need two arguments */
12356 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012357
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012358 /* ... and they pop one */
12359 --NUMPTR;
12360 numptr_val = rez;
12361 if (op == TOK_CONDITIONAL) {
12362 if (! numptr_m1->contidional_second_val_initialized) {
12363 /* protect $((expr1 ? expr2)) without ": expr" */
12364 goto err;
12365 }
12366 rez = numptr_m1->contidional_second_val;
12367 } else if (numptr_m1->contidional_second_val_initialized) {
12368 /* protect $((expr1 : expr2)) without "expr ? " */
12369 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012370 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012371 numptr_m1 = NUMPTR - 1;
12372 if (op != TOK_ASSIGN) {
12373 /* check operand is var with noninteger value for not '=' */
12374 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12375 if (ret_arith_lookup_val)
12376 return ret_arith_lookup_val;
12377 }
12378 if (op == TOK_CONDITIONAL) {
12379 numptr_m1->contidional_second_val = rez;
12380 }
12381 rez = numptr_m1->val;
12382 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012383 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012384 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012385 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012387 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012388 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012389 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012390 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012391 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012392 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012393 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012394 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012395 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012397 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012399 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012400 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012401 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012403 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012405 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012407 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012408 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012409 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012410 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012411 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012412 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012413 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012414 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012415 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012416 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012417 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012418 /* protect $((expr : expr)) without "expr ? " */
12419 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012420 }
12421 numptr_m1->contidional_second_val_initialized = op;
12422 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012423 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012424 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012425 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012426 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012427 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012428 return -3; /* exponent less than 0 */
12429 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012430 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012431
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012432 if (numptr_val)
12433 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012434 c *= rez;
12435 rez = c;
12436 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012437 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012438 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012440 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012442 rez %= numptr_val;
12443 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012444 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012445 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012446
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012447 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012448 /* Hmm, 1=2 ? */
12449 goto err;
12450 }
12451 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012452#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012453 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012454#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012455 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012456#endif
Eric Andersen90898442003-08-06 11:20:52 +000012457 setvar(numptr_m1->var, buf, 0);
12458 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012459 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012460 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012462 rez++;
12463 }
12464 numptr_m1->val = rez;
12465 /* protect geting var value, is number now */
12466 numptr_m1->var = NULL;
12467 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012468 err:
12469 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012470}
12471
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012472/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012473static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012474 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12475 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12476 '<','<', 0, TOK_LSHIFT,
12477 '>','>', 0, TOK_RSHIFT,
12478 '|','|', 0, TOK_OR,
12479 '&','&', 0, TOK_AND,
12480 '!','=', 0, TOK_NE,
12481 '<','=', 0, TOK_LE,
12482 '>','=', 0, TOK_GE,
12483 '=','=', 0, TOK_EQ,
12484 '|','=', 0, TOK_OR_ASSIGN,
12485 '&','=', 0, TOK_AND_ASSIGN,
12486 '*','=', 0, TOK_MUL_ASSIGN,
12487 '/','=', 0, TOK_DIV_ASSIGN,
12488 '%','=', 0, TOK_REM_ASSIGN,
12489 '+','=', 0, TOK_PLUS_ASSIGN,
12490 '-','=', 0, TOK_MINUS_ASSIGN,
12491 '-','-', 0, TOK_POST_DEC,
12492 '^','=', 0, TOK_XOR_ASSIGN,
12493 '+','+', 0, TOK_POST_INC,
12494 '*','*', 0, TOK_EXPONENT,
12495 '!', 0, TOK_NOT,
12496 '<', 0, TOK_LT,
12497 '>', 0, TOK_GT,
12498 '=', 0, TOK_ASSIGN,
12499 '|', 0, TOK_BOR,
12500 '&', 0, TOK_BAND,
12501 '*', 0, TOK_MUL,
12502 '/', 0, TOK_DIV,
12503 '%', 0, TOK_REM,
12504 '+', 0, TOK_ADD,
12505 '-', 0, TOK_SUB,
12506 '^', 0, TOK_BXOR,
12507 /* uniq */
12508 '~', 0, TOK_BNOT,
12509 ',', 0, TOK_COMMA,
12510 '?', 0, TOK_CONDITIONAL,
12511 ':', 0, TOK_CONDITIONAL_SEP,
12512 ')', 0, TOK_RPAREN,
12513 '(', 0, TOK_LPAREN,
12514 0
12515};
12516/* ptr to ")" */
12517#define endexpression &op_tokens[sizeof(op_tokens)-7]
12518
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012519static arith_t
12520arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012521{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012522 char arithval; /* Current character under analysis */
12523 operator lasttok, op;
12524 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012525
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012526 const char *p = endexpression;
12527 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012528
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012529 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012530
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 /* Stack of integers */
12532 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12533 * in any given correct or incorrect expression is left as an exercise to
12534 * the reader. */
12535 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12536 *numstackptr = numstack;
12537 /* Stack of operator tokens */
12538 operator *stack = alloca((datasizes) * sizeof(operator)),
12539 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012540
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012541 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12542 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012543
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012544 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012545 arithval = *expr;
12546 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012547 if (p == endexpression) {
12548 /* Null expression. */
12549 return 0;
12550 }
12551
12552 /* This is only reached after all tokens have been extracted from the
12553 * input stream. If there are still tokens on the operator stack, they
12554 * are to be applied in order. At the end, there should be a final
12555 * result on the integer stack */
12556
12557 if (expr != endexpression + 1) {
12558 /* If we haven't done so already, */
12559 /* append a closing right paren */
12560 expr = endexpression;
12561 /* and let the loop process it. */
12562 continue;
12563 }
12564 /* At this point, we're done with the expression. */
12565 if (numstackptr != numstack+1) {
12566 /* ... but if there isn't, it's bad */
12567 err:
12568 return (*perrcode = -1);
12569 }
12570 if (numstack->var) {
12571 /* expression is $((var)) only, lookup now */
12572 errcode = arith_lookup_val(numstack);
12573 }
12574 ret:
12575 *perrcode = errcode;
12576 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012577 }
12578
Eric Andersen90898442003-08-06 11:20:52 +000012579 /* Continue processing the expression. */
12580 if (arith_isspace(arithval)) {
12581 /* Skip whitespace */
12582 goto prologue;
12583 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012584 p = endofname(expr);
12585 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012586 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012587
12588 numstackptr->var = alloca(var_name_size);
12589 safe_strncpy(numstackptr->var, expr, var_name_size);
12590 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012591 num:
Eric Andersen90898442003-08-06 11:20:52 +000012592 numstackptr->contidional_second_val_initialized = 0;
12593 numstackptr++;
12594 lasttok = TOK_NUM;
12595 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012596 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012597 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012598 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012599#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012600 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012601#else
12602 numstackptr->val = strtol(expr, (char **) &expr, 0);
12603#endif
Eric Andersen90898442003-08-06 11:20:52 +000012604 goto num;
12605 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012606 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012607 const char *o;
12608
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012609 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012610 /* strange operator not found */
12611 goto err;
12612 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012613 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012614 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012615 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012616 /* found */
12617 expr = o - 1;
12618 break;
12619 }
12620 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012621 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012622 p++;
12623 /* skip zero delim */
12624 p++;
12625 }
12626 op = p[1];
12627
12628 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012629 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12630 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012631
12632 /* Plus and minus are binary (not unary) _only_ if the last
12633 * token was as number, or a right paren (which pretends to be
12634 * a number, since it evaluates to one). Think about it.
12635 * It makes sense. */
12636 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012637 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012638 case TOK_ADD:
12639 op = TOK_UPLUS;
12640 break;
12641 case TOK_SUB:
12642 op = TOK_UMINUS;
12643 break;
12644 case TOK_POST_INC:
12645 op = TOK_PRE_INC;
12646 break;
12647 case TOK_POST_DEC:
12648 op = TOK_PRE_DEC;
12649 break;
Eric Andersen90898442003-08-06 11:20:52 +000012650 }
12651 }
12652 /* We don't want a unary operator to cause recursive descent on the
12653 * stack, because there can be many in a row and it could cause an
12654 * operator to be evaluated before its argument is pushed onto the
12655 * integer stack. */
12656 /* But for binary operators, "apply" everything on the operator
12657 * stack until we find an operator with a lesser priority than the
12658 * one we have just extracted. */
12659 /* Left paren is given the lowest priority so it will never be
12660 * "applied" in this way.
12661 * if associativity is right and priority eq, applied also skip
12662 */
12663 prec = PREC(op);
12664 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12665 /* not left paren or unary */
12666 if (lasttok != TOK_NUM) {
12667 /* binary op must be preceded by a num */
12668 goto err;
12669 }
12670 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012671 if (op == TOK_RPAREN) {
12672 /* The algorithm employed here is simple: while we don't
12673 * hit an open paren nor the bottom of the stack, pop
12674 * tokens and apply them */
12675 if (stackptr[-1] == TOK_LPAREN) {
12676 --stackptr;
12677 /* Any operator directly after a */
12678 lasttok = TOK_NUM;
12679 /* close paren should consider itself binary */
12680 goto prologue;
12681 }
12682 } else {
12683 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012684
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012685 convert_prec_is_assing(prec);
12686 convert_prec_is_assing(prev_prec);
12687 if (prev_prec < prec)
12688 break;
12689 /* check right assoc */
12690 if (prev_prec == prec && is_right_associativity(prec))
12691 break;
12692 }
12693 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12694 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012695 }
12696 if (op == TOK_RPAREN) {
12697 goto err;
12698 }
12699 }
12700
12701 /* Push this operator to the stack and remember it. */
12702 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012703 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012704 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012705 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012706}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012707#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012708
12709
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012710/* ============ main() and helpers */
12711
12712/*
12713 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012714 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012715static void exitshell(void) ATTRIBUTE_NORETURN;
12716static void
12717exitshell(void)
12718{
12719 struct jmploc loc;
12720 char *p;
12721 int status;
12722
12723 status = exitstatus;
12724 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12725 if (setjmp(loc.loc)) {
12726 if (exception == EXEXIT)
12727/* dash bug: it just does _exit(exitstatus) here
12728 * but we have to do setjobctl(0) first!
12729 * (bug is still not fixed in dash-0.5.3 - if you run dash
12730 * under Midnight Commander, on exit from dash MC is backgrounded) */
12731 status = exitstatus;
12732 goto out;
12733 }
12734 exception_handler = &loc;
12735 p = trap[0];
12736 if (p) {
12737 trap[0] = NULL;
12738 evalstring(p, 0);
12739 }
12740 flush_stdout_stderr();
12741 out:
12742 setjobctl(0);
12743 _exit(status);
12744 /* NOTREACHED */
12745}
12746
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012747static void
12748init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012749{
12750 /* from input.c: */
12751 basepf.nextc = basepf.buf = basebuf;
12752
12753 /* from trap.c: */
12754 signal(SIGCHLD, SIG_DFL);
12755
12756 /* from var.c: */
12757 {
12758 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012759 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012760 const char *p;
12761 struct stat st1, st2;
12762
12763 initvar();
12764 for (envp = environ; envp && *envp; envp++) {
12765 if (strchr(*envp, '=')) {
12766 setvareq(*envp, VEXPORT|VTEXTFIXED);
12767 }
12768 }
12769
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012770 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012771 setvar("PPID", ppid, 0);
12772
12773 p = lookupvar("PWD");
12774 if (p)
12775 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12776 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12777 p = '\0';
12778 setpwd(p, 0);
12779 }
12780}
12781
12782/*
12783 * Process the shell command line arguments.
12784 */
12785static void
12786procargs(int argc, char **argv)
12787{
12788 int i;
12789 const char *xminusc;
12790 char **xargv;
12791
12792 xargv = argv;
12793 arg0 = xargv[0];
12794 if (argc > 0)
12795 xargv++;
12796 for (i = 0; i < NOPTS; i++)
12797 optlist[i] = 2;
12798 argptr = xargv;
12799 options(1);
12800 xargv = argptr;
12801 xminusc = minusc;
12802 if (*xargv == NULL) {
12803 if (xminusc)
12804 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12805 sflag = 1;
12806 }
12807 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12808 iflag = 1;
12809 if (mflag == 2)
12810 mflag = iflag;
12811 for (i = 0; i < NOPTS; i++)
12812 if (optlist[i] == 2)
12813 optlist[i] = 0;
12814#if DEBUG == 2
12815 debug = 1;
12816#endif
12817 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12818 if (xminusc) {
12819 minusc = *xargv++;
12820 if (*xargv)
12821 goto setarg0;
12822 } else if (!sflag) {
12823 setinputfile(*xargv, 0);
12824 setarg0:
12825 arg0 = *xargv++;
12826 commandname = arg0;
12827 }
12828
12829 shellparam.p = xargv;
12830#if ENABLE_ASH_GETOPTS
12831 shellparam.optind = 1;
12832 shellparam.optoff = -1;
12833#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012834 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012835 while (*xargv) {
12836 shellparam.nparam++;
12837 xargv++;
12838 }
12839 optschanged();
12840}
12841
12842/*
12843 * Read /etc/profile or .profile.
12844 */
12845static void
12846read_profile(const char *name)
12847{
12848 int skip;
12849
12850 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12851 return;
12852 skip = cmdloop(0);
12853 popfile();
12854 if (skip)
12855 exitshell();
12856}
12857
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012858/*
12859 * This routine is called when an error or an interrupt occurs in an
12860 * interactive shell and control is returned to the main command loop.
12861 */
12862static void
12863reset(void)
12864{
12865 /* from eval.c: */
12866 evalskip = 0;
12867 loopnest = 0;
12868 /* from input.c: */
12869 parselleft = parsenleft = 0; /* clear input buffer */
12870 popallfiles();
12871 /* from parser.c: */
12872 tokpushback = 0;
12873 checkkwd = 0;
12874 /* from redir.c: */
12875 clearredir(0);
12876}
12877
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012878#if PROFILE
12879static short profile_buf[16384];
12880extern int etext();
12881#endif
12882
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012883/*
12884 * Main routine. We initialize things, parse the arguments, execute
12885 * profiles if we're a login shell, and then call cmdloop to execute
12886 * commands. The setjmp call sets up the location to jump to when an
12887 * exception occurs. When an exception occurs the variable "state"
12888 * is used to figure out how far we had gotten.
12889 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012890int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012891int ash_main(int argc, char **argv)
12892{
12893 char *shinit;
12894 volatile int state;
12895 struct jmploc jmploc;
12896 struct stackmark smark;
12897
Denis Vlasenko01631112007-12-16 17:20:38 +000012898 /* Initialize global data */
12899 INIT_G_misc();
12900 INIT_G_memstack();
12901 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012902#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012903 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012904#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012905 INIT_G_cmdtable();
12906
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012907#if PROFILE
12908 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12909#endif
12910
12911#if ENABLE_FEATURE_EDITING
12912 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12913#endif
12914 state = 0;
12915 if (setjmp(jmploc.loc)) {
12916 int e;
12917 int s;
12918
12919 reset();
12920
12921 e = exception;
12922 if (e == EXERROR)
12923 exitstatus = 2;
12924 s = state;
12925 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12926 exitshell();
12927
12928 if (e == EXINT) {
12929 outcslow('\n', stderr);
12930 }
12931 popstackmark(&smark);
12932 FORCE_INT_ON; /* enable interrupts */
12933 if (s == 1)
12934 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012935 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012936 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012937 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012939 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012940 }
12941 exception_handler = &jmploc;
12942#if DEBUG
12943 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012944 trace_puts("Shell args: ");
12945 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012946#endif
12947 rootpid = getpid();
12948
12949#if ENABLE_ASH_RANDOM_SUPPORT
12950 rseed = rootpid + time(NULL);
12951#endif
12952 init();
12953 setstackmark(&smark);
12954 procargs(argc, argv);
12955#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12956 if (iflag) {
12957 const char *hp = lookupvar("HISTFILE");
12958
12959 if (hp == NULL) {
12960 hp = lookupvar("HOME");
12961 if (hp != NULL) {
12962 char *defhp = concat_path_file(hp, ".ash_history");
12963 setvar("HISTFILE", defhp, 0);
12964 free(defhp);
12965 }
12966 }
12967 }
12968#endif
12969 if (argv[0] && argv[0][0] == '-')
12970 isloginsh = 1;
12971 if (isloginsh) {
12972 state = 1;
12973 read_profile("/etc/profile");
12974 state1:
12975 state = 2;
12976 read_profile(".profile");
12977 }
12978 state2:
12979 state = 3;
12980 if (
12981#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012982 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012983#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012984 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012985 ) {
12986 shinit = lookupvar("ENV");
12987 if (shinit != NULL && *shinit != '\0') {
12988 read_profile(shinit);
12989 }
12990 }
12991 state3:
12992 state = 4;
12993 if (minusc)
12994 evalstring(minusc, 0);
12995
12996 if (sflag || minusc == NULL) {
12997#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12998 if ( iflag ) {
12999 const char *hp = lookupvar("HISTFILE");
13000
13001 if (hp != NULL)
13002 line_input_state->hist_file = hp;
13003 }
13004#endif
13005 state4: /* XXX ??? - why isn't this before the "if" statement */
13006 cmdloop(1);
13007 }
13008#if PROFILE
13009 monitor(0);
13010#endif
13011#ifdef GPROF
13012 {
13013 extern void _mcleanup(void);
13014 _mcleanup();
13015 }
13016#endif
13017 exitshell();
13018 /* NOTREACHED */
13019}
13020
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013021#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013022const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013023int main(int argc, char **argv)
13024{
13025 return ash_main(argc, argv);
13026}
13027#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013028
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013029
Eric Andersendf82f612001-06-28 07:46:40 +000013030/*-
13031 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013032 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013033 *
13034 * This code is derived from software contributed to Berkeley by
13035 * Kenneth Almquist.
13036 *
13037 * Redistribution and use in source and binary forms, with or without
13038 * modification, are permitted provided that the following conditions
13039 * are met:
13040 * 1. Redistributions of source code must retain the above copyright
13041 * notice, this list of conditions and the following disclaimer.
13042 * 2. Redistributions in binary form must reproduce the above copyright
13043 * notice, this list of conditions and the following disclaimer in the
13044 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013045 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013046 * may be used to endorse or promote products derived from this software
13047 * without specific prior written permission.
13048 *
13049 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13050 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13051 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13052 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13053 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13054 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13055 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13056 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13057 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13058 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13059 * SUCH DAMAGE.
13060 */