blob: ffa4019363b58d70c60b632b1a4f7ffef1ba41f8 [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
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000232 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000233 * 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 Vlasenko36fc3cd2008-01-29 09:23:49 +0000275 /* Signal is not automatically unmasked after it is raised,
276 * do it ourself - unmask all signals */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000277 sigemptyset(&mask);
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000278 sigprocmask(SIG_SETMASK, &mask, NULL);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000279 /* 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 */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003340#define DOWAIT_NONBLOCK WNOHANG
3341#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342
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
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003587 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003588 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
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003760waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003761{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762#if JOBS
3763 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003764 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765#endif
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003766 return waitpid(-1, status, wait_flags); // safe_waitpid?
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003767}
3768
3769/*
3770 * Wait for a process to terminate.
3771 */
3772static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003773dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774{
3775 int pid;
3776 int status;
3777 struct job *jp;
3778 struct job *thisjob;
3779 int state;
3780
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003781 TRACE(("dowait(%d) called\n", wait_flags));
3782 pid = waitproc(wait_flags, &status);
3783 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784 if (pid <= 0)
3785 return pid;
3786 INT_OFF;
3787 thisjob = NULL;
3788 for (jp = curjob; jp; jp = jp->prev_job) {
3789 struct procstat *sp;
3790 struct procstat *spend;
3791 if (jp->state == JOBDONE)
3792 continue;
3793 state = JOBDONE;
3794 spend = jp->ps + jp->nprocs;
3795 sp = jp->ps;
3796 do {
3797 if (sp->pid == pid) {
3798 TRACE(("Job %d: changing status of proc %d "
3799 "from 0x%x to 0x%x\n",
3800 jobno(jp), pid, sp->status, status));
3801 sp->status = status;
3802 thisjob = jp;
3803 }
3804 if (sp->status == -1)
3805 state = JOBRUNNING;
3806#if JOBS
3807 if (state == JOBRUNNING)
3808 continue;
3809 if (WIFSTOPPED(sp->status)) {
3810 jp->stopstatus = sp->status;
3811 state = JOBSTOPPED;
3812 }
3813#endif
3814 } while (++sp < spend);
3815 if (thisjob)
3816 goto gotjob;
3817 }
3818#if JOBS
3819 if (!WIFSTOPPED(status))
3820#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 jobless--;
3822 goto out;
3823
3824 gotjob:
3825 if (state != JOBRUNNING) {
3826 thisjob->changed = 1;
3827
3828 if (thisjob->state != state) {
3829 TRACE(("Job %d: changing state from %d to %d\n",
3830 jobno(thisjob), thisjob->state, state));
3831 thisjob->state = state;
3832#if JOBS
3833 if (state == JOBSTOPPED) {
3834 set_curjob(thisjob, CUR_STOPPED);
3835 }
3836#endif
3837 }
3838 }
3839
3840 out:
3841 INT_ON;
3842
3843 if (thisjob && thisjob == job) {
3844 char s[48 + 1];
3845 int len;
3846
3847 len = sprint_status(s, status, 1);
3848 if (len) {
3849 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003850 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003851 out2str(s);
3852 }
3853 }
3854 return pid;
3855}
3856
3857#if JOBS
3858static void
3859showjob(FILE *out, struct job *jp, int mode)
3860{
3861 struct procstat *ps;
3862 struct procstat *psend;
3863 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003864 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003865 char s[80];
3866
3867 ps = jp->ps;
3868
3869 if (mode & SHOW_PGID) {
3870 /* just output process (group) id of pipeline */
3871 fprintf(out, "%d\n", ps->pid);
3872 return;
3873 }
3874
3875 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003876 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003877
3878 if (jp == curjob)
3879 s[col - 2] = '+';
3880 else if (curjob && jp == curjob->prev_job)
3881 s[col - 2] = '-';
3882
3883 if (mode & SHOW_PID)
3884 col += fmtstr(s + col, 16, "%d ", ps->pid);
3885
3886 psend = ps + jp->nprocs;
3887
3888 if (jp->state == JOBRUNNING) {
3889 strcpy(s + col, "Running");
3890 col += sizeof("Running") - 1;
3891 } else {
3892 int status = psend[-1].status;
3893 if (jp->state == JOBSTOPPED)
3894 status = jp->stopstatus;
3895 col += sprint_status(s + col, status, 0);
3896 }
3897
3898 goto start;
3899
3900 do {
3901 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003902 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903 start:
3904 fprintf(out, "%s%*c%s",
3905 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3906 );
3907 if (!(mode & SHOW_PID)) {
3908 showpipe(jp, out);
3909 break;
3910 }
3911 if (++ps == psend) {
3912 outcslow('\n', out);
3913 break;
3914 }
3915 } while (1);
3916
3917 jp->changed = 0;
3918
3919 if (jp->state == JOBDONE) {
3920 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3921 freejob(jp);
3922 }
3923}
3924
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925/*
3926 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3927 * statuses have changed since the last call to showjobs.
3928 */
3929static void
3930showjobs(FILE *out, int mode)
3931{
3932 struct job *jp;
3933
3934 TRACE(("showjobs(%x) called\n", mode));
3935
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003936 /* If not even one job changed, there is nothing to do */
3937 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 continue;
3939
3940 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003941 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003943 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944 }
3945}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003946
3947static int
3948jobscmd(int argc, char **argv)
3949{
3950 int mode, m;
3951
3952 mode = 0;
3953 while ((m = nextopt("lp"))) {
3954 if (m == 'l')
3955 mode = SHOW_PID;
3956 else
3957 mode = SHOW_PGID;
3958 }
3959
3960 argv = argptr;
3961 if (*argv) {
3962 do
3963 showjob(stdout, getjob(*argv,0), mode);
3964 while (*++argv);
3965 } else
3966 showjobs(stdout, mode);
3967
3968 return 0;
3969}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970#endif /* JOBS */
3971
3972static int
3973getstatus(struct job *job)
3974{
3975 int status;
3976 int retval;
3977
3978 status = job->ps[job->nprocs - 1].status;
3979 retval = WEXITSTATUS(status);
3980 if (!WIFEXITED(status)) {
3981#if JOBS
3982 retval = WSTOPSIG(status);
3983 if (!WIFSTOPPED(status))
3984#endif
3985 {
3986 /* XXX: limits number of signals */
3987 retval = WTERMSIG(status);
3988#if JOBS
3989 if (retval == SIGINT)
3990 job->sigint = 1;
3991#endif
3992 }
3993 retval += 128;
3994 }
3995 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3996 jobno(job), job->nprocs, status, retval));
3997 return retval;
3998}
3999
4000static int
4001waitcmd(int argc, char **argv)
4002{
4003 struct job *job;
4004 int retval;
4005 struct job *jp;
4006
4007 EXSIGON;
4008
4009 nextopt(nullstr);
4010 retval = 0;
4011
4012 argv = argptr;
4013 if (!*argv) {
4014 /* wait for all jobs */
4015 for (;;) {
4016 jp = curjob;
4017 while (1) {
4018 if (!jp) {
4019 /* no running procs */
4020 goto out;
4021 }
4022 if (jp->state == JOBRUNNING)
4023 break;
4024 jp->waited = 1;
4025 jp = jp->prev_job;
4026 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004027 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004028 }
4029 }
4030
4031 retval = 127;
4032 do {
4033 if (**argv != '%') {
4034 pid_t pid = number(*argv);
4035 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004036 while (1) {
4037 if (!job)
4038 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004039 if (job->ps[job->nprocs - 1].pid == pid)
4040 break;
4041 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004042 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043 } else
4044 job = getjob(*argv, 0);
4045 /* loop until process terminated or stopped */
4046 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004047 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004048 job->waited = 1;
4049 retval = getstatus(job);
4050 repeat:
4051 ;
4052 } while (*++argv);
4053
4054 out:
4055 return retval;
4056}
4057
4058static struct job *
4059growjobtab(void)
4060{
4061 size_t len;
4062 ptrdiff_t offset;
4063 struct job *jp, *jq;
4064
4065 len = njobs * sizeof(*jp);
4066 jq = jobtab;
4067 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4068
4069 offset = (char *)jp - (char *)jq;
4070 if (offset) {
4071 /* Relocate pointers */
4072 size_t l = len;
4073
4074 jq = (struct job *)((char *)jq + l);
4075 while (l) {
4076 l -= sizeof(*jp);
4077 jq--;
4078#define joff(p) ((struct job *)((char *)(p) + l))
4079#define jmove(p) (p) = (void *)((char *)(p) + offset)
4080 if (joff(jp)->ps == &jq->ps0)
4081 jmove(joff(jp)->ps);
4082 if (joff(jp)->prev_job)
4083 jmove(joff(jp)->prev_job);
4084 }
4085 if (curjob)
4086 jmove(curjob);
4087#undef joff
4088#undef jmove
4089 }
4090
4091 njobs += 4;
4092 jobtab = jp;
4093 jp = (struct job *)((char *)jp + len);
4094 jq = jp + 3;
4095 do {
4096 jq->used = 0;
4097 } while (--jq >= jp);
4098 return jp;
4099}
4100
4101/*
4102 * Return a new job structure.
4103 * Called with interrupts off.
4104 */
4105static struct job *
4106makejob(union node *node, int nprocs)
4107{
4108 int i;
4109 struct job *jp;
4110
4111 for (i = njobs, jp = jobtab; ; jp++) {
4112 if (--i < 0) {
4113 jp = growjobtab();
4114 break;
4115 }
4116 if (jp->used == 0)
4117 break;
4118 if (jp->state != JOBDONE || !jp->waited)
4119 continue;
4120#if JOBS
4121 if (jobctl)
4122 continue;
4123#endif
4124 freejob(jp);
4125 break;
4126 }
4127 memset(jp, 0, sizeof(*jp));
4128#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004129 /* jp->jobctl is a bitfield.
4130 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004131 if (jobctl)
4132 jp->jobctl = 1;
4133#endif
4134 jp->prev_job = curjob;
4135 curjob = jp;
4136 jp->used = 1;
4137 jp->ps = &jp->ps0;
4138 if (nprocs > 1) {
4139 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4140 }
4141 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4142 jobno(jp)));
4143 return jp;
4144}
4145
4146#if JOBS
4147/*
4148 * Return a string identifying a command (to be printed by the
4149 * jobs command).
4150 */
4151static char *cmdnextc;
4152
4153static void
4154cmdputs(const char *s)
4155{
4156 const char *p, *str;
4157 char c, cc[2] = " ";
4158 char *nextc;
4159 int subtype = 0;
4160 int quoted = 0;
4161 static const char vstype[VSTYPE + 1][4] = {
4162 "", "}", "-", "+", "?", "=",
4163 "%", "%%", "#", "##"
4164 };
4165
4166 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4167 p = s;
4168 while ((c = *p++) != 0) {
4169 str = 0;
4170 switch (c) {
4171 case CTLESC:
4172 c = *p++;
4173 break;
4174 case CTLVAR:
4175 subtype = *p++;
4176 if ((subtype & VSTYPE) == VSLENGTH)
4177 str = "${#";
4178 else
4179 str = "${";
4180 if (!(subtype & VSQUOTE) == !(quoted & 1))
4181 goto dostr;
4182 quoted ^= 1;
4183 c = '"';
4184 break;
4185 case CTLENDVAR:
4186 str = "\"}" + !(quoted & 1);
4187 quoted >>= 1;
4188 subtype = 0;
4189 goto dostr;
4190 case CTLBACKQ:
4191 str = "$(...)";
4192 goto dostr;
4193 case CTLBACKQ+CTLQUOTE:
4194 str = "\"$(...)\"";
4195 goto dostr;
4196#if ENABLE_ASH_MATH_SUPPORT
4197 case CTLARI:
4198 str = "$((";
4199 goto dostr;
4200 case CTLENDARI:
4201 str = "))";
4202 goto dostr;
4203#endif
4204 case CTLQUOTEMARK:
4205 quoted ^= 1;
4206 c = '"';
4207 break;
4208 case '=':
4209 if (subtype == 0)
4210 break;
4211 if ((subtype & VSTYPE) != VSNORMAL)
4212 quoted <<= 1;
4213 str = vstype[subtype & VSTYPE];
4214 if (subtype & VSNUL)
4215 c = ':';
4216 else
4217 goto checkstr;
4218 break;
4219 case '\'':
4220 case '\\':
4221 case '"':
4222 case '$':
4223 /* These can only happen inside quotes */
4224 cc[0] = c;
4225 str = cc;
4226 c = '\\';
4227 break;
4228 default:
4229 break;
4230 }
4231 USTPUTC(c, nextc);
4232 checkstr:
4233 if (!str)
4234 continue;
4235 dostr:
4236 while ((c = *str++)) {
4237 USTPUTC(c, nextc);
4238 }
4239 }
4240 if (quoted & 1) {
4241 USTPUTC('"', nextc);
4242 }
4243 *nextc = 0;
4244 cmdnextc = nextc;
4245}
4246
4247/* cmdtxt() and cmdlist() call each other */
4248static void cmdtxt(union node *n);
4249
4250static void
4251cmdlist(union node *np, int sep)
4252{
4253 for (; np; np = np->narg.next) {
4254 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004255 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004256 cmdtxt(np);
4257 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004258 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004259 }
4260}
4261
4262static void
4263cmdtxt(union node *n)
4264{
4265 union node *np;
4266 struct nodelist *lp;
4267 const char *p;
4268 char s[2];
4269
4270 if (!n)
4271 return;
4272 switch (n->type) {
4273 default:
4274#if DEBUG
4275 abort();
4276#endif
4277 case NPIPE:
4278 lp = n->npipe.cmdlist;
4279 for (;;) {
4280 cmdtxt(lp->n);
4281 lp = lp->next;
4282 if (!lp)
4283 break;
4284 cmdputs(" | ");
4285 }
4286 break;
4287 case NSEMI:
4288 p = "; ";
4289 goto binop;
4290 case NAND:
4291 p = " && ";
4292 goto binop;
4293 case NOR:
4294 p = " || ";
4295 binop:
4296 cmdtxt(n->nbinary.ch1);
4297 cmdputs(p);
4298 n = n->nbinary.ch2;
4299 goto donode;
4300 case NREDIR:
4301 case NBACKGND:
4302 n = n->nredir.n;
4303 goto donode;
4304 case NNOT:
4305 cmdputs("!");
4306 n = n->nnot.com;
4307 donode:
4308 cmdtxt(n);
4309 break;
4310 case NIF:
4311 cmdputs("if ");
4312 cmdtxt(n->nif.test);
4313 cmdputs("; then ");
4314 n = n->nif.ifpart;
4315 if (n->nif.elsepart) {
4316 cmdtxt(n);
4317 cmdputs("; else ");
4318 n = n->nif.elsepart;
4319 }
4320 p = "; fi";
4321 goto dotail;
4322 case NSUBSHELL:
4323 cmdputs("(");
4324 n = n->nredir.n;
4325 p = ")";
4326 goto dotail;
4327 case NWHILE:
4328 p = "while ";
4329 goto until;
4330 case NUNTIL:
4331 p = "until ";
4332 until:
4333 cmdputs(p);
4334 cmdtxt(n->nbinary.ch1);
4335 n = n->nbinary.ch2;
4336 p = "; done";
4337 dodo:
4338 cmdputs("; do ");
4339 dotail:
4340 cmdtxt(n);
4341 goto dotail2;
4342 case NFOR:
4343 cmdputs("for ");
4344 cmdputs(n->nfor.var);
4345 cmdputs(" in ");
4346 cmdlist(n->nfor.args, 1);
4347 n = n->nfor.body;
4348 p = "; done";
4349 goto dodo;
4350 case NDEFUN:
4351 cmdputs(n->narg.text);
4352 p = "() { ... }";
4353 goto dotail2;
4354 case NCMD:
4355 cmdlist(n->ncmd.args, 1);
4356 cmdlist(n->ncmd.redirect, 0);
4357 break;
4358 case NARG:
4359 p = n->narg.text;
4360 dotail2:
4361 cmdputs(p);
4362 break;
4363 case NHERE:
4364 case NXHERE:
4365 p = "<<...";
4366 goto dotail2;
4367 case NCASE:
4368 cmdputs("case ");
4369 cmdputs(n->ncase.expr->narg.text);
4370 cmdputs(" in ");
4371 for (np = n->ncase.cases; np; np = np->nclist.next) {
4372 cmdtxt(np->nclist.pattern);
4373 cmdputs(") ");
4374 cmdtxt(np->nclist.body);
4375 cmdputs(";; ");
4376 }
4377 p = "esac";
4378 goto dotail2;
4379 case NTO:
4380 p = ">";
4381 goto redir;
4382 case NCLOBBER:
4383 p = ">|";
4384 goto redir;
4385 case NAPPEND:
4386 p = ">>";
4387 goto redir;
4388 case NTOFD:
4389 p = ">&";
4390 goto redir;
4391 case NFROM:
4392 p = "<";
4393 goto redir;
4394 case NFROMFD:
4395 p = "<&";
4396 goto redir;
4397 case NFROMTO:
4398 p = "<>";
4399 redir:
4400 s[0] = n->nfile.fd + '0';
4401 s[1] = '\0';
4402 cmdputs(s);
4403 cmdputs(p);
4404 if (n->type == NTOFD || n->type == NFROMFD) {
4405 s[0] = n->ndup.dupfd + '0';
4406 p = s;
4407 goto dotail2;
4408 }
4409 n = n->nfile.fname;
4410 goto donode;
4411 }
4412}
4413
4414static char *
4415commandtext(union node *n)
4416{
4417 char *name;
4418
4419 STARTSTACKSTR(cmdnextc);
4420 cmdtxt(n);
4421 name = stackblock();
4422 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4423 name, cmdnextc, cmdnextc));
4424 return ckstrdup(name);
4425}
4426#endif /* JOBS */
4427
4428/*
4429 * Fork off a subshell. If we are doing job control, give the subshell its
4430 * own process group. Jp is a job structure that the job is to be added to.
4431 * N is the command that will be evaluated by the child. Both jp and n may
4432 * be NULL. The mode parameter can be one of the following:
4433 * FORK_FG - Fork off a foreground process.
4434 * FORK_BG - Fork off a background process.
4435 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4436 * process group even if job control is on.
4437 *
4438 * When job control is turned off, background processes have their standard
4439 * input redirected to /dev/null (except for the second and later processes
4440 * in a pipeline).
4441 *
4442 * Called with interrupts off.
4443 */
4444/*
4445 * Clear traps on a fork.
4446 */
4447static void
4448clear_traps(void)
4449{
4450 char **tp;
4451
4452 for (tp = trap; tp < &trap[NSIG]; tp++) {
4453 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4454 INT_OFF;
4455 free(*tp);
4456 *tp = NULL;
4457 if (tp != &trap[0])
4458 setsignal(tp - trap);
4459 INT_ON;
4460 }
4461 }
4462}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004463
4464/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004465static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004466
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004467/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004468static void
4469forkchild(struct job *jp, union node *n, int mode)
4470{
4471 int oldlvl;
4472
4473 TRACE(("Child shell %d\n", getpid()));
4474 oldlvl = shlvl;
4475 shlvl++;
4476
4477 closescript();
4478 clear_traps();
4479#if JOBS
4480 /* do job control only in root shell */
4481 jobctl = 0;
4482 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4483 pid_t pgrp;
4484
4485 if (jp->nprocs == 0)
4486 pgrp = getpid();
4487 else
4488 pgrp = jp->ps[0].pid;
4489 /* This can fail because we are doing it in the parent also */
4490 (void)setpgid(0, pgrp);
4491 if (mode == FORK_FG)
4492 xtcsetpgrp(ttyfd, pgrp);
4493 setsignal(SIGTSTP);
4494 setsignal(SIGTTOU);
4495 } else
4496#endif
4497 if (mode == FORK_BG) {
4498 ignoresig(SIGINT);
4499 ignoresig(SIGQUIT);
4500 if (jp->nprocs == 0) {
4501 close(0);
4502 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004503 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004504 }
4505 }
4506 if (!oldlvl && iflag) {
4507 setsignal(SIGINT);
4508 setsignal(SIGQUIT);
4509 setsignal(SIGTERM);
4510 }
4511 for (jp = curjob; jp; jp = jp->prev_job)
4512 freejob(jp);
4513 jobless = 0;
4514}
4515
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004516/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004517static void
4518forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4519{
4520 TRACE(("In parent shell: child = %d\n", pid));
4521 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004522 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4523 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524 jobless++;
4525 return;
4526 }
4527#if JOBS
4528 if (mode != FORK_NOJOB && jp->jobctl) {
4529 int pgrp;
4530
4531 if (jp->nprocs == 0)
4532 pgrp = pid;
4533 else
4534 pgrp = jp->ps[0].pid;
4535 /* This can fail because we are doing it in the child also */
4536 setpgid(pid, pgrp);
4537 }
4538#endif
4539 if (mode == FORK_BG) {
4540 backgndpid = pid; /* set $! */
4541 set_curjob(jp, CUR_RUNNING);
4542 }
4543 if (jp) {
4544 struct procstat *ps = &jp->ps[jp->nprocs++];
4545 ps->pid = pid;
4546 ps->status = -1;
4547 ps->cmd = nullstr;
4548#if JOBS
4549 if (jobctl && n)
4550 ps->cmd = commandtext(n);
4551#endif
4552 }
4553}
4554
4555static int
4556forkshell(struct job *jp, union node *n, int mode)
4557{
4558 int pid;
4559
4560 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4561 pid = fork();
4562 if (pid < 0) {
4563 TRACE(("Fork failed, errno=%d", errno));
4564 if (jp)
4565 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004566 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004567 }
4568 if (pid == 0)
4569 forkchild(jp, n, mode);
4570 else
4571 forkparent(jp, n, mode, pid);
4572 return pid;
4573}
4574
4575/*
4576 * Wait for job to finish.
4577 *
4578 * Under job control we have the problem that while a child process is
4579 * running interrupts generated by the user are sent to the child but not
4580 * to the shell. This means that an infinite loop started by an inter-
4581 * active user may be hard to kill. With job control turned off, an
4582 * interactive user may place an interactive program inside a loop. If
4583 * the interactive program catches interrupts, the user doesn't want
4584 * these interrupts to also abort the loop. The approach we take here
4585 * is to have the shell ignore interrupt signals while waiting for a
4586 * foreground process to terminate, and then send itself an interrupt
4587 * signal if the child process was terminated by an interrupt signal.
4588 * Unfortunately, some programs want to do a bit of cleanup and then
4589 * exit on interrupt; unless these processes terminate themselves by
4590 * sending a signal to themselves (instead of calling exit) they will
4591 * confuse this approach.
4592 *
4593 * Called with interrupts off.
4594 */
4595static int
4596waitforjob(struct job *jp)
4597{
4598 int st;
4599
4600 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4601 while (jp->state == JOBRUNNING) {
4602 dowait(DOWAIT_BLOCK, jp);
4603 }
4604 st = getstatus(jp);
4605#if JOBS
4606 if (jp->jobctl) {
4607 xtcsetpgrp(ttyfd, rootpid);
4608 /*
4609 * This is truly gross.
4610 * If we're doing job control, then we did a TIOCSPGRP which
4611 * caused us (the shell) to no longer be in the controlling
4612 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4613 * intuit from the subprocess exit status whether a SIGINT
4614 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4615 */
4616 if (jp->sigint)
4617 raise(SIGINT);
4618 }
4619 if (jp->state == JOBDONE)
4620#endif
4621 freejob(jp);
4622 return st;
4623}
4624
4625/*
4626 * return 1 if there are stopped jobs, otherwise 0
4627 */
4628static int
4629stoppedjobs(void)
4630{
4631 struct job *jp;
4632 int retval;
4633
4634 retval = 0;
4635 if (job_warning)
4636 goto out;
4637 jp = curjob;
4638 if (jp && jp->state == JOBSTOPPED) {
4639 out2str("You have stopped jobs.\n");
4640 job_warning = 2;
4641 retval++;
4642 }
4643 out:
4644 return retval;
4645}
4646
4647
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004648/* ============ redir.c
4649 *
4650 * Code for dealing with input/output redirection.
4651 */
4652
4653#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004654#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004655#ifndef PIPE_BUF
4656# define PIPESIZE 4096 /* amount of buffering in a pipe */
4657#else
4658# define PIPESIZE PIPE_BUF
4659#endif
4660
4661/*
4662 * Open a file in noclobber mode.
4663 * The code was copied from bash.
4664 */
4665static int
4666noclobberopen(const char *fname)
4667{
4668 int r, fd;
4669 struct stat finfo, finfo2;
4670
4671 /*
4672 * If the file exists and is a regular file, return an error
4673 * immediately.
4674 */
4675 r = stat(fname, &finfo);
4676 if (r == 0 && S_ISREG(finfo.st_mode)) {
4677 errno = EEXIST;
4678 return -1;
4679 }
4680
4681 /*
4682 * If the file was not present (r != 0), make sure we open it
4683 * exclusively so that if it is created before we open it, our open
4684 * will fail. Make sure that we do not truncate an existing file.
4685 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4686 * file was not a regular file, we leave O_EXCL off.
4687 */
4688 if (r != 0)
4689 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4690 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4691
4692 /* If the open failed, return the file descriptor right away. */
4693 if (fd < 0)
4694 return fd;
4695
4696 /*
4697 * OK, the open succeeded, but the file may have been changed from a
4698 * non-regular file to a regular file between the stat and the open.
4699 * We are assuming that the O_EXCL open handles the case where FILENAME
4700 * did not exist and is symlinked to an existing file between the stat
4701 * and open.
4702 */
4703
4704 /*
4705 * If we can open it and fstat the file descriptor, and neither check
4706 * revealed that it was a regular file, and the file has not been
4707 * replaced, return the file descriptor.
4708 */
4709 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4710 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4711 return fd;
4712
4713 /* The file has been replaced. badness. */
4714 close(fd);
4715 errno = EEXIST;
4716 return -1;
4717}
4718
4719/*
4720 * Handle here documents. Normally we fork off a process to write the
4721 * data to a pipe. If the document is short, we can stuff the data in
4722 * the pipe without forking.
4723 */
4724/* openhere needs this forward reference */
4725static void expandhere(union node *arg, int fd);
4726static int
4727openhere(union node *redir)
4728{
4729 int pip[2];
4730 size_t len = 0;
4731
4732 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004733 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004734 if (redir->type == NHERE) {
4735 len = strlen(redir->nhere.doc->narg.text);
4736 if (len <= PIPESIZE) {
4737 full_write(pip[1], redir->nhere.doc->narg.text, len);
4738 goto out;
4739 }
4740 }
4741 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4742 close(pip[0]);
4743 signal(SIGINT, SIG_IGN);
4744 signal(SIGQUIT, SIG_IGN);
4745 signal(SIGHUP, SIG_IGN);
4746#ifdef SIGTSTP
4747 signal(SIGTSTP, SIG_IGN);
4748#endif
4749 signal(SIGPIPE, SIG_DFL);
4750 if (redir->type == NHERE)
4751 full_write(pip[1], redir->nhere.doc->narg.text, len);
4752 else
4753 expandhere(redir->nhere.doc, pip[1]);
4754 _exit(0);
4755 }
4756 out:
4757 close(pip[1]);
4758 return pip[0];
4759}
4760
4761static int
4762openredirect(union node *redir)
4763{
4764 char *fname;
4765 int f;
4766
4767 switch (redir->nfile.type) {
4768 case NFROM:
4769 fname = redir->nfile.expfname;
4770 f = open(fname, O_RDONLY);
4771 if (f < 0)
4772 goto eopen;
4773 break;
4774 case NFROMTO:
4775 fname = redir->nfile.expfname;
4776 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4777 if (f < 0)
4778 goto ecreate;
4779 break;
4780 case NTO:
4781 /* Take care of noclobber mode. */
4782 if (Cflag) {
4783 fname = redir->nfile.expfname;
4784 f = noclobberopen(fname);
4785 if (f < 0)
4786 goto ecreate;
4787 break;
4788 }
4789 /* FALLTHROUGH */
4790 case NCLOBBER:
4791 fname = redir->nfile.expfname;
4792 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4793 if (f < 0)
4794 goto ecreate;
4795 break;
4796 case NAPPEND:
4797 fname = redir->nfile.expfname;
4798 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4799 if (f < 0)
4800 goto ecreate;
4801 break;
4802 default:
4803#if DEBUG
4804 abort();
4805#endif
4806 /* Fall through to eliminate warning. */
4807 case NTOFD:
4808 case NFROMFD:
4809 f = -1;
4810 break;
4811 case NHERE:
4812 case NXHERE:
4813 f = openhere(redir);
4814 break;
4815 }
4816
4817 return f;
4818 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004819 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004820 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004821 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004822}
4823
4824/*
4825 * Copy a file descriptor to be >= to. Returns -1
4826 * if the source file descriptor is closed, EMPTY if there are no unused
4827 * file descriptors left.
4828 */
4829static int
4830copyfd(int from, int to)
4831{
4832 int newfd;
4833
4834 newfd = fcntl(from, F_DUPFD, to);
4835 if (newfd < 0) {
4836 if (errno == EMFILE)
4837 return EMPTY;
4838 ash_msg_and_raise_error("%d: %m", from);
4839 }
4840 return newfd;
4841}
4842
4843static void
4844dupredirect(union node *redir, int f)
4845{
4846 int fd = redir->nfile.fd;
4847
4848 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4849 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4850 copyfd(redir->ndup.dupfd, fd);
4851 }
4852 return;
4853 }
4854
4855 if (f != fd) {
4856 copyfd(f, fd);
4857 close(f);
4858 }
4859}
4860
4861/*
4862 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4863 * old file descriptors are stashed away so that the redirection can be
4864 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4865 * standard output, and the standard error if it becomes a duplicate of
4866 * stdout, is saved in memory.
4867 */
4868/* flags passed to redirect */
4869#define REDIR_PUSH 01 /* save previous values of file descriptors */
4870#define REDIR_SAVEFD2 03 /* set preverrout */
4871static void
4872redirect(union node *redir, int flags)
4873{
4874 union node *n;
4875 struct redirtab *sv;
4876 int i;
4877 int fd;
4878 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004879
Denis Vlasenko01631112007-12-16 17:20:38 +00004880 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004881 if (!redir) {
4882 return;
4883 }
4884 sv = NULL;
4885 INT_OFF;
4886 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004887 sv = ckmalloc(sizeof(*sv));
4888 sv->next = redirlist;
4889 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004890 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004891 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004892 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004893 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004894 }
4895 n = redir;
4896 do {
4897 fd = n->nfile.fd;
4898 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4899 && n->ndup.dupfd == fd)
4900 continue; /* redirect from/to same file descriptor */
4901
4902 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004903 if (fd == newfd) {
4904 /* Descriptor wasn't open before redirect.
4905 * Mark it for close in the future */
4906 if (sv && sv->renamed[fd] == EMPTY)
4907 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004908 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004909 }
4910 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004911 i = fcntl(fd, F_DUPFD, 10);
4912
4913 if (i == -1) {
4914 i = errno;
4915 if (i != EBADF) {
4916 close(newfd);
4917 errno = i;
4918 ash_msg_and_raise_error("%d: %m", fd);
4919 /* NOTREACHED */
4920 }
4921 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004922 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004923 close(fd);
4924 }
4925 } else {
4926 close(fd);
4927 }
4928 dupredirect(n, newfd);
4929 } while ((n = n->nfile.next));
4930 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004931 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004932 preverrout_fd = sv->renamed[2];
4933}
4934
4935/*
4936 * Undo the effects of the last redirection.
4937 */
4938static void
4939popredir(int drop)
4940{
4941 struct redirtab *rp;
4942 int i;
4943
Denis Vlasenko01631112007-12-16 17:20:38 +00004944 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004945 return;
4946 INT_OFF;
4947 rp = redirlist;
4948 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004949 if (rp->renamed[i] == CLOSED) {
4950 if (!drop)
4951 close(i);
4952 continue;
4953 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004954 if (rp->renamed[i] != EMPTY) {
4955 if (!drop) {
4956 close(i);
4957 copyfd(rp->renamed[i], i);
4958 }
4959 close(rp->renamed[i]);
4960 }
4961 }
4962 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004963 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004964 free(rp);
4965 INT_ON;
4966}
4967
4968/*
4969 * Undo all redirections. Called on error or interrupt.
4970 */
4971
4972/*
4973 * Discard all saved file descriptors.
4974 */
4975static void
4976clearredir(int drop)
4977{
4978 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004979 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004980 if (!redirlist)
4981 break;
4982 popredir(drop);
4983 }
4984}
4985
4986static int
4987redirectsafe(union node *redir, int flags)
4988{
4989 int err;
4990 volatile int saveint;
4991 struct jmploc *volatile savehandler = exception_handler;
4992 struct jmploc jmploc;
4993
4994 SAVE_INT(saveint);
4995 err = setjmp(jmploc.loc) * 2;
4996 if (!err) {
4997 exception_handler = &jmploc;
4998 redirect(redir, flags);
4999 }
5000 exception_handler = savehandler;
5001 if (err && exception != EXERROR)
5002 longjmp(exception_handler->loc, 1);
5003 RESTORE_INT(saveint);
5004 return err;
5005}
5006
5007
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005008/* ============ Routines to expand arguments to commands
5009 *
5010 * We have to deal with backquotes, shell variables, and file metacharacters.
5011 */
5012
5013/*
5014 * expandarg flags
5015 */
5016#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5017#define EXP_TILDE 0x2 /* do normal tilde expansion */
5018#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5019#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5020#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5021#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5022#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5023#define EXP_WORD 0x80 /* expand word in parameter expansion */
5024#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5025/*
5026 * _rmescape() flags
5027 */
5028#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5029#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5030#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5031#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5032#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5033
5034/*
5035 * Structure specifying which parts of the string should be searched
5036 * for IFS characters.
5037 */
5038struct ifsregion {
5039 struct ifsregion *next; /* next region in list */
5040 int begoff; /* offset of start of region */
5041 int endoff; /* offset of end of region */
5042 int nulonly; /* search for nul bytes only */
5043};
5044
5045struct arglist {
5046 struct strlist *list;
5047 struct strlist **lastp;
5048};
5049
5050/* output of current string */
5051static char *expdest;
5052/* list of back quote expressions */
5053static struct nodelist *argbackq;
5054/* first struct in list of ifs regions */
5055static struct ifsregion ifsfirst;
5056/* last struct in list */
5057static struct ifsregion *ifslastp;
5058/* holds expanded arg list */
5059static struct arglist exparg;
5060
5061/*
5062 * Our own itoa().
5063 */
5064static int
5065cvtnum(arith_t num)
5066{
5067 int len;
5068
5069 expdest = makestrspace(32, expdest);
5070#if ENABLE_ASH_MATH_SUPPORT_64
5071 len = fmtstr(expdest, 32, "%lld", (long long) num);
5072#else
5073 len = fmtstr(expdest, 32, "%ld", num);
5074#endif
5075 STADJUST(len, expdest);
5076 return len;
5077}
5078
5079static size_t
5080esclen(const char *start, const char *p)
5081{
5082 size_t esc = 0;
5083
5084 while (p > start && *--p == CTLESC) {
5085 esc++;
5086 }
5087 return esc;
5088}
5089
5090/*
5091 * Remove any CTLESC characters from a string.
5092 */
5093static char *
5094_rmescapes(char *str, int flag)
5095{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005096 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005097
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005098 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005099 unsigned inquotes;
5100 int notescaped;
5101 int globbing;
5102
5103 p = strpbrk(str, qchars);
5104 if (!p) {
5105 return str;
5106 }
5107 q = p;
5108 r = str;
5109 if (flag & RMESCAPE_ALLOC) {
5110 size_t len = p - str;
5111 size_t fulllen = len + strlen(p) + 1;
5112
5113 if (flag & RMESCAPE_GROW) {
5114 r = makestrspace(fulllen, expdest);
5115 } else if (flag & RMESCAPE_HEAP) {
5116 r = ckmalloc(fulllen);
5117 } else {
5118 r = stalloc(fulllen);
5119 }
5120 q = r;
5121 if (len > 0) {
5122 q = memcpy(q, str, len) + len;
5123 }
5124 }
5125 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5126 globbing = flag & RMESCAPE_GLOB;
5127 notescaped = globbing;
5128 while (*p) {
5129 if (*p == CTLQUOTEMARK) {
5130 inquotes = ~inquotes;
5131 p++;
5132 notescaped = globbing;
5133 continue;
5134 }
5135 if (*p == '\\') {
5136 /* naked back slash */
5137 notescaped = 0;
5138 goto copy;
5139 }
5140 if (*p == CTLESC) {
5141 p++;
5142 if (notescaped && inquotes && *p != '/') {
5143 *q++ = '\\';
5144 }
5145 }
5146 notescaped = globbing;
5147 copy:
5148 *q++ = *p++;
5149 }
5150 *q = '\0';
5151 if (flag & RMESCAPE_GROW) {
5152 expdest = r;
5153 STADJUST(q - r + 1, expdest);
5154 }
5155 return r;
5156}
5157#define rmescapes(p) _rmescapes((p), 0)
5158
5159#define pmatch(a, b) !fnmatch((a), (b), 0)
5160
5161/*
5162 * Prepare a pattern for a expmeta (internal glob(3)) call.
5163 *
5164 * Returns an stalloced string.
5165 */
5166static char *
5167preglob(const char *pattern, int quoted, int flag)
5168{
5169 flag |= RMESCAPE_GLOB;
5170 if (quoted) {
5171 flag |= RMESCAPE_QUOTED;
5172 }
5173 return _rmescapes((char *)pattern, flag);
5174}
5175
5176/*
5177 * Put a string on the stack.
5178 */
5179static void
5180memtodest(const char *p, size_t len, int syntax, int quotes)
5181{
5182 char *q = expdest;
5183
5184 q = makestrspace(len * 2, q);
5185
5186 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005187 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005188 if (!c)
5189 continue;
5190 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5191 USTPUTC(CTLESC, q);
5192 USTPUTC(c, q);
5193 }
5194
5195 expdest = q;
5196}
5197
5198static void
5199strtodest(const char *p, int syntax, int quotes)
5200{
5201 memtodest(p, strlen(p), syntax, quotes);
5202}
5203
5204/*
5205 * Record the fact that we have to scan this region of the
5206 * string for IFS characters.
5207 */
5208static void
5209recordregion(int start, int end, int nulonly)
5210{
5211 struct ifsregion *ifsp;
5212
5213 if (ifslastp == NULL) {
5214 ifsp = &ifsfirst;
5215 } else {
5216 INT_OFF;
5217 ifsp = ckmalloc(sizeof(*ifsp));
5218 ifsp->next = NULL;
5219 ifslastp->next = ifsp;
5220 INT_ON;
5221 }
5222 ifslastp = ifsp;
5223 ifslastp->begoff = start;
5224 ifslastp->endoff = end;
5225 ifslastp->nulonly = nulonly;
5226}
5227
5228static void
5229removerecordregions(int endoff)
5230{
5231 if (ifslastp == NULL)
5232 return;
5233
5234 if (ifsfirst.endoff > endoff) {
5235 while (ifsfirst.next != NULL) {
5236 struct ifsregion *ifsp;
5237 INT_OFF;
5238 ifsp = ifsfirst.next->next;
5239 free(ifsfirst.next);
5240 ifsfirst.next = ifsp;
5241 INT_ON;
5242 }
5243 if (ifsfirst.begoff > endoff)
5244 ifslastp = NULL;
5245 else {
5246 ifslastp = &ifsfirst;
5247 ifsfirst.endoff = endoff;
5248 }
5249 return;
5250 }
5251
5252 ifslastp = &ifsfirst;
5253 while (ifslastp->next && ifslastp->next->begoff < endoff)
5254 ifslastp=ifslastp->next;
5255 while (ifslastp->next != NULL) {
5256 struct ifsregion *ifsp;
5257 INT_OFF;
5258 ifsp = ifslastp->next->next;
5259 free(ifslastp->next);
5260 ifslastp->next = ifsp;
5261 INT_ON;
5262 }
5263 if (ifslastp->endoff > endoff)
5264 ifslastp->endoff = endoff;
5265}
5266
5267static char *
5268exptilde(char *startp, char *p, int flag)
5269{
5270 char c;
5271 char *name;
5272 struct passwd *pw;
5273 const char *home;
5274 int quotes = flag & (EXP_FULL | EXP_CASE);
5275 int startloc;
5276
5277 name = p + 1;
5278
5279 while ((c = *++p) != '\0') {
5280 switch (c) {
5281 case CTLESC:
5282 return startp;
5283 case CTLQUOTEMARK:
5284 return startp;
5285 case ':':
5286 if (flag & EXP_VARTILDE)
5287 goto done;
5288 break;
5289 case '/':
5290 case CTLENDVAR:
5291 goto done;
5292 }
5293 }
5294 done:
5295 *p = '\0';
5296 if (*name == '\0') {
5297 home = lookupvar(homestr);
5298 } else {
5299 pw = getpwnam(name);
5300 if (pw == NULL)
5301 goto lose;
5302 home = pw->pw_dir;
5303 }
5304 if (!home || !*home)
5305 goto lose;
5306 *p = c;
5307 startloc = expdest - (char *)stackblock();
5308 strtodest(home, SQSYNTAX, quotes);
5309 recordregion(startloc, expdest - (char *)stackblock(), 0);
5310 return p;
5311 lose:
5312 *p = c;
5313 return startp;
5314}
5315
5316/*
5317 * Execute a command inside back quotes. If it's a builtin command, we
5318 * want to save its output in a block obtained from malloc. Otherwise
5319 * we fork off a subprocess and get the output of the command via a pipe.
5320 * Should be called with interrupts off.
5321 */
5322struct backcmd { /* result of evalbackcmd */
5323 int fd; /* file descriptor to read from */
5324 char *buf; /* buffer */
5325 int nleft; /* number of chars in buffer */
5326 struct job *jp; /* job structure for command */
5327};
5328
5329/* These forward decls are needed to use "eval" code for backticks handling: */
5330static int back_exitstatus; /* exit status of backquoted command */
5331#define EV_EXIT 01 /* exit after evaluating tree */
5332static void evaltree(union node *, int);
5333
5334static void
5335evalbackcmd(union node *n, struct backcmd *result)
5336{
5337 int saveherefd;
5338
5339 result->fd = -1;
5340 result->buf = NULL;
5341 result->nleft = 0;
5342 result->jp = NULL;
5343 if (n == NULL) {
5344 goto out;
5345 }
5346
5347 saveherefd = herefd;
5348 herefd = -1;
5349
5350 {
5351 int pip[2];
5352 struct job *jp;
5353
5354 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005355 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005356 jp = makejob(n, 1);
5357 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5358 FORCE_INT_ON;
5359 close(pip[0]);
5360 if (pip[1] != 1) {
5361 close(1);
5362 copyfd(pip[1], 1);
5363 close(pip[1]);
5364 }
5365 eflag = 0;
5366 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5367 /* NOTREACHED */
5368 }
5369 close(pip[1]);
5370 result->fd = pip[0];
5371 result->jp = jp;
5372 }
5373 herefd = saveherefd;
5374 out:
5375 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5376 result->fd, result->buf, result->nleft, result->jp));
5377}
5378
5379/*
5380 * Expand stuff in backwards quotes.
5381 */
5382static void
5383expbackq(union node *cmd, int quoted, int quotes)
5384{
5385 struct backcmd in;
5386 int i;
5387 char buf[128];
5388 char *p;
5389 char *dest;
5390 int startloc;
5391 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5392 struct stackmark smark;
5393
5394 INT_OFF;
5395 setstackmark(&smark);
5396 dest = expdest;
5397 startloc = dest - (char *)stackblock();
5398 grabstackstr(dest);
5399 evalbackcmd(cmd, &in);
5400 popstackmark(&smark);
5401
5402 p = in.buf;
5403 i = in.nleft;
5404 if (i == 0)
5405 goto read;
5406 for (;;) {
5407 memtodest(p, i, syntax, quotes);
5408 read:
5409 if (in.fd < 0)
5410 break;
5411 i = safe_read(in.fd, buf, sizeof(buf));
5412 TRACE(("expbackq: read returns %d\n", i));
5413 if (i <= 0)
5414 break;
5415 p = buf;
5416 }
5417
Denis Vlasenko60818682007-09-28 22:07:23 +00005418 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005419 if (in.fd >= 0) {
5420 close(in.fd);
5421 back_exitstatus = waitforjob(in.jp);
5422 }
5423 INT_ON;
5424
5425 /* Eat all trailing newlines */
5426 dest = expdest;
5427 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5428 STUNPUTC(dest);
5429 expdest = dest;
5430
5431 if (quoted == 0)
5432 recordregion(startloc, dest - (char *)stackblock(), 0);
5433 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5434 (dest - (char *)stackblock()) - startloc,
5435 (dest - (char *)stackblock()) - startloc,
5436 stackblock() + startloc));
5437}
5438
5439#if ENABLE_ASH_MATH_SUPPORT
5440/*
5441 * Expand arithmetic expression. Backup to start of expression,
5442 * evaluate, place result in (backed up) result, adjust string position.
5443 */
5444static void
5445expari(int quotes)
5446{
5447 char *p, *start;
5448 int begoff;
5449 int flag;
5450 int len;
5451
5452 /* ifsfree(); */
5453
5454 /*
5455 * This routine is slightly over-complicated for
5456 * efficiency. Next we scan backwards looking for the
5457 * start of arithmetic.
5458 */
5459 start = stackblock();
5460 p = expdest - 1;
5461 *p = '\0';
5462 p--;
5463 do {
5464 int esc;
5465
5466 while (*p != CTLARI) {
5467 p--;
5468#if DEBUG
5469 if (p < start) {
5470 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5471 }
5472#endif
5473 }
5474
5475 esc = esclen(start, p);
5476 if (!(esc % 2)) {
5477 break;
5478 }
5479
5480 p -= esc + 1;
5481 } while (1);
5482
5483 begoff = p - start;
5484
5485 removerecordregions(begoff);
5486
5487 flag = p[1];
5488
5489 expdest = p;
5490
5491 if (quotes)
5492 rmescapes(p + 2);
5493
5494 len = cvtnum(dash_arith(p + 2));
5495
5496 if (flag != '"')
5497 recordregion(begoff, begoff + len, 0);
5498}
5499#endif
5500
5501/* argstr needs it */
5502static char *evalvar(char *p, int flag);
5503
5504/*
5505 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5506 * characters to allow for further processing. Otherwise treat
5507 * $@ like $* since no splitting will be performed.
5508 */
5509static void
5510argstr(char *p, int flag)
5511{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005512 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005513 '=',
5514 ':',
5515 CTLQUOTEMARK,
5516 CTLENDVAR,
5517 CTLESC,
5518 CTLVAR,
5519 CTLBACKQ,
5520 CTLBACKQ | CTLQUOTE,
5521#if ENABLE_ASH_MATH_SUPPORT
5522 CTLENDARI,
5523#endif
5524 0
5525 };
5526 const char *reject = spclchars;
5527 int c;
5528 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5529 int breakall = flag & EXP_WORD;
5530 int inquotes;
5531 size_t length;
5532 int startloc;
5533
5534 if (!(flag & EXP_VARTILDE)) {
5535 reject += 2;
5536 } else if (flag & EXP_VARTILDE2) {
5537 reject++;
5538 }
5539 inquotes = 0;
5540 length = 0;
5541 if (flag & EXP_TILDE) {
5542 char *q;
5543
5544 flag &= ~EXP_TILDE;
5545 tilde:
5546 q = p;
5547 if (*q == CTLESC && (flag & EXP_QWORD))
5548 q++;
5549 if (*q == '~')
5550 p = exptilde(p, q, flag);
5551 }
5552 start:
5553 startloc = expdest - (char *)stackblock();
5554 for (;;) {
5555 length += strcspn(p + length, reject);
5556 c = p[length];
5557 if (c && (!(c & 0x80)
5558#if ENABLE_ASH_MATH_SUPPORT
5559 || c == CTLENDARI
5560#endif
5561 )) {
5562 /* c == '=' || c == ':' || c == CTLENDARI */
5563 length++;
5564 }
5565 if (length > 0) {
5566 int newloc;
5567 expdest = stack_nputstr(p, length, expdest);
5568 newloc = expdest - (char *)stackblock();
5569 if (breakall && !inquotes && newloc > startloc) {
5570 recordregion(startloc, newloc, 0);
5571 }
5572 startloc = newloc;
5573 }
5574 p += length + 1;
5575 length = 0;
5576
5577 switch (c) {
5578 case '\0':
5579 goto breakloop;
5580 case '=':
5581 if (flag & EXP_VARTILDE2) {
5582 p--;
5583 continue;
5584 }
5585 flag |= EXP_VARTILDE2;
5586 reject++;
5587 /* fall through */
5588 case ':':
5589 /*
5590 * sort of a hack - expand tildes in variable
5591 * assignments (after the first '=' and after ':'s).
5592 */
5593 if (*--p == '~') {
5594 goto tilde;
5595 }
5596 continue;
5597 }
5598
5599 switch (c) {
5600 case CTLENDVAR: /* ??? */
5601 goto breakloop;
5602 case CTLQUOTEMARK:
5603 /* "$@" syntax adherence hack */
5604 if (
5605 !inquotes &&
5606 !memcmp(p, dolatstr, 4) &&
5607 (p[4] == CTLQUOTEMARK || (
5608 p[4] == CTLENDVAR &&
5609 p[5] == CTLQUOTEMARK
5610 ))
5611 ) {
5612 p = evalvar(p + 1, flag) + 1;
5613 goto start;
5614 }
5615 inquotes = !inquotes;
5616 addquote:
5617 if (quotes) {
5618 p--;
5619 length++;
5620 startloc++;
5621 }
5622 break;
5623 case CTLESC:
5624 startloc++;
5625 length++;
5626 goto addquote;
5627 case CTLVAR:
5628 p = evalvar(p, flag);
5629 goto start;
5630 case CTLBACKQ:
5631 c = 0;
5632 case CTLBACKQ|CTLQUOTE:
5633 expbackq(argbackq->n, c, quotes);
5634 argbackq = argbackq->next;
5635 goto start;
5636#if ENABLE_ASH_MATH_SUPPORT
5637 case CTLENDARI:
5638 p--;
5639 expari(quotes);
5640 goto start;
5641#endif
5642 }
5643 }
5644 breakloop:
5645 ;
5646}
5647
5648static char *
5649scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5650 int zero)
5651{
5652 char *loc;
5653 char *loc2;
5654 char c;
5655
5656 loc = startp;
5657 loc2 = rmesc;
5658 do {
5659 int match;
5660 const char *s = loc2;
5661 c = *loc2;
5662 if (zero) {
5663 *loc2 = '\0';
5664 s = rmesc;
5665 }
5666 match = pmatch(str, s);
5667 *loc2 = c;
5668 if (match)
5669 return loc;
5670 if (quotes && *loc == CTLESC)
5671 loc++;
5672 loc++;
5673 loc2++;
5674 } while (c);
5675 return 0;
5676}
5677
5678static char *
5679scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5680 int zero)
5681{
5682 int esc = 0;
5683 char *loc;
5684 char *loc2;
5685
5686 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5687 int match;
5688 char c = *loc2;
5689 const char *s = loc2;
5690 if (zero) {
5691 *loc2 = '\0';
5692 s = rmesc;
5693 }
5694 match = pmatch(str, s);
5695 *loc2 = c;
5696 if (match)
5697 return loc;
5698 loc--;
5699 if (quotes) {
5700 if (--esc < 0) {
5701 esc = esclen(startp, loc);
5702 }
5703 if (esc % 2) {
5704 esc--;
5705 loc--;
5706 }
5707 }
5708 }
5709 return 0;
5710}
5711
5712static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5713static void
5714varunset(const char *end, const char *var, const char *umsg, int varflags)
5715{
5716 const char *msg;
5717 const char *tail;
5718
5719 tail = nullstr;
5720 msg = "parameter not set";
5721 if (umsg) {
5722 if (*end == CTLENDVAR) {
5723 if (varflags & VSNUL)
5724 tail = " or null";
5725 } else
5726 msg = umsg;
5727 }
5728 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5729}
5730
5731static const char *
5732subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5733{
5734 char *startp;
5735 char *loc;
5736 int saveherefd = herefd;
5737 struct nodelist *saveargbackq = argbackq;
5738 int amount;
5739 char *rmesc, *rmescend;
5740 int zero;
5741 char *(*scan)(char *, char *, char *, char *, int , int);
5742
5743 herefd = -1;
5744 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5745 STPUTC('\0', expdest);
5746 herefd = saveherefd;
5747 argbackq = saveargbackq;
5748 startp = stackblock() + startloc;
5749
5750 switch (subtype) {
5751 case VSASSIGN:
5752 setvar(str, startp, 0);
5753 amount = startp - expdest;
5754 STADJUST(amount, expdest);
5755 return startp;
5756
5757 case VSQUESTION:
5758 varunset(p, str, startp, varflags);
5759 /* NOTREACHED */
5760 }
5761
5762 subtype -= VSTRIMRIGHT;
5763#if DEBUG
5764 if (subtype < 0 || subtype > 3)
5765 abort();
5766#endif
5767
5768 rmesc = startp;
5769 rmescend = stackblock() + strloc;
5770 if (quotes) {
5771 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5772 if (rmesc != startp) {
5773 rmescend = expdest;
5774 startp = stackblock() + startloc;
5775 }
5776 }
5777 rmescend--;
5778 str = stackblock() + strloc;
5779 preglob(str, varflags & VSQUOTE, 0);
5780
5781 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5782 zero = subtype >> 1;
5783 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5784 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5785
5786 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5787 if (loc) {
5788 if (zero) {
5789 memmove(startp, loc, str - loc);
5790 loc = startp + (str - loc) - 1;
5791 }
5792 *loc = '\0';
5793 amount = loc - expdest;
5794 STADJUST(amount, expdest);
5795 }
5796 return loc;
5797}
5798
5799/*
5800 * Add the value of a specialized variable to the stack string.
5801 */
5802static ssize_t
5803varvalue(char *name, int varflags, int flags)
5804{
5805 int num;
5806 char *p;
5807 int i;
5808 int sep = 0;
5809 int sepq = 0;
5810 ssize_t len = 0;
5811 char **ap;
5812 int syntax;
5813 int quoted = varflags & VSQUOTE;
5814 int subtype = varflags & VSTYPE;
5815 int quotes = flags & (EXP_FULL | EXP_CASE);
5816
5817 if (quoted && (flags & EXP_FULL))
5818 sep = 1 << CHAR_BIT;
5819
5820 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5821 switch (*name) {
5822 case '$':
5823 num = rootpid;
5824 goto numvar;
5825 case '?':
5826 num = exitstatus;
5827 goto numvar;
5828 case '#':
5829 num = shellparam.nparam;
5830 goto numvar;
5831 case '!':
5832 num = backgndpid;
5833 if (num == 0)
5834 return -1;
5835 numvar:
5836 len = cvtnum(num);
5837 break;
5838 case '-':
5839 p = makestrspace(NOPTS, expdest);
5840 for (i = NOPTS - 1; i >= 0; i--) {
5841 if (optlist[i]) {
5842 USTPUTC(optletters(i), p);
5843 len++;
5844 }
5845 }
5846 expdest = p;
5847 break;
5848 case '@':
5849 if (sep)
5850 goto param;
5851 /* fall through */
5852 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005853 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005854 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5855 sepq = 1;
5856 param:
5857 ap = shellparam.p;
5858 if (!ap)
5859 return -1;
5860 while ((p = *ap++)) {
5861 size_t partlen;
5862
5863 partlen = strlen(p);
5864 len += partlen;
5865
5866 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5867 memtodest(p, partlen, syntax, quotes);
5868
5869 if (*ap && sep) {
5870 char *q;
5871
5872 len++;
5873 if (subtype == VSPLUS || subtype == VSLENGTH) {
5874 continue;
5875 }
5876 q = expdest;
5877 if (sepq)
5878 STPUTC(CTLESC, q);
5879 STPUTC(sep, q);
5880 expdest = q;
5881 }
5882 }
5883 return len;
5884 case '0':
5885 case '1':
5886 case '2':
5887 case '3':
5888 case '4':
5889 case '5':
5890 case '6':
5891 case '7':
5892 case '8':
5893 case '9':
5894 num = atoi(name);
5895 if (num < 0 || num > shellparam.nparam)
5896 return -1;
5897 p = num ? shellparam.p[num - 1] : arg0;
5898 goto value;
5899 default:
5900 p = lookupvar(name);
5901 value:
5902 if (!p)
5903 return -1;
5904
5905 len = strlen(p);
5906 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5907 memtodest(p, len, syntax, quotes);
5908 return len;
5909 }
5910
5911 if (subtype == VSPLUS || subtype == VSLENGTH)
5912 STADJUST(-len, expdest);
5913 return len;
5914}
5915
5916/*
5917 * Expand a variable, and return a pointer to the next character in the
5918 * input string.
5919 */
5920static char *
5921evalvar(char *p, int flag)
5922{
5923 int subtype;
5924 int varflags;
5925 char *var;
5926 int patloc;
5927 int c;
5928 int startloc;
5929 ssize_t varlen;
5930 int easy;
5931 int quotes;
5932 int quoted;
5933
5934 quotes = flag & (EXP_FULL | EXP_CASE);
5935 varflags = *p++;
5936 subtype = varflags & VSTYPE;
5937 quoted = varflags & VSQUOTE;
5938 var = p;
5939 easy = (!quoted || (*var == '@' && shellparam.nparam));
5940 startloc = expdest - (char *)stackblock();
5941 p = strchr(p, '=') + 1;
5942
5943 again:
5944 varlen = varvalue(var, varflags, flag);
5945 if (varflags & VSNUL)
5946 varlen--;
5947
5948 if (subtype == VSPLUS) {
5949 varlen = -1 - varlen;
5950 goto vsplus;
5951 }
5952
5953 if (subtype == VSMINUS) {
5954 vsplus:
5955 if (varlen < 0) {
5956 argstr(
5957 p, flag | EXP_TILDE |
5958 (quoted ? EXP_QWORD : EXP_WORD)
5959 );
5960 goto end;
5961 }
5962 if (easy)
5963 goto record;
5964 goto end;
5965 }
5966
5967 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5968 if (varlen < 0) {
5969 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5970 varflags &= ~VSNUL;
5971 /*
5972 * Remove any recorded regions beyond
5973 * start of variable
5974 */
5975 removerecordregions(startloc);
5976 goto again;
5977 }
5978 goto end;
5979 }
5980 if (easy)
5981 goto record;
5982 goto end;
5983 }
5984
5985 if (varlen < 0 && uflag)
5986 varunset(p, var, 0, 0);
5987
5988 if (subtype == VSLENGTH) {
5989 cvtnum(varlen > 0 ? varlen : 0);
5990 goto record;
5991 }
5992
5993 if (subtype == VSNORMAL) {
5994 if (!easy)
5995 goto end;
5996 record:
5997 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5998 goto end;
5999 }
6000
6001#if DEBUG
6002 switch (subtype) {
6003 case VSTRIMLEFT:
6004 case VSTRIMLEFTMAX:
6005 case VSTRIMRIGHT:
6006 case VSTRIMRIGHTMAX:
6007 break;
6008 default:
6009 abort();
6010 }
6011#endif
6012
6013 if (varlen >= 0) {
6014 /*
6015 * Terminate the string and start recording the pattern
6016 * right after it
6017 */
6018 STPUTC('\0', expdest);
6019 patloc = expdest - (char *)stackblock();
6020 if (subevalvar(p, NULL, patloc, subtype,
6021 startloc, varflags, quotes) == 0) {
6022 int amount = expdest - (
6023 (char *)stackblock() + patloc - 1
6024 );
6025 STADJUST(-amount, expdest);
6026 }
6027 /* Remove any recorded regions beyond start of variable */
6028 removerecordregions(startloc);
6029 goto record;
6030 }
6031
6032 end:
6033 if (subtype != VSNORMAL) { /* skip to end of alternative */
6034 int nesting = 1;
6035 for (;;) {
6036 c = *p++;
6037 if (c == CTLESC)
6038 p++;
6039 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6040 if (varlen >= 0)
6041 argbackq = argbackq->next;
6042 } else if (c == CTLVAR) {
6043 if ((*p++ & VSTYPE) != VSNORMAL)
6044 nesting++;
6045 } else if (c == CTLENDVAR) {
6046 if (--nesting == 0)
6047 break;
6048 }
6049 }
6050 }
6051 return p;
6052}
6053
6054/*
6055 * Break the argument string into pieces based upon IFS and add the
6056 * strings to the argument list. The regions of the string to be
6057 * searched for IFS characters have been stored by recordregion.
6058 */
6059static void
6060ifsbreakup(char *string, struct arglist *arglist)
6061{
6062 struct ifsregion *ifsp;
6063 struct strlist *sp;
6064 char *start;
6065 char *p;
6066 char *q;
6067 const char *ifs, *realifs;
6068 int ifsspc;
6069 int nulonly;
6070
6071 start = string;
6072 if (ifslastp != NULL) {
6073 ifsspc = 0;
6074 nulonly = 0;
6075 realifs = ifsset() ? ifsval() : defifs;
6076 ifsp = &ifsfirst;
6077 do {
6078 p = string + ifsp->begoff;
6079 nulonly = ifsp->nulonly;
6080 ifs = nulonly ? nullstr : realifs;
6081 ifsspc = 0;
6082 while (p < string + ifsp->endoff) {
6083 q = p;
6084 if (*p == CTLESC)
6085 p++;
6086 if (!strchr(ifs, *p)) {
6087 p++;
6088 continue;
6089 }
6090 if (!nulonly)
6091 ifsspc = (strchr(defifs, *p) != NULL);
6092 /* Ignore IFS whitespace at start */
6093 if (q == start && ifsspc) {
6094 p++;
6095 start = p;
6096 continue;
6097 }
6098 *q = '\0';
6099 sp = stalloc(sizeof(*sp));
6100 sp->text = start;
6101 *arglist->lastp = sp;
6102 arglist->lastp = &sp->next;
6103 p++;
6104 if (!nulonly) {
6105 for (;;) {
6106 if (p >= string + ifsp->endoff) {
6107 break;
6108 }
6109 q = p;
6110 if (*p == CTLESC)
6111 p++;
6112 if (strchr(ifs, *p) == NULL ) {
6113 p = q;
6114 break;
6115 } else if (strchr(defifs, *p) == NULL) {
6116 if (ifsspc) {
6117 p++;
6118 ifsspc = 0;
6119 } else {
6120 p = q;
6121 break;
6122 }
6123 } else
6124 p++;
6125 }
6126 }
6127 start = p;
6128 } /* while */
6129 ifsp = ifsp->next;
6130 } while (ifsp != NULL);
6131 if (nulonly)
6132 goto add;
6133 }
6134
6135 if (!*start)
6136 return;
6137
6138 add:
6139 sp = stalloc(sizeof(*sp));
6140 sp->text = start;
6141 *arglist->lastp = sp;
6142 arglist->lastp = &sp->next;
6143}
6144
6145static void
6146ifsfree(void)
6147{
6148 struct ifsregion *p;
6149
6150 INT_OFF;
6151 p = ifsfirst.next;
6152 do {
6153 struct ifsregion *ifsp;
6154 ifsp = p->next;
6155 free(p);
6156 p = ifsp;
6157 } while (p);
6158 ifslastp = NULL;
6159 ifsfirst.next = NULL;
6160 INT_ON;
6161}
6162
6163/*
6164 * Add a file name to the list.
6165 */
6166static void
6167addfname(const char *name)
6168{
6169 struct strlist *sp;
6170
6171 sp = stalloc(sizeof(*sp));
6172 sp->text = ststrdup(name);
6173 *exparg.lastp = sp;
6174 exparg.lastp = &sp->next;
6175}
6176
6177static char *expdir;
6178
6179/*
6180 * Do metacharacter (i.e. *, ?, [...]) expansion.
6181 */
6182static void
6183expmeta(char *enddir, char *name)
6184{
6185 char *p;
6186 const char *cp;
6187 char *start;
6188 char *endname;
6189 int metaflag;
6190 struct stat statb;
6191 DIR *dirp;
6192 struct dirent *dp;
6193 int atend;
6194 int matchdot;
6195
6196 metaflag = 0;
6197 start = name;
6198 for (p = name; *p; p++) {
6199 if (*p == '*' || *p == '?')
6200 metaflag = 1;
6201 else if (*p == '[') {
6202 char *q = p + 1;
6203 if (*q == '!')
6204 q++;
6205 for (;;) {
6206 if (*q == '\\')
6207 q++;
6208 if (*q == '/' || *q == '\0')
6209 break;
6210 if (*++q == ']') {
6211 metaflag = 1;
6212 break;
6213 }
6214 }
6215 } else if (*p == '\\')
6216 p++;
6217 else if (*p == '/') {
6218 if (metaflag)
6219 goto out;
6220 start = p + 1;
6221 }
6222 }
6223 out:
6224 if (metaflag == 0) { /* we've reached the end of the file name */
6225 if (enddir != expdir)
6226 metaflag++;
6227 p = name;
6228 do {
6229 if (*p == '\\')
6230 p++;
6231 *enddir++ = *p;
6232 } while (*p++);
6233 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6234 addfname(expdir);
6235 return;
6236 }
6237 endname = p;
6238 if (name < start) {
6239 p = name;
6240 do {
6241 if (*p == '\\')
6242 p++;
6243 *enddir++ = *p++;
6244 } while (p < start);
6245 }
6246 if (enddir == expdir) {
6247 cp = ".";
6248 } else if (enddir == expdir + 1 && *expdir == '/') {
6249 cp = "/";
6250 } else {
6251 cp = expdir;
6252 enddir[-1] = '\0';
6253 }
6254 dirp = opendir(cp);
6255 if (dirp == NULL)
6256 return;
6257 if (enddir != expdir)
6258 enddir[-1] = '/';
6259 if (*endname == 0) {
6260 atend = 1;
6261 } else {
6262 atend = 0;
6263 *endname++ = '\0';
6264 }
6265 matchdot = 0;
6266 p = start;
6267 if (*p == '\\')
6268 p++;
6269 if (*p == '.')
6270 matchdot++;
6271 while (! intpending && (dp = readdir(dirp)) != NULL) {
6272 if (dp->d_name[0] == '.' && ! matchdot)
6273 continue;
6274 if (pmatch(start, dp->d_name)) {
6275 if (atend) {
6276 strcpy(enddir, dp->d_name);
6277 addfname(expdir);
6278 } else {
6279 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6280 continue;
6281 p[-1] = '/';
6282 expmeta(p, endname);
6283 }
6284 }
6285 }
6286 closedir(dirp);
6287 if (! atend)
6288 endname[-1] = '/';
6289}
6290
6291static struct strlist *
6292msort(struct strlist *list, int len)
6293{
6294 struct strlist *p, *q = NULL;
6295 struct strlist **lpp;
6296 int half;
6297 int n;
6298
6299 if (len <= 1)
6300 return list;
6301 half = len >> 1;
6302 p = list;
6303 for (n = half; --n >= 0; ) {
6304 q = p;
6305 p = p->next;
6306 }
6307 q->next = NULL; /* terminate first half of list */
6308 q = msort(list, half); /* sort first half of list */
6309 p = msort(p, len - half); /* sort second half */
6310 lpp = &list;
6311 for (;;) {
6312#if ENABLE_LOCALE_SUPPORT
6313 if (strcoll(p->text, q->text) < 0)
6314#else
6315 if (strcmp(p->text, q->text) < 0)
6316#endif
6317 {
6318 *lpp = p;
6319 lpp = &p->next;
6320 p = *lpp;
6321 if (p == NULL) {
6322 *lpp = q;
6323 break;
6324 }
6325 } else {
6326 *lpp = q;
6327 lpp = &q->next;
6328 q = *lpp;
6329 if (q == NULL) {
6330 *lpp = p;
6331 break;
6332 }
6333 }
6334 }
6335 return list;
6336}
6337
6338/*
6339 * Sort the results of file name expansion. It calculates the number of
6340 * strings to sort and then calls msort (short for merge sort) to do the
6341 * work.
6342 */
6343static struct strlist *
6344expsort(struct strlist *str)
6345{
6346 int len;
6347 struct strlist *sp;
6348
6349 len = 0;
6350 for (sp = str; sp; sp = sp->next)
6351 len++;
6352 return msort(str, len);
6353}
6354
6355static void
6356expandmeta(struct strlist *str, int flag)
6357{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006358 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006359 '*', '?', '[', 0
6360 };
6361 /* TODO - EXP_REDIR */
6362
6363 while (str) {
6364 struct strlist **savelastp;
6365 struct strlist *sp;
6366 char *p;
6367
6368 if (fflag)
6369 goto nometa;
6370 if (!strpbrk(str->text, metachars))
6371 goto nometa;
6372 savelastp = exparg.lastp;
6373
6374 INT_OFF;
6375 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6376 {
6377 int i = strlen(str->text);
6378 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6379 }
6380
6381 expmeta(expdir, p);
6382 free(expdir);
6383 if (p != str->text)
6384 free(p);
6385 INT_ON;
6386 if (exparg.lastp == savelastp) {
6387 /*
6388 * no matches
6389 */
6390 nometa:
6391 *exparg.lastp = str;
6392 rmescapes(str->text);
6393 exparg.lastp = &str->next;
6394 } else {
6395 *exparg.lastp = NULL;
6396 *savelastp = sp = expsort(*savelastp);
6397 while (sp->next != NULL)
6398 sp = sp->next;
6399 exparg.lastp = &sp->next;
6400 }
6401 str = str->next;
6402 }
6403}
6404
6405/*
6406 * Perform variable substitution and command substitution on an argument,
6407 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6408 * perform splitting and file name expansion. When arglist is NULL, perform
6409 * here document expansion.
6410 */
6411static void
6412expandarg(union node *arg, struct arglist *arglist, int flag)
6413{
6414 struct strlist *sp;
6415 char *p;
6416
6417 argbackq = arg->narg.backquote;
6418 STARTSTACKSTR(expdest);
6419 ifsfirst.next = NULL;
6420 ifslastp = NULL;
6421 argstr(arg->narg.text, flag);
6422 p = _STPUTC('\0', expdest);
6423 expdest = p - 1;
6424 if (arglist == NULL) {
6425 return; /* here document expanded */
6426 }
6427 p = grabstackstr(p);
6428 exparg.lastp = &exparg.list;
6429 /*
6430 * TODO - EXP_REDIR
6431 */
6432 if (flag & EXP_FULL) {
6433 ifsbreakup(p, &exparg);
6434 *exparg.lastp = NULL;
6435 exparg.lastp = &exparg.list;
6436 expandmeta(exparg.list, flag);
6437 } else {
6438 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6439 rmescapes(p);
6440 sp = stalloc(sizeof(*sp));
6441 sp->text = p;
6442 *exparg.lastp = sp;
6443 exparg.lastp = &sp->next;
6444 }
6445 if (ifsfirst.next)
6446 ifsfree();
6447 *exparg.lastp = NULL;
6448 if (exparg.list) {
6449 *arglist->lastp = exparg.list;
6450 arglist->lastp = exparg.lastp;
6451 }
6452}
6453
6454/*
6455 * Expand shell variables and backquotes inside a here document.
6456 */
6457static void
6458expandhere(union node *arg, int fd)
6459{
6460 herefd = fd;
6461 expandarg(arg, (struct arglist *)NULL, 0);
6462 full_write(fd, stackblock(), expdest - (char *)stackblock());
6463}
6464
6465/*
6466 * Returns true if the pattern matches the string.
6467 */
6468static int
6469patmatch(char *pattern, const char *string)
6470{
6471 return pmatch(preglob(pattern, 0, 0), string);
6472}
6473
6474/*
6475 * See if a pattern matches in a case statement.
6476 */
6477static int
6478casematch(union node *pattern, char *val)
6479{
6480 struct stackmark smark;
6481 int result;
6482
6483 setstackmark(&smark);
6484 argbackq = pattern->narg.backquote;
6485 STARTSTACKSTR(expdest);
6486 ifslastp = NULL;
6487 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6488 STACKSTRNUL(expdest);
6489 result = patmatch(stackblock(), val);
6490 popstackmark(&smark);
6491 return result;
6492}
6493
6494
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006495/* ============ find_command */
6496
6497struct builtincmd {
6498 const char *name;
6499 int (*builtin)(int, char **);
6500 /* unsigned flags; */
6501};
6502#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6503#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6504#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6505
6506struct cmdentry {
6507 int cmdtype;
6508 union param {
6509 int index;
6510 const struct builtincmd *cmd;
6511 struct funcnode *func;
6512 } u;
6513};
6514/* values of cmdtype */
6515#define CMDUNKNOWN -1 /* no entry in table for command */
6516#define CMDNORMAL 0 /* command is an executable program */
6517#define CMDFUNCTION 1 /* command is a shell function */
6518#define CMDBUILTIN 2 /* command is a shell builtin */
6519
6520/* action to find_command() */
6521#define DO_ERR 0x01 /* prints errors */
6522#define DO_ABS 0x02 /* checks absolute paths */
6523#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6524#define DO_ALTPATH 0x08 /* using alternate path */
6525#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6526
6527static void find_command(char *, struct cmdentry *, int, const char *);
6528
6529
6530/* ============ Hashing commands */
6531
6532/*
6533 * When commands are first encountered, they are entered in a hash table.
6534 * This ensures that a full path search will not have to be done for them
6535 * on each invocation.
6536 *
6537 * We should investigate converting to a linear search, even though that
6538 * would make the command name "hash" a misnomer.
6539 */
6540
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006541#define ARB 1 /* actual size determined at run time */
6542
6543struct tblentry {
6544 struct tblentry *next; /* next entry in hash chain */
6545 union param param; /* definition of builtin function */
6546 short cmdtype; /* index identifying command */
6547 char rehash; /* if set, cd done since entry created */
6548 char cmdname[ARB]; /* name of command */
6549};
6550
Denis Vlasenko01631112007-12-16 17:20:38 +00006551static struct tblentry **cmdtable;
6552#define INIT_G_cmdtable() do { \
6553 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6554} while (0)
6555
6556static int builtinloc = -1; /* index in path of %builtin, or -1 */
6557
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006558
6559static void
6560tryexec(char *cmd, char **argv, char **envp)
6561{
6562 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006563
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006564#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006565 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006566 int a = find_applet_by_name(cmd);
6567 if (a >= 0) {
6568 if (APPLET_IS_NOEXEC(a))
6569 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006570 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006571 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006572 /* If they called chroot or otherwise made the binary no longer
6573 * executable, fall through */
6574 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006575 }
6576#endif
6577
6578 repeat:
6579#ifdef SYSV
6580 do {
6581 execve(cmd, argv, envp);
6582 } while (errno == EINTR);
6583#else
6584 execve(cmd, argv, envp);
6585#endif
6586 if (repeated++) {
6587 free(argv);
6588 } else if (errno == ENOEXEC) {
6589 char **ap;
6590 char **new;
6591
6592 for (ap = argv; *ap; ap++)
6593 ;
6594 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6595 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006596 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006597 ap += 2;
6598 argv++;
6599 while ((*ap++ = *argv++))
6600 ;
6601 argv = new;
6602 goto repeat;
6603 }
6604}
6605
6606/*
6607 * Exec a program. Never returns. If you change this routine, you may
6608 * have to change the find_command routine as well.
6609 */
6610#define environment() listvars(VEXPORT, VUNSET, 0)
6611static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6612static void
6613shellexec(char **argv, const char *path, int idx)
6614{
6615 char *cmdname;
6616 int e;
6617 char **envp;
6618 int exerrno;
6619
6620 clearredir(1);
6621 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006622 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006623#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006624 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006625#endif
6626 ) {
6627 tryexec(argv[0], argv, envp);
6628 e = errno;
6629 } else {
6630 e = ENOENT;
6631 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6632 if (--idx < 0 && pathopt == NULL) {
6633 tryexec(cmdname, argv, envp);
6634 if (errno != ENOENT && errno != ENOTDIR)
6635 e = errno;
6636 }
6637 stunalloc(cmdname);
6638 }
6639 }
6640
6641 /* Map to POSIX errors */
6642 switch (e) {
6643 case EACCES:
6644 exerrno = 126;
6645 break;
6646 case ENOENT:
6647 exerrno = 127;
6648 break;
6649 default:
6650 exerrno = 2;
6651 break;
6652 }
6653 exitstatus = exerrno;
6654 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6655 argv[0], e, suppressint ));
6656 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6657 /* NOTREACHED */
6658}
6659
6660static void
6661printentry(struct tblentry *cmdp)
6662{
6663 int idx;
6664 const char *path;
6665 char *name;
6666
6667 idx = cmdp->param.index;
6668 path = pathval();
6669 do {
6670 name = padvance(&path, cmdp->cmdname);
6671 stunalloc(name);
6672 } while (--idx >= 0);
6673 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6674}
6675
6676/*
6677 * Clear out command entries. The argument specifies the first entry in
6678 * PATH which has changed.
6679 */
6680static void
6681clearcmdentry(int firstchange)
6682{
6683 struct tblentry **tblp;
6684 struct tblentry **pp;
6685 struct tblentry *cmdp;
6686
6687 INT_OFF;
6688 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6689 pp = tblp;
6690 while ((cmdp = *pp) != NULL) {
6691 if ((cmdp->cmdtype == CMDNORMAL &&
6692 cmdp->param.index >= firstchange)
6693 || (cmdp->cmdtype == CMDBUILTIN &&
6694 builtinloc >= firstchange)
6695 ) {
6696 *pp = cmdp->next;
6697 free(cmdp);
6698 } else {
6699 pp = &cmdp->next;
6700 }
6701 }
6702 }
6703 INT_ON;
6704}
6705
6706/*
6707 * Locate a command in the command hash table. If "add" is nonzero,
6708 * add the command to the table if it is not already present. The
6709 * variable "lastcmdentry" is set to point to the address of the link
6710 * pointing to the entry, so that delete_cmd_entry can delete the
6711 * entry.
6712 *
6713 * Interrupts must be off if called with add != 0.
6714 */
6715static struct tblentry **lastcmdentry;
6716
6717static struct tblentry *
6718cmdlookup(const char *name, int add)
6719{
6720 unsigned int hashval;
6721 const char *p;
6722 struct tblentry *cmdp;
6723 struct tblentry **pp;
6724
6725 p = name;
6726 hashval = (unsigned char)*p << 4;
6727 while (*p)
6728 hashval += (unsigned char)*p++;
6729 hashval &= 0x7FFF;
6730 pp = &cmdtable[hashval % CMDTABLESIZE];
6731 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6732 if (strcmp(cmdp->cmdname, name) == 0)
6733 break;
6734 pp = &cmdp->next;
6735 }
6736 if (add && cmdp == NULL) {
6737 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6738 + strlen(name) + 1);
6739 cmdp->next = NULL;
6740 cmdp->cmdtype = CMDUNKNOWN;
6741 strcpy(cmdp->cmdname, name);
6742 }
6743 lastcmdentry = pp;
6744 return cmdp;
6745}
6746
6747/*
6748 * Delete the command entry returned on the last lookup.
6749 */
6750static void
6751delete_cmd_entry(void)
6752{
6753 struct tblentry *cmdp;
6754
6755 INT_OFF;
6756 cmdp = *lastcmdentry;
6757 *lastcmdentry = cmdp->next;
6758 if (cmdp->cmdtype == CMDFUNCTION)
6759 freefunc(cmdp->param.func);
6760 free(cmdp);
6761 INT_ON;
6762}
6763
6764/*
6765 * Add a new command entry, replacing any existing command entry for
6766 * the same name - except special builtins.
6767 */
6768static void
6769addcmdentry(char *name, struct cmdentry *entry)
6770{
6771 struct tblentry *cmdp;
6772
6773 cmdp = cmdlookup(name, 1);
6774 if (cmdp->cmdtype == CMDFUNCTION) {
6775 freefunc(cmdp->param.func);
6776 }
6777 cmdp->cmdtype = entry->cmdtype;
6778 cmdp->param = entry->u;
6779 cmdp->rehash = 0;
6780}
6781
6782static int
6783hashcmd(int argc, char **argv)
6784{
6785 struct tblentry **pp;
6786 struct tblentry *cmdp;
6787 int c;
6788 struct cmdentry entry;
6789 char *name;
6790
6791 while ((c = nextopt("r")) != '\0') {
6792 clearcmdentry(0);
6793 return 0;
6794 }
6795 if (*argptr == NULL) {
6796 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6797 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6798 if (cmdp->cmdtype == CMDNORMAL)
6799 printentry(cmdp);
6800 }
6801 }
6802 return 0;
6803 }
6804 c = 0;
6805 while ((name = *argptr) != NULL) {
6806 cmdp = cmdlookup(name, 0);
6807 if (cmdp != NULL
6808 && (cmdp->cmdtype == CMDNORMAL
6809 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6810 delete_cmd_entry();
6811 find_command(name, &entry, DO_ERR, pathval());
6812 if (entry.cmdtype == CMDUNKNOWN)
6813 c = 1;
6814 argptr++;
6815 }
6816 return c;
6817}
6818
6819/*
6820 * Called when a cd is done. Marks all commands so the next time they
6821 * are executed they will be rehashed.
6822 */
6823static void
6824hashcd(void)
6825{
6826 struct tblentry **pp;
6827 struct tblentry *cmdp;
6828
6829 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6830 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6831 if (cmdp->cmdtype == CMDNORMAL || (
6832 cmdp->cmdtype == CMDBUILTIN &&
6833 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6834 builtinloc > 0
6835 ))
6836 cmdp->rehash = 1;
6837 }
6838 }
6839}
6840
6841/*
6842 * Fix command hash table when PATH changed.
6843 * Called before PATH is changed. The argument is the new value of PATH;
6844 * pathval() still returns the old value at this point.
6845 * Called with interrupts off.
6846 */
6847static void
6848changepath(const char *newval)
6849{
6850 const char *old, *new;
6851 int idx;
6852 int firstchange;
6853 int idx_bltin;
6854
6855 old = pathval();
6856 new = newval;
6857 firstchange = 9999; /* assume no change */
6858 idx = 0;
6859 idx_bltin = -1;
6860 for (;;) {
6861 if (*old != *new) {
6862 firstchange = idx;
6863 if ((*old == '\0' && *new == ':')
6864 || (*old == ':' && *new == '\0'))
6865 firstchange++;
6866 old = new; /* ignore subsequent differences */
6867 }
6868 if (*new == '\0')
6869 break;
6870 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6871 idx_bltin = idx;
6872 if (*new == ':') {
6873 idx++;
6874 }
6875 new++, old++;
6876 }
6877 if (builtinloc < 0 && idx_bltin >= 0)
6878 builtinloc = idx_bltin; /* zap builtins */
6879 if (builtinloc >= 0 && idx_bltin < 0)
6880 firstchange = 0;
6881 clearcmdentry(firstchange);
6882 builtinloc = idx_bltin;
6883}
6884
6885#define TEOF 0
6886#define TNL 1
6887#define TREDIR 2
6888#define TWORD 3
6889#define TSEMI 4
6890#define TBACKGND 5
6891#define TAND 6
6892#define TOR 7
6893#define TPIPE 8
6894#define TLP 9
6895#define TRP 10
6896#define TENDCASE 11
6897#define TENDBQUOTE 12
6898#define TNOT 13
6899#define TCASE 14
6900#define TDO 15
6901#define TDONE 16
6902#define TELIF 17
6903#define TELSE 18
6904#define TESAC 19
6905#define TFI 20
6906#define TFOR 21
6907#define TIF 22
6908#define TIN 23
6909#define TTHEN 24
6910#define TUNTIL 25
6911#define TWHILE 26
6912#define TBEGIN 27
6913#define TEND 28
6914
6915/* first char is indicating which tokens mark the end of a list */
6916static const char *const tokname_array[] = {
6917 "\1end of file",
6918 "\0newline",
6919 "\0redirection",
6920 "\0word",
6921 "\0;",
6922 "\0&",
6923 "\0&&",
6924 "\0||",
6925 "\0|",
6926 "\0(",
6927 "\1)",
6928 "\1;;",
6929 "\1`",
6930#define KWDOFFSET 13
6931 /* the following are keywords */
6932 "\0!",
6933 "\0case",
6934 "\1do",
6935 "\1done",
6936 "\1elif",
6937 "\1else",
6938 "\1esac",
6939 "\1fi",
6940 "\0for",
6941 "\0if",
6942 "\0in",
6943 "\1then",
6944 "\0until",
6945 "\0while",
6946 "\0{",
6947 "\1}",
6948};
6949
6950static const char *
6951tokname(int tok)
6952{
6953 static char buf[16];
6954
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006955//try this:
6956//if (tok < TSEMI) return tokname_array[tok] + 1;
6957//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6958//return buf;
6959
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006960 if (tok >= TSEMI)
6961 buf[0] = '"';
6962 sprintf(buf + (tok >= TSEMI), "%s%c",
6963 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6964 return buf;
6965}
6966
6967/* Wrapper around strcmp for qsort/bsearch/... */
6968static int
6969pstrcmp(const void *a, const void *b)
6970{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006971 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006972}
6973
6974static const char *const *
6975findkwd(const char *s)
6976{
6977 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006978 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6979 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006980}
6981
6982/*
6983 * Locate and print what a word is...
6984 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985static int
6986describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987{
6988 struct cmdentry entry;
6989 struct tblentry *cmdp;
6990#if ENABLE_ASH_ALIAS
6991 const struct alias *ap;
6992#endif
6993 const char *path = pathval();
6994
6995 if (describe_command_verbose) {
6996 out1str(command);
6997 }
6998
6999 /* First look at the keywords */
7000 if (findkwd(command)) {
7001 out1str(describe_command_verbose ? " is a shell keyword" : command);
7002 goto out;
7003 }
7004
7005#if ENABLE_ASH_ALIAS
7006 /* Then look at the aliases */
7007 ap = lookupalias(command, 0);
7008 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007009 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007010 out1str("alias ");
7011 printalias(ap);
7012 return 0;
7013 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007014 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007015 goto out;
7016 }
7017#endif
7018 /* Then check if it is a tracked alias */
7019 cmdp = cmdlookup(command, 0);
7020 if (cmdp != NULL) {
7021 entry.cmdtype = cmdp->cmdtype;
7022 entry.u = cmdp->param;
7023 } else {
7024 /* Finally use brute force */
7025 find_command(command, &entry, DO_ABS, path);
7026 }
7027
7028 switch (entry.cmdtype) {
7029 case CMDNORMAL: {
7030 int j = entry.u.index;
7031 char *p;
7032 if (j == -1) {
7033 p = command;
7034 } else {
7035 do {
7036 p = padvance(&path, command);
7037 stunalloc(p);
7038 } while (--j >= 0);
7039 }
7040 if (describe_command_verbose) {
7041 out1fmt(" is%s %s",
7042 (cmdp ? " a tracked alias for" : nullstr), p
7043 );
7044 } else {
7045 out1str(p);
7046 }
7047 break;
7048 }
7049
7050 case CMDFUNCTION:
7051 if (describe_command_verbose) {
7052 out1str(" is a shell function");
7053 } else {
7054 out1str(command);
7055 }
7056 break;
7057
7058 case CMDBUILTIN:
7059 if (describe_command_verbose) {
7060 out1fmt(" is a %sshell builtin",
7061 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7062 "special " : nullstr
7063 );
7064 } else {
7065 out1str(command);
7066 }
7067 break;
7068
7069 default:
7070 if (describe_command_verbose) {
7071 out1str(": not found\n");
7072 }
7073 return 127;
7074 }
7075 out:
7076 outstr("\n", stdout);
7077 return 0;
7078}
7079
7080static int
7081typecmd(int argc, char **argv)
7082{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007083 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007084 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007085 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007086
Denis Vlasenko46846e22007-05-20 13:08:31 +00007087 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007088 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007089 i++;
7090 verbose = 0;
7091 }
7092 while (i < argc) {
7093 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007094 }
7095 return err;
7096}
7097
7098#if ENABLE_ASH_CMDCMD
7099static int
7100commandcmd(int argc, char **argv)
7101{
7102 int c;
7103 enum {
7104 VERIFY_BRIEF = 1,
7105 VERIFY_VERBOSE = 2,
7106 } verify = 0;
7107
7108 while ((c = nextopt("pvV")) != '\0')
7109 if (c == 'V')
7110 verify |= VERIFY_VERBOSE;
7111 else if (c == 'v')
7112 verify |= VERIFY_BRIEF;
7113#if DEBUG
7114 else if (c != 'p')
7115 abort();
7116#endif
7117 if (verify)
7118 return describe_command(*argptr, verify - VERIFY_BRIEF);
7119
7120 return 0;
7121}
7122#endif
7123
7124
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007125/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007126
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007127static int funcblocksize; /* size of structures in function */
7128static int funcstringsize; /* size of strings in node */
7129static void *funcblock; /* block to allocate function from */
7130static char *funcstring; /* block to allocate strings from */
7131
Eric Andersencb57d552001-06-28 07:25:16 +00007132/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007133#define EV_EXIT 01 /* exit after evaluating tree */
7134#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7135#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007136
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007137static const short nodesize[26] = {
7138 SHELL_ALIGN(sizeof(struct ncmd)),
7139 SHELL_ALIGN(sizeof(struct npipe)),
7140 SHELL_ALIGN(sizeof(struct nredir)),
7141 SHELL_ALIGN(sizeof(struct nredir)),
7142 SHELL_ALIGN(sizeof(struct nredir)),
7143 SHELL_ALIGN(sizeof(struct nbinary)),
7144 SHELL_ALIGN(sizeof(struct nbinary)),
7145 SHELL_ALIGN(sizeof(struct nbinary)),
7146 SHELL_ALIGN(sizeof(struct nif)),
7147 SHELL_ALIGN(sizeof(struct nbinary)),
7148 SHELL_ALIGN(sizeof(struct nbinary)),
7149 SHELL_ALIGN(sizeof(struct nfor)),
7150 SHELL_ALIGN(sizeof(struct ncase)),
7151 SHELL_ALIGN(sizeof(struct nclist)),
7152 SHELL_ALIGN(sizeof(struct narg)),
7153 SHELL_ALIGN(sizeof(struct narg)),
7154 SHELL_ALIGN(sizeof(struct nfile)),
7155 SHELL_ALIGN(sizeof(struct nfile)),
7156 SHELL_ALIGN(sizeof(struct nfile)),
7157 SHELL_ALIGN(sizeof(struct nfile)),
7158 SHELL_ALIGN(sizeof(struct nfile)),
7159 SHELL_ALIGN(sizeof(struct ndup)),
7160 SHELL_ALIGN(sizeof(struct ndup)),
7161 SHELL_ALIGN(sizeof(struct nhere)),
7162 SHELL_ALIGN(sizeof(struct nhere)),
7163 SHELL_ALIGN(sizeof(struct nnot)),
7164};
7165
7166static void calcsize(union node *n);
7167
7168static void
7169sizenodelist(struct nodelist *lp)
7170{
7171 while (lp) {
7172 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7173 calcsize(lp->n);
7174 lp = lp->next;
7175 }
7176}
7177
7178static void
7179calcsize(union node *n)
7180{
7181 if (n == NULL)
7182 return;
7183 funcblocksize += nodesize[n->type];
7184 switch (n->type) {
7185 case NCMD:
7186 calcsize(n->ncmd.redirect);
7187 calcsize(n->ncmd.args);
7188 calcsize(n->ncmd.assign);
7189 break;
7190 case NPIPE:
7191 sizenodelist(n->npipe.cmdlist);
7192 break;
7193 case NREDIR:
7194 case NBACKGND:
7195 case NSUBSHELL:
7196 calcsize(n->nredir.redirect);
7197 calcsize(n->nredir.n);
7198 break;
7199 case NAND:
7200 case NOR:
7201 case NSEMI:
7202 case NWHILE:
7203 case NUNTIL:
7204 calcsize(n->nbinary.ch2);
7205 calcsize(n->nbinary.ch1);
7206 break;
7207 case NIF:
7208 calcsize(n->nif.elsepart);
7209 calcsize(n->nif.ifpart);
7210 calcsize(n->nif.test);
7211 break;
7212 case NFOR:
7213 funcstringsize += strlen(n->nfor.var) + 1;
7214 calcsize(n->nfor.body);
7215 calcsize(n->nfor.args);
7216 break;
7217 case NCASE:
7218 calcsize(n->ncase.cases);
7219 calcsize(n->ncase.expr);
7220 break;
7221 case NCLIST:
7222 calcsize(n->nclist.body);
7223 calcsize(n->nclist.pattern);
7224 calcsize(n->nclist.next);
7225 break;
7226 case NDEFUN:
7227 case NARG:
7228 sizenodelist(n->narg.backquote);
7229 funcstringsize += strlen(n->narg.text) + 1;
7230 calcsize(n->narg.next);
7231 break;
7232 case NTO:
7233 case NCLOBBER:
7234 case NFROM:
7235 case NFROMTO:
7236 case NAPPEND:
7237 calcsize(n->nfile.fname);
7238 calcsize(n->nfile.next);
7239 break;
7240 case NTOFD:
7241 case NFROMFD:
7242 calcsize(n->ndup.vname);
7243 calcsize(n->ndup.next);
7244 break;
7245 case NHERE:
7246 case NXHERE:
7247 calcsize(n->nhere.doc);
7248 calcsize(n->nhere.next);
7249 break;
7250 case NNOT:
7251 calcsize(n->nnot.com);
7252 break;
7253 };
7254}
7255
7256static char *
7257nodeckstrdup(char *s)
7258{
7259 char *rtn = funcstring;
7260
7261 strcpy(funcstring, s);
7262 funcstring += strlen(s) + 1;
7263 return rtn;
7264}
7265
7266static union node *copynode(union node *);
7267
7268static struct nodelist *
7269copynodelist(struct nodelist *lp)
7270{
7271 struct nodelist *start;
7272 struct nodelist **lpp;
7273
7274 lpp = &start;
7275 while (lp) {
7276 *lpp = funcblock;
7277 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7278 (*lpp)->n = copynode(lp->n);
7279 lp = lp->next;
7280 lpp = &(*lpp)->next;
7281 }
7282 *lpp = NULL;
7283 return start;
7284}
7285
7286static union node *
7287copynode(union node *n)
7288{
7289 union node *new;
7290
7291 if (n == NULL)
7292 return NULL;
7293 new = funcblock;
7294 funcblock = (char *) funcblock + nodesize[n->type];
7295
7296 switch (n->type) {
7297 case NCMD:
7298 new->ncmd.redirect = copynode(n->ncmd.redirect);
7299 new->ncmd.args = copynode(n->ncmd.args);
7300 new->ncmd.assign = copynode(n->ncmd.assign);
7301 break;
7302 case NPIPE:
7303 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7304 new->npipe.backgnd = n->npipe.backgnd;
7305 break;
7306 case NREDIR:
7307 case NBACKGND:
7308 case NSUBSHELL:
7309 new->nredir.redirect = copynode(n->nredir.redirect);
7310 new->nredir.n = copynode(n->nredir.n);
7311 break;
7312 case NAND:
7313 case NOR:
7314 case NSEMI:
7315 case NWHILE:
7316 case NUNTIL:
7317 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7318 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7319 break;
7320 case NIF:
7321 new->nif.elsepart = copynode(n->nif.elsepart);
7322 new->nif.ifpart = copynode(n->nif.ifpart);
7323 new->nif.test = copynode(n->nif.test);
7324 break;
7325 case NFOR:
7326 new->nfor.var = nodeckstrdup(n->nfor.var);
7327 new->nfor.body = copynode(n->nfor.body);
7328 new->nfor.args = copynode(n->nfor.args);
7329 break;
7330 case NCASE:
7331 new->ncase.cases = copynode(n->ncase.cases);
7332 new->ncase.expr = copynode(n->ncase.expr);
7333 break;
7334 case NCLIST:
7335 new->nclist.body = copynode(n->nclist.body);
7336 new->nclist.pattern = copynode(n->nclist.pattern);
7337 new->nclist.next = copynode(n->nclist.next);
7338 break;
7339 case NDEFUN:
7340 case NARG:
7341 new->narg.backquote = copynodelist(n->narg.backquote);
7342 new->narg.text = nodeckstrdup(n->narg.text);
7343 new->narg.next = copynode(n->narg.next);
7344 break;
7345 case NTO:
7346 case NCLOBBER:
7347 case NFROM:
7348 case NFROMTO:
7349 case NAPPEND:
7350 new->nfile.fname = copynode(n->nfile.fname);
7351 new->nfile.fd = n->nfile.fd;
7352 new->nfile.next = copynode(n->nfile.next);
7353 break;
7354 case NTOFD:
7355 case NFROMFD:
7356 new->ndup.vname = copynode(n->ndup.vname);
7357 new->ndup.dupfd = n->ndup.dupfd;
7358 new->ndup.fd = n->ndup.fd;
7359 new->ndup.next = copynode(n->ndup.next);
7360 break;
7361 case NHERE:
7362 case NXHERE:
7363 new->nhere.doc = copynode(n->nhere.doc);
7364 new->nhere.fd = n->nhere.fd;
7365 new->nhere.next = copynode(n->nhere.next);
7366 break;
7367 case NNOT:
7368 new->nnot.com = copynode(n->nnot.com);
7369 break;
7370 };
7371 new->type = n->type;
7372 return new;
7373}
7374
7375/*
7376 * Make a copy of a parse tree.
7377 */
7378static struct funcnode *
7379copyfunc(union node *n)
7380{
7381 struct funcnode *f;
7382 size_t blocksize;
7383
7384 funcblocksize = offsetof(struct funcnode, n);
7385 funcstringsize = 0;
7386 calcsize(n);
7387 blocksize = funcblocksize;
7388 f = ckmalloc(blocksize + funcstringsize);
7389 funcblock = (char *) f + offsetof(struct funcnode, n);
7390 funcstring = (char *) f + blocksize;
7391 copynode(n);
7392 f->count = 0;
7393 return f;
7394}
7395
7396/*
7397 * Define a shell function.
7398 */
7399static void
7400defun(char *name, union node *func)
7401{
7402 struct cmdentry entry;
7403
7404 INT_OFF;
7405 entry.cmdtype = CMDFUNCTION;
7406 entry.u.func = copyfunc(func);
7407 addcmdentry(name, &entry);
7408 INT_ON;
7409}
7410
7411static int evalskip; /* set if we are skipping commands */
7412/* reasons for skipping commands (see comment on breakcmd routine) */
7413#define SKIPBREAK (1 << 0)
7414#define SKIPCONT (1 << 1)
7415#define SKIPFUNC (1 << 2)
7416#define SKIPFILE (1 << 3)
7417#define SKIPEVAL (1 << 4)
7418static int skipcount; /* number of levels to skip */
7419static int funcnest; /* depth of function calls */
7420
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007421/* forward decl way out to parsing code - dotrap needs it */
7422static int evalstring(char *s, int mask);
7423
7424/*
7425 * Called to execute a trap. Perhaps we should avoid entering new trap
7426 * handlers while we are executing a trap handler.
7427 */
7428static int
7429dotrap(void)
7430{
7431 char *p;
7432 char *q;
7433 int i;
7434 int savestatus;
7435 int skip = 0;
7436
7437 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007438 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007439 xbarrier();
7440
7441 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7442 if (!*q)
7443 continue;
7444 *q = '\0';
7445
7446 p = trap[i + 1];
7447 if (!p)
7448 continue;
7449 skip = evalstring(p, SKIPEVAL);
7450 exitstatus = savestatus;
7451 if (skip)
7452 break;
7453 }
7454
7455 return skip;
7456}
7457
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007458/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007459static void evalloop(union node *, int);
7460static void evalfor(union node *, int);
7461static void evalcase(union node *, int);
7462static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007463static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007464static void evalpipe(union node *, int);
7465static void evalcommand(union node *, int);
7466static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007467static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007468
Eric Andersen62483552001-07-10 06:09:16 +00007469/*
Eric Andersenc470f442003-07-28 09:56:35 +00007470 * Evaluate a parse tree. The value is left in the global variable
7471 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007472 */
Eric Andersenc470f442003-07-28 09:56:35 +00007473static void
7474evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007475{
Eric Andersenc470f442003-07-28 09:56:35 +00007476 int checkexit = 0;
7477 void (*evalfn)(union node *, int);
7478 unsigned isor;
7479 int status;
7480 if (n == NULL) {
7481 TRACE(("evaltree(NULL) called\n"));
7482 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007483 }
Eric Andersenc470f442003-07-28 09:56:35 +00007484 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007485 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007486 switch (n->type) {
7487 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007488#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007489 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007490 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007491 break;
7492#endif
7493 case NNOT:
7494 evaltree(n->nnot.com, EV_TESTED);
7495 status = !exitstatus;
7496 goto setstatus;
7497 case NREDIR:
7498 expredir(n->nredir.redirect);
7499 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7500 if (!status) {
7501 evaltree(n->nredir.n, flags & EV_TESTED);
7502 status = exitstatus;
7503 }
7504 popredir(0);
7505 goto setstatus;
7506 case NCMD:
7507 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007508 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007509 if (eflag && !(flags & EV_TESTED))
7510 checkexit = ~0;
7511 goto calleval;
7512 case NFOR:
7513 evalfn = evalfor;
7514 goto calleval;
7515 case NWHILE:
7516 case NUNTIL:
7517 evalfn = evalloop;
7518 goto calleval;
7519 case NSUBSHELL:
7520 case NBACKGND:
7521 evalfn = evalsubshell;
7522 goto calleval;
7523 case NPIPE:
7524 evalfn = evalpipe;
7525 goto checkexit;
7526 case NCASE:
7527 evalfn = evalcase;
7528 goto calleval;
7529 case NAND:
7530 case NOR:
7531 case NSEMI:
7532#if NAND + 1 != NOR
7533#error NAND + 1 != NOR
7534#endif
7535#if NOR + 1 != NSEMI
7536#error NOR + 1 != NSEMI
7537#endif
7538 isor = n->type - NAND;
7539 evaltree(
7540 n->nbinary.ch1,
7541 (flags | ((isor >> 1) - 1)) & EV_TESTED
7542 );
7543 if (!exitstatus == isor)
7544 break;
7545 if (!evalskip) {
7546 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007547 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007548 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007549 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007550 evalfn(n, flags);
7551 break;
7552 }
7553 break;
7554 case NIF:
7555 evaltree(n->nif.test, EV_TESTED);
7556 if (evalskip)
7557 break;
7558 if (exitstatus == 0) {
7559 n = n->nif.ifpart;
7560 goto evaln;
7561 } else if (n->nif.elsepart) {
7562 n = n->nif.elsepart;
7563 goto evaln;
7564 }
7565 goto success;
7566 case NDEFUN:
7567 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007568 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007569 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007570 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007571 exitstatus = status;
7572 break;
7573 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007574 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007575 if ((checkexit & exitstatus))
7576 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007577 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007578 goto exexit;
7579
7580 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007581 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007582 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007583 }
Eric Andersen62483552001-07-10 06:09:16 +00007584}
7585
Eric Andersenc470f442003-07-28 09:56:35 +00007586#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7587static
7588#endif
7589void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7590
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007591static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007592
7593static void
7594evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007595{
7596 int status;
7597
7598 loopnest++;
7599 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007600 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007601 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007602 int i;
7603
Eric Andersencb57d552001-06-28 07:25:16 +00007604 evaltree(n->nbinary.ch1, EV_TESTED);
7605 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007606 skipping:
7607 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007608 evalskip = 0;
7609 continue;
7610 }
7611 if (evalskip == SKIPBREAK && --skipcount <= 0)
7612 evalskip = 0;
7613 break;
7614 }
Eric Andersenc470f442003-07-28 09:56:35 +00007615 i = exitstatus;
7616 if (n->type != NWHILE)
7617 i = !i;
7618 if (i != 0)
7619 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007620 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007621 status = exitstatus;
7622 if (evalskip)
7623 goto skipping;
7624 }
7625 loopnest--;
7626 exitstatus = status;
7627}
7628
Eric Andersenc470f442003-07-28 09:56:35 +00007629static void
7630evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007631{
7632 struct arglist arglist;
7633 union node *argp;
7634 struct strlist *sp;
7635 struct stackmark smark;
7636
7637 setstackmark(&smark);
7638 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007639 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007640 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007641 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007642 if (evalskip)
7643 goto out;
7644 }
7645 *arglist.lastp = NULL;
7646
7647 exitstatus = 0;
7648 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007649 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007650 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007651 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007652 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007653 if (evalskip) {
7654 if (evalskip == SKIPCONT && --skipcount <= 0) {
7655 evalskip = 0;
7656 continue;
7657 }
7658 if (evalskip == SKIPBREAK && --skipcount <= 0)
7659 evalskip = 0;
7660 break;
7661 }
7662 }
7663 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007664 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007665 popstackmark(&smark);
7666}
7667
Eric Andersenc470f442003-07-28 09:56:35 +00007668static void
7669evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007670{
7671 union node *cp;
7672 union node *patp;
7673 struct arglist arglist;
7674 struct stackmark smark;
7675
7676 setstackmark(&smark);
7677 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007678 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007679 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007680 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7681 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007682 if (casematch(patp, arglist.list->text)) {
7683 if (evalskip == 0) {
7684 evaltree(cp->nclist.body, flags);
7685 }
7686 goto out;
7687 }
7688 }
7689 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007690 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007691 popstackmark(&smark);
7692}
7693
Eric Andersenc470f442003-07-28 09:56:35 +00007694/*
7695 * Kick off a subshell to evaluate a tree.
7696 */
Eric Andersenc470f442003-07-28 09:56:35 +00007697static void
7698evalsubshell(union node *n, int flags)
7699{
7700 struct job *jp;
7701 int backgnd = (n->type == NBACKGND);
7702 int status;
7703
7704 expredir(n->nredir.redirect);
7705 if (!backgnd && flags & EV_EXIT && !trap[0])
7706 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007707 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007708 jp = makejob(n, 1);
7709 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007710 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007711 flags |= EV_EXIT;
7712 if (backgnd)
7713 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007714 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007715 redirect(n->nredir.redirect, 0);
7716 evaltreenr(n->nredir.n, flags);
7717 /* never returns */
7718 }
7719 status = 0;
7720 if (! backgnd)
7721 status = waitforjob(jp);
7722 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007723 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007724}
7725
Eric Andersenc470f442003-07-28 09:56:35 +00007726/*
7727 * Compute the names of the files in a redirection list.
7728 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007729static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007730static void
7731expredir(union node *n)
7732{
7733 union node *redir;
7734
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007735 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007736 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007737
7738 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007739 fn.lastp = &fn.list;
7740 switch (redir->type) {
7741 case NFROMTO:
7742 case NFROM:
7743 case NTO:
7744 case NCLOBBER:
7745 case NAPPEND:
7746 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7747 redir->nfile.expfname = fn.list->text;
7748 break;
7749 case NFROMFD:
7750 case NTOFD:
7751 if (redir->ndup.vname) {
7752 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007753 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007754 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007755 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007756 }
7757 break;
7758 }
7759 }
7760}
7761
Eric Andersencb57d552001-06-28 07:25:16 +00007762/*
Eric Andersencb57d552001-06-28 07:25:16 +00007763 * Evaluate a pipeline. All the processes in the pipeline are children
7764 * of the process creating the pipeline. (This differs from some versions
7765 * of the shell, which make the last process in a pipeline the parent
7766 * of all the rest.)
7767 */
Eric Andersenc470f442003-07-28 09:56:35 +00007768static void
7769evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007770{
7771 struct job *jp;
7772 struct nodelist *lp;
7773 int pipelen;
7774 int prevfd;
7775 int pip[2];
7776
Eric Andersenc470f442003-07-28 09:56:35 +00007777 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007778 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007779 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007780 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007781 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007782 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007783 jp = makejob(n, pipelen);
7784 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007785 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007786 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007787 pip[1] = -1;
7788 if (lp->next) {
7789 if (pipe(pip) < 0) {
7790 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007791 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007792 }
7793 }
7794 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007795 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007796 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007797 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007798 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007799 if (prevfd > 0) {
7800 dup2(prevfd, 0);
7801 close(prevfd);
7802 }
7803 if (pip[1] > 1) {
7804 dup2(pip[1], 1);
7805 close(pip[1]);
7806 }
Eric Andersenc470f442003-07-28 09:56:35 +00007807 evaltreenr(lp->n, flags);
7808 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007809 }
7810 if (prevfd >= 0)
7811 close(prevfd);
7812 prevfd = pip[0];
7813 close(pip[1]);
7814 }
Eric Andersencb57d552001-06-28 07:25:16 +00007815 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007816 exitstatus = waitforjob(jp);
7817 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007818 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007819 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007820}
7821
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007822/*
7823 * Controls whether the shell is interactive or not.
7824 */
7825static void
7826setinteractive(int on)
7827{
7828 static int is_interactive;
7829
7830 if (++on == is_interactive)
7831 return;
7832 is_interactive = on;
7833 setsignal(SIGINT);
7834 setsignal(SIGQUIT);
7835 setsignal(SIGTERM);
7836#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7837 if (is_interactive > 1) {
7838 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007839 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007840
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007841 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007842 out1fmt(
7843 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007844 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007845 "Enter 'help' for a list of built-in commands."
7846 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007847 bb_banner);
7848 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007849 }
7850 }
7851#endif
7852}
7853
7854#if ENABLE_FEATURE_EDITING_VI
7855#define setvimode(on) do { \
7856 if (on) line_input_state->flags |= VI_MODE; \
7857 else line_input_state->flags &= ~VI_MODE; \
7858} while (0)
7859#else
7860#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7861#endif
7862
7863static void
7864optschanged(void)
7865{
7866#if DEBUG
7867 opentrace();
7868#endif
7869 setinteractive(iflag);
7870 setjobctl(mflag);
7871 setvimode(viflag);
7872}
7873
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874static struct localvar *localvars;
7875
7876/*
7877 * Called after a function returns.
7878 * Interrupts must be off.
7879 */
7880static void
7881poplocalvars(void)
7882{
7883 struct localvar *lvp;
7884 struct var *vp;
7885
7886 while ((lvp = localvars) != NULL) {
7887 localvars = lvp->next;
7888 vp = lvp->vp;
7889 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7890 if (vp == NULL) { /* $- saved */
7891 memcpy(optlist, lvp->text, sizeof(optlist));
7892 free((char*)lvp->text);
7893 optschanged();
7894 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7895 unsetvar(vp->text);
7896 } else {
7897 if (vp->func)
7898 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7899 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7900 free((char*)vp->text);
7901 vp->flags = lvp->flags;
7902 vp->text = lvp->text;
7903 }
7904 free(lvp);
7905 }
7906}
7907
7908static int
7909evalfun(struct funcnode *func, int argc, char **argv, int flags)
7910{
7911 volatile struct shparam saveparam;
7912 struct localvar *volatile savelocalvars;
7913 struct jmploc *volatile savehandler;
7914 struct jmploc jmploc;
7915 int e;
7916
7917 saveparam = shellparam;
7918 savelocalvars = localvars;
7919 e = setjmp(jmploc.loc);
7920 if (e) {
7921 goto funcdone;
7922 }
7923 INT_OFF;
7924 savehandler = exception_handler;
7925 exception_handler = &jmploc;
7926 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007927 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007928 func->count++;
7929 funcnest++;
7930 INT_ON;
7931 shellparam.nparam = argc - 1;
7932 shellparam.p = argv + 1;
7933#if ENABLE_ASH_GETOPTS
7934 shellparam.optind = 1;
7935 shellparam.optoff = -1;
7936#endif
7937 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007938 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007939 INT_OFF;
7940 funcnest--;
7941 freefunc(func);
7942 poplocalvars();
7943 localvars = savelocalvars;
7944 freeparam(&shellparam);
7945 shellparam = saveparam;
7946 exception_handler = savehandler;
7947 INT_ON;
7948 evalskip &= ~SKIPFUNC;
7949 return e;
7950}
7951
Denis Vlasenko131ae172007-02-18 13:00:19 +00007952#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007953static char **
7954parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007955{
7956 char *cp, c;
7957
7958 for (;;) {
7959 cp = *++argv;
7960 if (!cp)
7961 return 0;
7962 if (*cp++ != '-')
7963 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007964 c = *cp++;
7965 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007966 break;
7967 if (c == '-' && !*cp) {
7968 argv++;
7969 break;
7970 }
7971 do {
7972 switch (c) {
7973 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007974 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007975 break;
7976 default:
7977 /* run 'typecmd' for other options */
7978 return 0;
7979 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007980 c = *cp++;
7981 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007982 }
7983 return argv;
7984}
7985#endif
7986
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007987/*
7988 * Make a variable a local variable. When a variable is made local, it's
7989 * value and flags are saved in a localvar structure. The saved values
7990 * will be restored when the shell function returns. We handle the name
7991 * "-" as a special case.
7992 */
7993static void
7994mklocal(char *name)
7995{
7996 struct localvar *lvp;
7997 struct var **vpp;
7998 struct var *vp;
7999
8000 INT_OFF;
8001 lvp = ckmalloc(sizeof(struct localvar));
8002 if (LONE_DASH(name)) {
8003 char *p;
8004 p = ckmalloc(sizeof(optlist));
8005 lvp->text = memcpy(p, optlist, sizeof(optlist));
8006 vp = NULL;
8007 } else {
8008 char *eq;
8009
8010 vpp = hashvar(name);
8011 vp = *findvar(vpp, name);
8012 eq = strchr(name, '=');
8013 if (vp == NULL) {
8014 if (eq)
8015 setvareq(name, VSTRFIXED);
8016 else
8017 setvar(name, NULL, VSTRFIXED);
8018 vp = *vpp; /* the new variable */
8019 lvp->flags = VUNSET;
8020 } else {
8021 lvp->text = vp->text;
8022 lvp->flags = vp->flags;
8023 vp->flags |= VSTRFIXED|VTEXTFIXED;
8024 if (eq)
8025 setvareq(name, 0);
8026 }
8027 }
8028 lvp->vp = vp;
8029 lvp->next = localvars;
8030 localvars = lvp;
8031 INT_ON;
8032}
8033
8034/*
8035 * The "local" command.
8036 */
8037static int
8038localcmd(int argc, char **argv)
8039{
8040 char *name;
8041
8042 argv = argptr;
8043 while ((name = *argv++) != NULL) {
8044 mklocal(name);
8045 }
8046 return 0;
8047}
8048
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008049static int
8050falsecmd(int argc, char **argv)
8051{
8052 return 1;
8053}
8054
8055static int
8056truecmd(int argc, char **argv)
8057{
8058 return 0;
8059}
8060
8061static int
8062execcmd(int argc, char **argv)
8063{
8064 if (argc > 1) {
8065 iflag = 0; /* exit on error */
8066 mflag = 0;
8067 optschanged();
8068 shellexec(argv + 1, pathval(), 0);
8069 }
8070 return 0;
8071}
8072
8073/*
8074 * The return command.
8075 */
8076static int
8077returncmd(int argc, char **argv)
8078{
8079 /*
8080 * If called outside a function, do what ksh does;
8081 * skip the rest of the file.
8082 */
8083 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8084 return argv[1] ? number(argv[1]) : exitstatus;
8085}
8086
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008087/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008088static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008089static int dotcmd(int, char **);
8090static int evalcmd(int, char **);
8091#if ENABLE_ASH_BUILTIN_ECHO
8092static int echocmd(int, char **);
8093#endif
8094#if ENABLE_ASH_BUILTIN_TEST
8095static int testcmd(int, char **);
8096#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008097static int exitcmd(int, char **);
8098static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008099#if ENABLE_ASH_GETOPTS
8100static int getoptscmd(int, char **);
8101#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008102#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8103static int helpcmd(int argc, char **argv);
8104#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008105#if ENABLE_ASH_MATH_SUPPORT
8106static int letcmd(int, char **);
8107#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008108static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008109static int setcmd(int, char **);
8110static int shiftcmd(int, char **);
8111static int timescmd(int, char **);
8112static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008113static int umaskcmd(int, char **);
8114static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008115static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008116
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008117#define BUILTIN_NOSPEC "0"
8118#define BUILTIN_SPECIAL "1"
8119#define BUILTIN_REGULAR "2"
8120#define BUILTIN_SPEC_REG "3"
8121#define BUILTIN_ASSIGN "4"
8122#define BUILTIN_SPEC_ASSG "5"
8123#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008124#define BUILTIN_SPEC_REG_ASSG "7"
8125
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008126/* make sure to keep these in proper order since it is searched via bsearch() */
8127static const struct builtincmd builtintab[] = {
8128 { BUILTIN_SPEC_REG ".", dotcmd },
8129 { BUILTIN_SPEC_REG ":", truecmd },
8130#if ENABLE_ASH_BUILTIN_TEST
8131 { BUILTIN_REGULAR "[", testcmd },
8132 { BUILTIN_REGULAR "[[", testcmd },
8133#endif
8134#if ENABLE_ASH_ALIAS
8135 { BUILTIN_REG_ASSG "alias", aliascmd },
8136#endif
8137#if JOBS
8138 { BUILTIN_REGULAR "bg", fg_bgcmd },
8139#endif
8140 { BUILTIN_SPEC_REG "break", breakcmd },
8141 { BUILTIN_REGULAR "cd", cdcmd },
8142 { BUILTIN_NOSPEC "chdir", cdcmd },
8143#if ENABLE_ASH_CMDCMD
8144 { BUILTIN_REGULAR "command", commandcmd },
8145#endif
8146 { BUILTIN_SPEC_REG "continue", breakcmd },
8147#if ENABLE_ASH_BUILTIN_ECHO
8148 { BUILTIN_REGULAR "echo", echocmd },
8149#endif
8150 { BUILTIN_SPEC_REG "eval", evalcmd },
8151 { BUILTIN_SPEC_REG "exec", execcmd },
8152 { BUILTIN_SPEC_REG "exit", exitcmd },
8153 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8154 { BUILTIN_REGULAR "false", falsecmd },
8155#if JOBS
8156 { BUILTIN_REGULAR "fg", fg_bgcmd },
8157#endif
8158#if ENABLE_ASH_GETOPTS
8159 { BUILTIN_REGULAR "getopts", getoptscmd },
8160#endif
8161 { BUILTIN_NOSPEC "hash", hashcmd },
8162#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8163 { BUILTIN_NOSPEC "help", helpcmd },
8164#endif
8165#if JOBS
8166 { BUILTIN_REGULAR "jobs", jobscmd },
8167 { BUILTIN_REGULAR "kill", killcmd },
8168#endif
8169#if ENABLE_ASH_MATH_SUPPORT
8170 { BUILTIN_NOSPEC "let", letcmd },
8171#endif
8172 { BUILTIN_ASSIGN "local", localcmd },
8173 { BUILTIN_NOSPEC "pwd", pwdcmd },
8174 { BUILTIN_REGULAR "read", readcmd },
8175 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8176 { BUILTIN_SPEC_REG "return", returncmd },
8177 { BUILTIN_SPEC_REG "set", setcmd },
8178 { BUILTIN_SPEC_REG "shift", shiftcmd },
8179 { BUILTIN_SPEC_REG "source", dotcmd },
8180#if ENABLE_ASH_BUILTIN_TEST
8181 { BUILTIN_REGULAR "test", testcmd },
8182#endif
8183 { BUILTIN_SPEC_REG "times", timescmd },
8184 { BUILTIN_SPEC_REG "trap", trapcmd },
8185 { BUILTIN_REGULAR "true", truecmd },
8186 { BUILTIN_NOSPEC "type", typecmd },
8187 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8188 { BUILTIN_REGULAR "umask", umaskcmd },
8189#if ENABLE_ASH_ALIAS
8190 { BUILTIN_REGULAR "unalias", unaliascmd },
8191#endif
8192 { BUILTIN_SPEC_REG "unset", unsetcmd },
8193 { BUILTIN_REGULAR "wait", waitcmd },
8194};
8195
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008196
8197#define COMMANDCMD (builtintab + 5 + \
8198 2 * ENABLE_ASH_BUILTIN_TEST + \
8199 ENABLE_ASH_ALIAS + \
8200 ENABLE_ASH_JOB_CONTROL)
8201#define EXECCMD (builtintab + 7 + \
8202 2 * ENABLE_ASH_BUILTIN_TEST + \
8203 ENABLE_ASH_ALIAS + \
8204 ENABLE_ASH_JOB_CONTROL + \
8205 ENABLE_ASH_CMDCMD + \
8206 ENABLE_ASH_BUILTIN_ECHO)
8207
8208/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008209 * Search the table of builtin commands.
8210 */
8211static struct builtincmd *
8212find_builtin(const char *name)
8213{
8214 struct builtincmd *bp;
8215
8216 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008217 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008218 pstrcmp
8219 );
8220 return bp;
8221}
8222
8223/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008224 * Execute a simple command.
8225 */
8226static int back_exitstatus; /* exit status of backquoted command */
8227static int
8228isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008229{
8230 const char *q = endofname(p);
8231 if (p == q)
8232 return 0;
8233 return *q == '=';
8234}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008235static int
8236bltincmd(int argc, char **argv)
8237{
8238 /* Preserve exitstatus of a previous possible redirection
8239 * as POSIX mandates */
8240 return back_exitstatus;
8241}
Eric Andersenc470f442003-07-28 09:56:35 +00008242static void
8243evalcommand(union node *cmd, int flags)
8244{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008245 static const struct builtincmd bltin = {
8246 "\0\0", bltincmd
8247 };
Eric Andersenc470f442003-07-28 09:56:35 +00008248 struct stackmark smark;
8249 union node *argp;
8250 struct arglist arglist;
8251 struct arglist varlist;
8252 char **argv;
8253 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008254 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008255 struct cmdentry cmdentry;
8256 struct job *jp;
8257 char *lastarg;
8258 const char *path;
8259 int spclbltin;
8260 int cmd_is_exec;
8261 int status;
8262 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008263 struct builtincmd *bcmd;
8264 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008265
8266 /* First expand the arguments. */
8267 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8268 setstackmark(&smark);
8269 back_exitstatus = 0;
8270
8271 cmdentry.cmdtype = CMDBUILTIN;
8272 cmdentry.u.cmd = &bltin;
8273 varlist.lastp = &varlist.list;
8274 *varlist.lastp = NULL;
8275 arglist.lastp = &arglist.list;
8276 *arglist.lastp = NULL;
8277
8278 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008279 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008280 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8281 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8282 }
8283
Eric Andersenc470f442003-07-28 09:56:35 +00008284 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8285 struct strlist **spp;
8286
8287 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008288 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008289 expandarg(argp, &arglist, EXP_VARTILDE);
8290 else
8291 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8292
Eric Andersenc470f442003-07-28 09:56:35 +00008293 for (sp = *spp; sp; sp = sp->next)
8294 argc++;
8295 }
8296
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008297 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008298 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008299 TRACE(("evalcommand arg: %s\n", sp->text));
8300 *nargv++ = sp->text;
8301 }
8302 *nargv = NULL;
8303
8304 lastarg = NULL;
8305 if (iflag && funcnest == 0 && argc > 0)
8306 lastarg = nargv[-1];
8307
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008308 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008309 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008310 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008311
8312 path = vpath.text;
8313 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8314 struct strlist **spp;
8315 char *p;
8316
8317 spp = varlist.lastp;
8318 expandarg(argp, &varlist, EXP_VARTILDE);
8319
8320 /*
8321 * Modify the command lookup path, if a PATH= assignment
8322 * is present
8323 */
8324 p = (*spp)->text;
8325 if (varequal(p, path))
8326 path = p;
8327 }
8328
8329 /* Print the command if xflag is set. */
8330 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008331 int n;
8332 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008333
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008334 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008335 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008336
8337 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008338 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008339 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008340 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008341 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008342 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008343 p--;
8344 }
8345 }
8346 sp = arglist.list;
8347 }
Rob Landley53437472006-07-16 08:14:35 +00008348 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008349 }
8350
8351 cmd_is_exec = 0;
8352 spclbltin = -1;
8353
8354 /* Now locate the command. */
8355 if (argc) {
8356 const char *oldpath;
8357 int cmd_flag = DO_ERR;
8358
8359 path += 5;
8360 oldpath = path;
8361 for (;;) {
8362 find_command(argv[0], &cmdentry, cmd_flag, path);
8363 if (cmdentry.cmdtype == CMDUNKNOWN) {
8364 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008365 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008366 goto bail;
8367 }
8368
8369 /* implement bltin and command here */
8370 if (cmdentry.cmdtype != CMDBUILTIN)
8371 break;
8372 if (spclbltin < 0)
8373 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8374 if (cmdentry.u.cmd == EXECCMD)
8375 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008376#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008377 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008378 path = oldpath;
8379 nargv = parse_command_args(argv, &path);
8380 if (!nargv)
8381 break;
8382 argc -= nargv - argv;
8383 argv = nargv;
8384 cmd_flag |= DO_NOFUNC;
8385 } else
8386#endif
8387 break;
8388 }
8389 }
8390
8391 if (status) {
8392 /* We have a redirection error. */
8393 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008394 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008395 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008396 exitstatus = status;
8397 goto out;
8398 }
8399
8400 /* Execute the command. */
8401 switch (cmdentry.cmdtype) {
8402 default:
8403 /* Fork off a child process if necessary. */
8404 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008405 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008406 jp = makejob(cmd, 1);
8407 if (forkshell(jp, cmd, FORK_FG) != 0) {
8408 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008409 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008410 break;
8411 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008412 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008413 }
8414 listsetvar(varlist.list, VEXPORT|VSTACK);
8415 shellexec(argv, path, cmdentry.u.index);
8416 /* NOTREACHED */
8417
8418 case CMDBUILTIN:
8419 cmdenviron = varlist.list;
8420 if (cmdenviron) {
8421 struct strlist *list = cmdenviron;
8422 int i = VNOSET;
8423 if (spclbltin > 0 || argc == 0) {
8424 i = 0;
8425 if (cmd_is_exec && argc > 1)
8426 i = VEXPORT;
8427 }
8428 listsetvar(list, i);
8429 }
8430 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8431 int exit_status;
8432 int i, j;
8433
8434 i = exception;
8435 if (i == EXEXIT)
8436 goto raise;
8437
8438 exit_status = 2;
8439 j = 0;
8440 if (i == EXINT)
8441 j = SIGINT;
8442 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008443 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008444 if (j)
8445 exit_status = j + 128;
8446 exitstatus = exit_status;
8447
8448 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008449 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008450 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008451 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008452 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008453 }
8454 break;
8455
8456 case CMDFUNCTION:
8457 listsetvar(varlist.list, 0);
8458 if (evalfun(cmdentry.u.func, argc, argv, flags))
8459 goto raise;
8460 break;
8461 }
8462
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008463 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008464 popredir(cmd_is_exec);
8465 if (lastarg)
8466 /* dsl: I think this is intended to be used to support
8467 * '_' in 'vi' command mode during line editing...
8468 * However I implemented that within libedit itself.
8469 */
8470 setvar("_", lastarg, 0);
8471 popstackmark(&smark);
8472}
8473
8474static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008475evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8476{
Eric Andersenc470f442003-07-28 09:56:35 +00008477 char *volatile savecmdname;
8478 struct jmploc *volatile savehandler;
8479 struct jmploc jmploc;
8480 int i;
8481
8482 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008483 i = setjmp(jmploc.loc);
8484 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008485 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008486 savehandler = exception_handler;
8487 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008488 commandname = argv[0];
8489 argptr = argv + 1;
8490 optptr = NULL; /* initialize nextopt */
8491 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008492 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008493 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008494 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008495 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008496 commandname = savecmdname;
8497 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008498 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008499
8500 return i;
8501}
8502
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008503static int
8504goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008505{
8506 return !*endofname(p);
8507}
8508
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008509
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008510/*
8511 * Search for a command. This is called before we fork so that the
8512 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008513 * the child. The check for "goodname" is an overly conservative
8514 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008515 */
Eric Andersenc470f442003-07-28 09:56:35 +00008516static void
8517prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008518{
8519 struct cmdentry entry;
8520
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008521 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8522 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008523}
8524
Eric Andersencb57d552001-06-28 07:25:16 +00008525
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008526/* ============ Builtin commands
8527 *
8528 * Builtin commands whose functions are closely tied to evaluation
8529 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008530 */
8531
8532/*
Eric Andersencb57d552001-06-28 07:25:16 +00008533 * Handle break and continue commands. Break, continue, and return are
8534 * all handled by setting the evalskip flag. The evaluation routines
8535 * above all check this flag, and if it is set they start skipping
8536 * commands rather than executing them. The variable skipcount is
8537 * the number of loops to break/continue, or the number of function
8538 * levels to return. (The latter is always 1.) It should probably
8539 * be an error to break out of more loops than exist, but it isn't
8540 * in the standard shell so we don't make it one here.
8541 */
Eric Andersenc470f442003-07-28 09:56:35 +00008542static int
8543breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008544{
8545 int n = argc > 1 ? number(argv[1]) : 1;
8546
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008547 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008548 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008549 if (n > loopnest)
8550 n = loopnest;
8551 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008552 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008553 skipcount = n;
8554 }
8555 return 0;
8556}
8557
Eric Andersenc470f442003-07-28 09:56:35 +00008558
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008559/* ============ input.c
8560 *
Eric Andersen90898442003-08-06 11:20:52 +00008561 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008562 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008563
Eric Andersenc470f442003-07-28 09:56:35 +00008564#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008566enum {
8567 INPUT_PUSH_FILE = 1,
8568 INPUT_NOFILE_OK = 2,
8569};
Eric Andersencb57d552001-06-28 07:25:16 +00008570
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008571static int plinno = 1; /* input line number */
8572/* number of characters left in input buffer */
8573static int parsenleft; /* copy of parsefile->nleft */
8574static int parselleft; /* copy of parsefile->lleft */
8575/* next character in input buffer */
8576static char *parsenextc; /* copy of parsefile->nextc */
8577
8578static int checkkwd;
8579/* values of checkkwd variable */
8580#define CHKALIAS 0x1
8581#define CHKKWD 0x2
8582#define CHKNL 0x4
8583
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008584static void
8585popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008586{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008587 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008588
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008590#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008591 if (sp->ap) {
8592 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8593 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008594 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008595 if (sp->string != sp->ap->val) {
8596 free(sp->string);
8597 }
8598 sp->ap->flag &= ~ALIASINUSE;
8599 if (sp->ap->flag & ALIASDEAD) {
8600 unalias(sp->ap->name);
8601 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008602 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008603#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008604 parsenextc = sp->prevstring;
8605 parsenleft = sp->prevnleft;
8606/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8607 parsefile->strpush = sp->prev;
8608 if (sp != &(parsefile->basestrpush))
8609 free(sp);
8610 INT_ON;
8611}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008612
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008613static int
8614preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008615{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008616 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008617 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008618 parsenextc = buf;
8619
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008620 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008621#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008622 if (!iflag || parsefile->fd)
8623 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8624 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008625#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008626 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008627#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008628 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8629 if (nr == 0) {
8630 /* Ctrl+C pressed */
8631 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008632 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008633 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008634 raise(SIGINT);
8635 return 1;
8636 }
Eric Andersenc470f442003-07-28 09:56:35 +00008637 goto retry;
8638 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008639 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008640 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008641 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008642 }
Eric Andersencb57d552001-06-28 07:25:16 +00008643 }
8644#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008645 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008646#endif
8647
8648 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008649 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008650 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008651 if (flags >= 0 && (flags & O_NONBLOCK)) {
8652 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008653 if (fcntl(0, F_SETFL, flags) >= 0) {
8654 out2str("sh: turning off NDELAY mode\n");
8655 goto retry;
8656 }
8657 }
8658 }
8659 }
8660 return nr;
8661}
8662
8663/*
8664 * Refill the input buffer and return the next input character:
8665 *
8666 * 1) If a string was pushed back on the input, pop it;
8667 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8668 * from a string so we can't refill the buffer, return EOF.
8669 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8670 * 4) Process input up to the next newline, deleting nul characters.
8671 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008672static int
Eric Andersenc470f442003-07-28 09:56:35 +00008673preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008674{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008675 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008676 int more;
8677 char savec;
8678
8679 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008680#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008681 if (parsenleft == -1 && parsefile->strpush->ap &&
8682 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008683 return PEOA;
8684 }
Eric Andersen2870d962001-07-02 17:27:21 +00008685#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008686 popstring();
8687 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008688 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008689 }
8690 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8691 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008692 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008693
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008694 more = parselleft;
8695 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008696 again:
8697 more = preadfd();
8698 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008699 parselleft = parsenleft = EOF_NLEFT;
8700 return PEOF;
8701 }
8702 }
8703
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008704 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008705
8706 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008707 for (;;) {
8708 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008709
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008710 more--;
8711 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008712
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008713 if (!c)
8714 memmove(q, q + 1, more);
8715 else {
8716 q++;
8717 if (c == '\n') {
8718 parsenleft = q - parsenextc - 1;
8719 break;
8720 }
Eric Andersencb57d552001-06-28 07:25:16 +00008721 }
8722
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008723 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008724 parsenleft = q - parsenextc - 1;
8725 if (parsenleft < 0)
8726 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008727 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008728 }
8729 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008730 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008731
8732 savec = *q;
8733 *q = '\0';
8734
8735 if (vflag) {
8736 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008737 }
8738
8739 *q = savec;
8740
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008741 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008742}
8743
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008744#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008745static int
8746pgetc(void)
8747{
8748 return pgetc_as_macro();
8749}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008750
8751#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8752#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008753#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008754#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008755#endif
8756
8757/*
8758 * Same as pgetc(), but ignores PEOA.
8759 */
8760#if ENABLE_ASH_ALIAS
8761static int
8762pgetc2(void)
8763{
8764 int c;
8765
8766 do {
8767 c = pgetc_macro();
8768 } while (c == PEOA);
8769 return c;
8770}
8771#else
8772static int
8773pgetc2(void)
8774{
8775 return pgetc_macro();
8776}
8777#endif
8778
8779/*
8780 * Read a line from the script.
8781 */
8782static char *
8783pfgets(char *line, int len)
8784{
8785 char *p = line;
8786 int nleft = len;
8787 int c;
8788
8789 while (--nleft > 0) {
8790 c = pgetc2();
8791 if (c == PEOF) {
8792 if (p == line)
8793 return NULL;
8794 break;
8795 }
8796 *p++ = c;
8797 if (c == '\n')
8798 break;
8799 }
8800 *p = '\0';
8801 return line;
8802}
8803
Eric Andersenc470f442003-07-28 09:56:35 +00008804/*
8805 * Undo the last call to pgetc. Only one character may be pushed back.
8806 * PEOF may be pushed back.
8807 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008808static void
Eric Andersenc470f442003-07-28 09:56:35 +00008809pungetc(void)
8810{
8811 parsenleft++;
8812 parsenextc--;
8813}
Eric Andersencb57d552001-06-28 07:25:16 +00008814
8815/*
8816 * Push a string back onto the input at this current parsefile level.
8817 * We handle aliases this way.
8818 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008819static void
Eric Andersenc470f442003-07-28 09:56:35 +00008820pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008821{
Eric Andersencb57d552001-06-28 07:25:16 +00008822 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008823 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008824
Eric Andersenc470f442003-07-28 09:56:35 +00008825 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008826 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008827/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8828 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008829 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008830 sp->prev = parsefile->strpush;
8831 parsefile->strpush = sp;
8832 } else
8833 sp = parsefile->strpush = &(parsefile->basestrpush);
8834 sp->prevstring = parsenextc;
8835 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008836#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008837 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008838 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008839 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008840 sp->string = s;
8841 }
Eric Andersen2870d962001-07-02 17:27:21 +00008842#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008843 parsenextc = s;
8844 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008845 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008846}
8847
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008848/*
8849 * To handle the "." command, a stack of input files is used. Pushfile
8850 * adds a new entry to the stack and popfile restores the previous level.
8851 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008852static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008853pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008854{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008855 struct parsefile *pf;
8856
8857 parsefile->nleft = parsenleft;
8858 parsefile->lleft = parselleft;
8859 parsefile->nextc = parsenextc;
8860 parsefile->linno = plinno;
8861 pf = ckmalloc(sizeof(*pf));
8862 pf->prev = parsefile;
8863 pf->fd = -1;
8864 pf->strpush = NULL;
8865 pf->basestrpush.prev = NULL;
8866 parsefile = pf;
8867}
8868
8869static void
8870popfile(void)
8871{
8872 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008873
Denis Vlasenkob012b102007-02-19 22:43:01 +00008874 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008875 if (pf->fd >= 0)
8876 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008877 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008878 while (pf->strpush)
8879 popstring();
8880 parsefile = pf->prev;
8881 free(pf);
8882 parsenleft = parsefile->nleft;
8883 parselleft = parsefile->lleft;
8884 parsenextc = parsefile->nextc;
8885 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008886 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008887}
8888
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008889/*
8890 * Return to top level.
8891 */
8892static void
8893popallfiles(void)
8894{
8895 while (parsefile != &basepf)
8896 popfile();
8897}
8898
8899/*
8900 * Close the file(s) that the shell is reading commands from. Called
8901 * after a fork is done.
8902 */
8903static void
8904closescript(void)
8905{
8906 popallfiles();
8907 if (parsefile->fd > 0) {
8908 close(parsefile->fd);
8909 parsefile->fd = 0;
8910 }
8911}
8912
8913/*
8914 * Like setinputfile, but takes an open file descriptor. Call this with
8915 * interrupts off.
8916 */
8917static void
8918setinputfd(int fd, int push)
8919{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008920 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008921 if (push) {
8922 pushfile();
8923 parsefile->buf = 0;
8924 }
8925 parsefile->fd = fd;
8926 if (parsefile->buf == NULL)
8927 parsefile->buf = ckmalloc(IBUFSIZ);
8928 parselleft = parsenleft = 0;
8929 plinno = 1;
8930}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008931
Eric Andersenc470f442003-07-28 09:56:35 +00008932/*
8933 * Set the input to take input from a file. If push is set, push the
8934 * old input onto the stack first.
8935 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008936static int
8937setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008938{
8939 int fd;
8940 int fd2;
8941
Denis Vlasenkob012b102007-02-19 22:43:01 +00008942 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008943 fd = open(fname, O_RDONLY);
8944 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008945 if (flags & INPUT_NOFILE_OK)
8946 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008947 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008948 }
Eric Andersenc470f442003-07-28 09:56:35 +00008949 if (fd < 10) {
8950 fd2 = copyfd(fd, 10);
8951 close(fd);
8952 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008953 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008954 fd = fd2;
8955 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008956 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008957 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008958 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008959 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008960}
8961
Eric Andersencb57d552001-06-28 07:25:16 +00008962/*
8963 * Like setinputfile, but takes input from a string.
8964 */
Eric Andersenc470f442003-07-28 09:56:35 +00008965static void
8966setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008967{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008968 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008969 pushfile();
8970 parsenextc = string;
8971 parsenleft = strlen(string);
8972 parsefile->buf = NULL;
8973 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008974 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008975}
8976
8977
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008978/* ============ mail.c
8979 *
8980 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008981 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008983#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008984
Eric Andersencb57d552001-06-28 07:25:16 +00008985#define MAXMBOXES 10
8986
Eric Andersenc470f442003-07-28 09:56:35 +00008987/* times of mailboxes */
8988static time_t mailtime[MAXMBOXES];
8989/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008990static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008991
Eric Andersencb57d552001-06-28 07:25:16 +00008992/*
Eric Andersenc470f442003-07-28 09:56:35 +00008993 * Print appropriate message(s) if mail has arrived.
8994 * If mail_var_path_changed is set,
8995 * then the value of MAIL has mail_var_path_changed,
8996 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008997 */
Eric Andersenc470f442003-07-28 09:56:35 +00008998static void
8999chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009000{
Eric Andersencb57d552001-06-28 07:25:16 +00009001 const char *mpath;
9002 char *p;
9003 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009004 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009005 struct stackmark smark;
9006 struct stat statb;
9007
Eric Andersencb57d552001-06-28 07:25:16 +00009008 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009009 mpath = mpathset() ? mpathval() : mailval();
9010 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009011 p = padvance(&mpath, nullstr);
9012 if (p == NULL)
9013 break;
9014 if (*p == '\0')
9015 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009016 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009017#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009018 if (q[-1] != '/')
9019 abort();
9020#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009021 q[-1] = '\0'; /* delete trailing '/' */
9022 if (stat(p, &statb) < 0) {
9023 *mtp = 0;
9024 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009025 }
Eric Andersenc470f442003-07-28 09:56:35 +00009026 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9027 fprintf(
9028 stderr, snlfmt,
9029 pathopt ? pathopt : "you have mail"
9030 );
9031 }
9032 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009033 }
Eric Andersenc470f442003-07-28 09:56:35 +00009034 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009035 popstackmark(&smark);
9036}
Eric Andersencb57d552001-06-28 07:25:16 +00009037
Eric Andersenc470f442003-07-28 09:56:35 +00009038static void
9039changemail(const char *val)
9040{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009041 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009042}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009043
Denis Vlasenko131ae172007-02-18 13:00:19 +00009044#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009046
9047/* ============ ??? */
9048
Eric Andersencb57d552001-06-28 07:25:16 +00009049/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009050 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009051 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009052static void
9053setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009054{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009055 char **newparam;
9056 char **ap;
9057 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009058
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009059 for (nparam = 0; argv[nparam]; nparam++);
9060 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9061 while (*argv) {
9062 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009063 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009064 *ap = NULL;
9065 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009066 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009067 shellparam.nparam = nparam;
9068 shellparam.p = newparam;
9069#if ENABLE_ASH_GETOPTS
9070 shellparam.optind = 1;
9071 shellparam.optoff = -1;
9072#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009073}
9074
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009075/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009076 * Process shell options. The global variable argptr contains a pointer
9077 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009078 */
9079static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009080minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009081{
9082 int i;
9083
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009084 if (name) {
9085 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009086 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009087 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009088 return;
9089 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009090 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009091 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009092 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009093 out1str("Current option settings\n");
9094 for (i = 0; i < NOPTS; i++)
9095 out1fmt("%-16s%s\n", optnames(i),
9096 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009097}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009098static void
9099setoption(int flag, int val)
9100{
9101 int i;
9102
9103 for (i = 0; i < NOPTS; i++) {
9104 if (optletters(i) == flag) {
9105 optlist[i] = val;
9106 return;
9107 }
9108 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009109 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009110 /* NOTREACHED */
9111}
Eric Andersenc470f442003-07-28 09:56:35 +00009112static void
9113options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009114{
9115 char *p;
9116 int val;
9117 int c;
9118
9119 if (cmdline)
9120 minusc = NULL;
9121 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009122 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009123 if (c != '-' && c != '+')
9124 break;
9125 argptr++;
9126 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009127 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009128 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009129 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009130 if (!cmdline) {
9131 /* "-" means turn off -x and -v */
9132 if (p[0] == '\0')
9133 xflag = vflag = 0;
9134 /* "--" means reset params */
9135 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009136 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009137 }
Eric Andersenc470f442003-07-28 09:56:35 +00009138 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009139 }
Eric Andersencb57d552001-06-28 07:25:16 +00009140 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009141 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009142 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009143 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009144 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009145 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009146 } else if (c == 'o') {
9147 minus_o(*argptr, val);
9148 if (*argptr)
9149 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009150 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9151 isloginsh = 1;
9152 /* bash does not accept +-login, we also won't */
9153 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009154 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009155 isloginsh = 1;
9156 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009157 } else {
9158 setoption(c, val);
9159 }
9160 }
9161 }
9162}
9163
Eric Andersencb57d552001-06-28 07:25:16 +00009164/*
Eric Andersencb57d552001-06-28 07:25:16 +00009165 * The shift builtin command.
9166 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009167static int
Eric Andersenc470f442003-07-28 09:56:35 +00009168shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009169{
9170 int n;
9171 char **ap1, **ap2;
9172
9173 n = 1;
9174 if (argc > 1)
9175 n = number(argv[1]);
9176 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009177 ash_msg_and_raise_error("can't shift that many");
9178 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009179 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009180 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009181 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009182 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009183 }
9184 ap2 = shellparam.p;
9185 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009186#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009187 shellparam.optind = 1;
9188 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009189#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009190 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009191 return 0;
9192}
9193
Eric Andersencb57d552001-06-28 07:25:16 +00009194/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009195 * POSIX requires that 'set' (but not export or readonly) output the
9196 * variables in lexicographic order - by the locale's collating order (sigh).
9197 * Maybe we could keep them in an ordered balanced binary tree
9198 * instead of hashed lists.
9199 * For now just roll 'em through qsort for printing...
9200 */
9201static int
9202showvars(const char *sep_prefix, int on, int off)
9203{
9204 const char *sep;
9205 char **ep, **epend;
9206
9207 ep = listvars(on, off, &epend);
9208 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9209
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009210 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009211
9212 for (; ep < epend; ep++) {
9213 const char *p;
9214 const char *q;
9215
9216 p = strchrnul(*ep, '=');
9217 q = nullstr;
9218 if (*p)
9219 q = single_quote(++p);
9220 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9221 }
9222 return 0;
9223}
9224
9225/*
Eric Andersencb57d552001-06-28 07:25:16 +00009226 * The set command builtin.
9227 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009228static int
Eric Andersenc470f442003-07-28 09:56:35 +00009229setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009230{
9231 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009232 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009233 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009234 options(0);
9235 optschanged();
9236 if (*argptr != NULL) {
9237 setparam(argptr);
9238 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009239 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009240 return 0;
9241}
9242
Denis Vlasenko131ae172007-02-18 13:00:19 +00009243#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009244/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009245static void
9246change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009247{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009248 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009249 /* "get", generate */
9250 char buf[16];
9251
9252 rseed = rseed * 1103515245 + 12345;
9253 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9254 /* set without recursion */
9255 setvar(vrandom.text, buf, VNOFUNC);
9256 vrandom.flags &= ~VNOFUNC;
9257 } else {
9258 /* set/reset */
9259 rseed = strtoul(value, (char **)NULL, 10);
9260 }
Eric Andersenef02f822004-03-11 13:34:24 +00009261}
Eric Andersen16767e22004-03-16 05:14:10 +00009262#endif
9263
Denis Vlasenko131ae172007-02-18 13:00:19 +00009264#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009265static int
Eric Andersenc470f442003-07-28 09:56:35 +00009266getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009267{
9268 char *p, *q;
9269 char c = '?';
9270 int done = 0;
9271 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009272 char s[12];
9273 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009274
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009275 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009276 return 1;
9277 optnext = optfirst + *param_optind - 1;
9278
9279 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009280 p = NULL;
9281 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009282 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009283 if (p == NULL || *p == '\0') {
9284 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009285 p = *optnext;
9286 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009287 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009288 p = NULL;
9289 done = 1;
9290 goto out;
9291 }
9292 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009293 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009294 goto atend;
9295 }
9296
9297 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009298 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009299 if (*q == '\0') {
9300 if (optstr[0] == ':') {
9301 s[0] = c;
9302 s[1] = '\0';
9303 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009304 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009305 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009306 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009307 }
9308 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009309 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009310 }
9311 if (*++q == ':')
9312 q++;
9313 }
9314
9315 if (*++q == ':') {
9316 if (*p == '\0' && (p = *optnext) == NULL) {
9317 if (optstr[0] == ':') {
9318 s[0] = c;
9319 s[1] = '\0';
9320 err |= setvarsafe("OPTARG", s, 0);
9321 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009322 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009323 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009324 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009325 c = '?';
9326 }
Eric Andersenc470f442003-07-28 09:56:35 +00009327 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009328 }
9329
9330 if (p == *optnext)
9331 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009332 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009333 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009334 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009335 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009336 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009337 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 *param_optind = optnext - optfirst + 1;
9339 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009340 err |= setvarsafe("OPTIND", s, VNOFUNC);
9341 s[0] = c;
9342 s[1] = '\0';
9343 err |= setvarsafe(optvar, s, 0);
9344 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009345 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009346 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009347 flush_stdout_stderr();
9348 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009349 }
9350 return done;
9351}
Eric Andersenc470f442003-07-28 09:56:35 +00009352
9353/*
9354 * The getopts builtin. Shellparam.optnext points to the next argument
9355 * to be processed. Shellparam.optptr points to the next character to
9356 * be processed in the current argument. If shellparam.optnext is NULL,
9357 * then it's the first time getopts has been called.
9358 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009359static int
Eric Andersenc470f442003-07-28 09:56:35 +00009360getoptscmd(int argc, char **argv)
9361{
9362 char **optbase;
9363
9364 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009365 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009366 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009367 optbase = shellparam.p;
9368 if (shellparam.optind > shellparam.nparam + 1) {
9369 shellparam.optind = 1;
9370 shellparam.optoff = -1;
9371 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009372 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009373 optbase = &argv[3];
9374 if (shellparam.optind > argc - 2) {
9375 shellparam.optind = 1;
9376 shellparam.optoff = -1;
9377 }
9378 }
9379
9380 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009381 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009382}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009383#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009384
Eric Andersencb57d552001-06-28 07:25:16 +00009385
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009386/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009387
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009388/*
9389 * NEOF is returned by parsecmd when it encounters an end of file. It
9390 * must be distinct from NULL, so we use the address of a variable that
9391 * happens to be handy.
9392 */
9393static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009394#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009395static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009396static int lasttoken; /* last token read */
9397static char *wordtext; /* text of last word returned by readtoken */
9398static struct nodelist *backquotelist;
9399static union node *redirnode;
9400static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009401static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009402
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009403static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9404static void
9405raise_error_syntax(const char *msg)
9406{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009407 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009408 /* NOTREACHED */
9409}
9410
9411/*
9412 * Called when an unexpected token is read during the parse. The argument
9413 * is the token that is expected, or -1 if more than one type of token can
9414 * occur at this point.
9415 */
9416static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9417static void
9418raise_error_unexpected_syntax(int token)
9419{
9420 char msg[64];
9421 int l;
9422
9423 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9424 if (token >= 0)
9425 sprintf(msg + l, " (expecting %s)", tokname(token));
9426 raise_error_syntax(msg);
9427 /* NOTREACHED */
9428}
Eric Andersencb57d552001-06-28 07:25:16 +00009429
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009430#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009431
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009432struct heredoc {
9433 struct heredoc *next; /* next here document in list */
9434 union node *here; /* redirection node */
9435 char *eofmark; /* string indicating end of input */
9436 int striptabs; /* if set, strip leading tabs */
9437};
Eric Andersencb57d552001-06-28 07:25:16 +00009438
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009439static struct heredoc *heredoclist; /* list of here documents to read */
9440
9441/* parsing is heavily cross-recursive, need these forward decls */
9442static union node *andor(void);
9443static union node *pipeline(void);
9444static union node *parse_command(void);
9445static void parseheredoc(void);
9446static char peektoken(void);
9447static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009448
Eric Andersenc470f442003-07-28 09:56:35 +00009449static union node *
9450list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009451{
9452 union node *n1, *n2, *n3;
9453 int tok;
9454
Eric Andersenc470f442003-07-28 09:56:35 +00009455 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9456 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009457 return NULL;
9458 n1 = NULL;
9459 for (;;) {
9460 n2 = andor();
9461 tok = readtoken();
9462 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009463 if (n2->type == NPIPE) {
9464 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009465 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009466 if (n2->type != NREDIR) {
9467 n3 = stalloc(sizeof(struct nredir));
9468 n3->nredir.n = n2;
9469 n3->nredir.redirect = NULL;
9470 n2 = n3;
9471 }
9472 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009473 }
9474 }
9475 if (n1 == NULL) {
9476 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009477 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009478 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009479 n3->type = NSEMI;
9480 n3->nbinary.ch1 = n1;
9481 n3->nbinary.ch2 = n2;
9482 n1 = n3;
9483 }
9484 switch (tok) {
9485 case TBACKGND:
9486 case TSEMI:
9487 tok = readtoken();
9488 /* fall through */
9489 case TNL:
9490 if (tok == TNL) {
9491 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009492 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009493 return n1;
9494 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009495 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009496 }
Eric Andersenc470f442003-07-28 09:56:35 +00009497 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009498 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009499 return n1;
9500 break;
9501 case TEOF:
9502 if (heredoclist)
9503 parseheredoc();
9504 else
Eric Andersenc470f442003-07-28 09:56:35 +00009505 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009506 return n1;
9507 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009508 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009509 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009510 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009511 return n1;
9512 }
9513 }
9514}
9515
Eric Andersenc470f442003-07-28 09:56:35 +00009516static union node *
9517andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009518{
Eric Andersencb57d552001-06-28 07:25:16 +00009519 union node *n1, *n2, *n3;
9520 int t;
9521
Eric Andersencb57d552001-06-28 07:25:16 +00009522 n1 = pipeline();
9523 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009524 t = readtoken();
9525 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009526 t = NAND;
9527 } else if (t == TOR) {
9528 t = NOR;
9529 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009530 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009531 return n1;
9532 }
Eric Andersenc470f442003-07-28 09:56:35 +00009533 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009534 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009535 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009536 n3->type = t;
9537 n3->nbinary.ch1 = n1;
9538 n3->nbinary.ch2 = n2;
9539 n1 = n3;
9540 }
9541}
9542
Eric Andersenc470f442003-07-28 09:56:35 +00009543static union node *
9544pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009545{
Eric Andersencb57d552001-06-28 07:25:16 +00009546 union node *n1, *n2, *pipenode;
9547 struct nodelist *lp, *prev;
9548 int negate;
9549
9550 negate = 0;
9551 TRACE(("pipeline: entered\n"));
9552 if (readtoken() == TNOT) {
9553 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009554 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009555 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009556 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009557 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009558 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009559 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009560 pipenode->type = NPIPE;
9561 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009562 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009563 pipenode->npipe.cmdlist = lp;
9564 lp->n = n1;
9565 do {
9566 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009567 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009568 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009569 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009570 prev->next = lp;
9571 } while (readtoken() == TPIPE);
9572 lp->next = NULL;
9573 n1 = pipenode;
9574 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009575 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009576 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009577 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009578 n2->type = NNOT;
9579 n2->nnot.com = n1;
9580 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009581 }
9582 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009583}
9584
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009585static union node *
9586makename(void)
9587{
9588 union node *n;
9589
9590 n = stalloc(sizeof(struct narg));
9591 n->type = NARG;
9592 n->narg.next = NULL;
9593 n->narg.text = wordtext;
9594 n->narg.backquote = backquotelist;
9595 return n;
9596}
9597
9598static void
9599fixredir(union node *n, const char *text, int err)
9600{
9601 TRACE(("Fix redir %s %d\n", text, err));
9602 if (!err)
9603 n->ndup.vname = NULL;
9604
9605 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009606 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009607 else if (LONE_DASH(text))
9608 n->ndup.dupfd = -1;
9609 else {
9610 if (err)
9611 raise_error_syntax("Bad fd number");
9612 n->ndup.vname = makename();
9613 }
9614}
9615
9616/*
9617 * Returns true if the text contains nothing to expand (no dollar signs
9618 * or backquotes).
9619 */
9620static int
9621noexpand(char *text)
9622{
9623 char *p;
9624 char c;
9625
9626 p = text;
9627 while ((c = *p++) != '\0') {
9628 if (c == CTLQUOTEMARK)
9629 continue;
9630 if (c == CTLESC)
9631 p++;
9632 else if (SIT(c, BASESYNTAX) == CCTL)
9633 return 0;
9634 }
9635 return 1;
9636}
9637
9638static void
9639parsefname(void)
9640{
9641 union node *n = redirnode;
9642
9643 if (readtoken() != TWORD)
9644 raise_error_unexpected_syntax(-1);
9645 if (n->type == NHERE) {
9646 struct heredoc *here = heredoc;
9647 struct heredoc *p;
9648 int i;
9649
9650 if (quoteflag == 0)
9651 n->type = NXHERE;
9652 TRACE(("Here document %d\n", n->type));
9653 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9654 raise_error_syntax("Illegal eof marker for << redirection");
9655 rmescapes(wordtext);
9656 here->eofmark = wordtext;
9657 here->next = NULL;
9658 if (heredoclist == NULL)
9659 heredoclist = here;
9660 else {
9661 for (p = heredoclist; p->next; p = p->next);
9662 p->next = here;
9663 }
9664 } else if (n->type == NTOFD || n->type == NFROMFD) {
9665 fixredir(n, wordtext, 0);
9666 } else {
9667 n->nfile.fname = makename();
9668 }
9669}
Eric Andersencb57d552001-06-28 07:25:16 +00009670
Eric Andersenc470f442003-07-28 09:56:35 +00009671static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009672simplecmd(void)
9673{
9674 union node *args, **app;
9675 union node *n = NULL;
9676 union node *vars, **vpp;
9677 union node **rpp, *redir;
9678 int savecheckkwd;
9679
9680 args = NULL;
9681 app = &args;
9682 vars = NULL;
9683 vpp = &vars;
9684 redir = NULL;
9685 rpp = &redir;
9686
9687 savecheckkwd = CHKALIAS;
9688 for (;;) {
9689 checkkwd = savecheckkwd;
9690 switch (readtoken()) {
9691 case TWORD:
9692 n = stalloc(sizeof(struct narg));
9693 n->type = NARG;
9694 n->narg.text = wordtext;
9695 n->narg.backquote = backquotelist;
9696 if (savecheckkwd && isassignment(wordtext)) {
9697 *vpp = n;
9698 vpp = &n->narg.next;
9699 } else {
9700 *app = n;
9701 app = &n->narg.next;
9702 savecheckkwd = 0;
9703 }
9704 break;
9705 case TREDIR:
9706 *rpp = n = redirnode;
9707 rpp = &n->nfile.next;
9708 parsefname(); /* read name of redirection file */
9709 break;
9710 case TLP:
9711 if (args && app == &args->narg.next
9712 && !vars && !redir
9713 ) {
9714 struct builtincmd *bcmd;
9715 const char *name;
9716
9717 /* We have a function */
9718 if (readtoken() != TRP)
9719 raise_error_unexpected_syntax(TRP);
9720 name = n->narg.text;
9721 if (!goodname(name)
9722 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9723 ) {
9724 raise_error_syntax("Bad function name");
9725 }
9726 n->type = NDEFUN;
9727 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9728 n->narg.next = parse_command();
9729 return n;
9730 }
9731 /* fall through */
9732 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009733 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009734 goto out;
9735 }
9736 }
9737 out:
9738 *app = NULL;
9739 *vpp = NULL;
9740 *rpp = NULL;
9741 n = stalloc(sizeof(struct ncmd));
9742 n->type = NCMD;
9743 n->ncmd.args = args;
9744 n->ncmd.assign = vars;
9745 n->ncmd.redirect = redir;
9746 return n;
9747}
9748
9749static union node *
9750parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009751{
Eric Andersencb57d552001-06-28 07:25:16 +00009752 union node *n1, *n2;
9753 union node *ap, **app;
9754 union node *cp, **cpp;
9755 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009756 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009757 int t;
9758
9759 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009760 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009761
Eric Andersencb57d552001-06-28 07:25:16 +00009762 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009763 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009764 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009765 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009766 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009767 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009768 n1->type = NIF;
9769 n1->nif.test = list(0);
9770 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009771 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009772 n1->nif.ifpart = list(0);
9773 n2 = n1;
9774 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009775 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009776 n2 = n2->nif.elsepart;
9777 n2->type = NIF;
9778 n2->nif.test = list(0);
9779 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009780 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009781 n2->nif.ifpart = list(0);
9782 }
9783 if (lasttoken == TELSE)
9784 n2->nif.elsepart = list(0);
9785 else {
9786 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009787 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009788 }
Eric Andersenc470f442003-07-28 09:56:35 +00009789 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009790 break;
9791 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009792 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009793 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009794 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009795 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009796 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009797 got = readtoken();
9798 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009799 TRACE(("expecting DO got %s %s\n", tokname(got),
9800 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009802 }
9803 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009804 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009805 break;
9806 }
9807 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009808 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009809 raise_error_syntax("Bad for loop variable");
9810 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009811 n1->type = NFOR;
9812 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009813 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009814 if (readtoken() == TIN) {
9815 app = &ap;
9816 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009817 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009818 n2->type = NARG;
9819 n2->narg.text = wordtext;
9820 n2->narg.backquote = backquotelist;
9821 *app = n2;
9822 app = &n2->narg.next;
9823 }
9824 *app = NULL;
9825 n1->nfor.args = ap;
9826 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009827 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009828 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009829 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009830 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009831 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009832 n2->narg.backquote = NULL;
9833 n2->narg.next = NULL;
9834 n1->nfor.args = n2;
9835 /*
9836 * Newline or semicolon here is optional (but note
9837 * that the original Bourne shell only allowed NL).
9838 */
9839 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009840 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009841 }
Eric Andersenc470f442003-07-28 09:56:35 +00009842 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009843 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009844 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009845 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009846 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009847 break;
9848 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009849 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009850 n1->type = NCASE;
9851 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009852 raise_error_unexpected_syntax(TWORD);
9853 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009854 n2->type = NARG;
9855 n2->narg.text = wordtext;
9856 n2->narg.backquote = backquotelist;
9857 n2->narg.next = NULL;
9858 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009859 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009860 } while (readtoken() == TNL);
9861 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009862 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009863 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009864 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009865 checkkwd = CHKNL | CHKKWD;
9866 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009867 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009868 if (lasttoken == TLP)
9869 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009870 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009871 cp->type = NCLIST;
9872 app = &cp->nclist.pattern;
9873 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009874 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009875 ap->type = NARG;
9876 ap->narg.text = wordtext;
9877 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009878 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009879 break;
9880 app = &ap->narg.next;
9881 readtoken();
9882 }
9883 ap->narg.next = NULL;
9884 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009885 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009886 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009887
Eric Andersenc470f442003-07-28 09:56:35 +00009888 cpp = &cp->nclist.next;
9889
9890 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009891 t = readtoken();
9892 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009893 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009894 raise_error_unexpected_syntax(TENDCASE);
9895 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009896 }
Eric Andersenc470f442003-07-28 09:56:35 +00009897 }
Eric Andersencb57d552001-06-28 07:25:16 +00009898 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009899 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009900 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009901 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009902 n1->type = NSUBSHELL;
9903 n1->nredir.n = list(0);
9904 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009905 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009906 break;
9907 case TBEGIN:
9908 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009909 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009910 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009912 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009913 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009914 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009915 }
9916
Eric Andersenc470f442003-07-28 09:56:35 +00009917 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009918 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009919
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009920 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009921 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009922 checkkwd = CHKKWD | CHKALIAS;
9923 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009924 while (readtoken() == TREDIR) {
9925 *rpp = n2 = redirnode;
9926 rpp = &n2->nfile.next;
9927 parsefname();
9928 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009929 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009930 *rpp = NULL;
9931 if (redir) {
9932 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009933 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009934 n2->type = NREDIR;
9935 n2->nredir.n = n1;
9936 n1 = n2;
9937 }
9938 n1->nredir.redirect = redir;
9939 }
Eric Andersencb57d552001-06-28 07:25:16 +00009940 return n1;
9941}
9942
Eric Andersencb57d552001-06-28 07:25:16 +00009943/*
9944 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9945 * is not NULL, read a here document. In the latter case, eofmark is the
9946 * word which marks the end of the document and striptabs is true if
9947 * leading tabs should be stripped from the document. The argument firstc
9948 * is the first character of the input token or document.
9949 *
9950 * Because C does not have internal subroutines, I have simulated them
9951 * using goto's to implement the subroutine linkage. The following macros
9952 * will run code that appears at the end of readtoken1.
9953 */
9954
Eric Andersen2870d962001-07-02 17:27:21 +00009955#define CHECKEND() {goto checkend; checkend_return:;}
9956#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9957#define PARSESUB() {goto parsesub; parsesub_return:;}
9958#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9959#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9960#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009961
9962static int
Eric Andersenc470f442003-07-28 09:56:35 +00009963readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009964{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009965 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009966 int c = firstc;
9967 char *out;
9968 int len;
9969 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009970 struct nodelist *bqlist;
9971 smallint quotef;
9972 smallint dblquote;
9973 smallint oldstyle;
9974 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009975#if ENABLE_ASH_EXPAND_PRMT
9976 smallint pssyntax; /* we are expanding a prompt string */
9977#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009978 int varnest; /* levels of variables expansion */
9979 int arinest; /* levels of arithmetic expansion */
9980 int parenlevel; /* levels of parens in arithmetic */
9981 int dqvarnest; /* levels of variables expansion within double quotes */
9982
Eric Andersencb57d552001-06-28 07:25:16 +00009983#if __GNUC__
9984 /* Avoid longjmp clobbering */
9985 (void) &out;
9986 (void) &quotef;
9987 (void) &dblquote;
9988 (void) &varnest;
9989 (void) &arinest;
9990 (void) &parenlevel;
9991 (void) &dqvarnest;
9992 (void) &oldstyle;
9993 (void) &prevsyntax;
9994 (void) &syntax;
9995#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009996 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009997 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009998 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009999 oldstyle = 0;
10000 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010001#if ENABLE_ASH_EXPAND_PRMT
10002 pssyntax = (syntax == PSSYNTAX);
10003 if (pssyntax)
10004 syntax = DQSYNTAX;
10005#endif
10006 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010007 varnest = 0;
10008 arinest = 0;
10009 parenlevel = 0;
10010 dqvarnest = 0;
10011
10012 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010013 loop: { /* for each line, until end of word */
10014 CHECKEND(); /* set c to PEOF if at end of here document */
10015 for (;;) { /* until end of line or end of word */
10016 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010017 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010018 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010019 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010020 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010021 USTPUTC(c, out);
10022 plinno++;
10023 if (doprompt)
10024 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010025 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010026 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010027 case CWORD:
10028 USTPUTC(c, out);
10029 break;
10030 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010031 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010032 USTPUTC(CTLESC, out);
10033 USTPUTC(c, out);
10034 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010035 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010036 c = pgetc2();
10037 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010038 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010039 USTPUTC('\\', out);
10040 pungetc();
10041 } else if (c == '\n') {
10042 if (doprompt)
10043 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010044 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010045#if ENABLE_ASH_EXPAND_PRMT
10046 if (c == '$' && pssyntax) {
10047 USTPUTC(CTLESC, out);
10048 USTPUTC('\\', out);
10049 }
10050#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010051 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010052 c != '\\' && c != '`' &&
10053 c != '$' && (
10054 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010055 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010056 ) {
10057 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010058 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010059 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010060 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010061 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010062 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010063 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010064 }
10065 break;
10066 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010067 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010068 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010069 if (eofmark == NULL) {
10070 USTPUTC(CTLQUOTEMARK, out);
10071 }
Eric Andersencb57d552001-06-28 07:25:16 +000010072 break;
10073 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010074 syntax = DQSYNTAX;
10075 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010076 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010077 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010078 if (eofmark != NULL && arinest == 0
10079 && varnest == 0
10080 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010081 USTPUTC(c, out);
10082 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010083 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010084 syntax = BASESYNTAX;
10085 dblquote = 0;
10086 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010087 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010088 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010089 }
10090 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010091 case CVAR: /* '$' */
10092 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010093 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010094 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010095 if (varnest > 0) {
10096 varnest--;
10097 if (dqvarnest > 0) {
10098 dqvarnest--;
10099 }
10100 USTPUTC(CTLENDVAR, out);
10101 } else {
10102 USTPUTC(c, out);
10103 }
10104 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010105#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010106 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010107 parenlevel++;
10108 USTPUTC(c, out);
10109 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010110 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010111 if (parenlevel > 0) {
10112 USTPUTC(c, out);
10113 --parenlevel;
10114 } else {
10115 if (pgetc() == ')') {
10116 if (--arinest == 0) {
10117 USTPUTC(CTLENDARI, out);
10118 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010119 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010120 } else
10121 USTPUTC(')', out);
10122 } else {
10123 /*
10124 * unbalanced parens
10125 * (don't 2nd guess - no error)
10126 */
10127 pungetc();
10128 USTPUTC(')', out);
10129 }
10130 }
10131 break;
10132#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010133 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010134 PARSEBACKQOLD();
10135 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010136 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010137 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010138 case CIGN:
10139 break;
10140 default:
10141 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010142 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010143#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010144 if (c != PEOA)
10145#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010146 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010147
Eric Andersencb57d552001-06-28 07:25:16 +000010148 }
10149 c = pgetc_macro();
10150 }
10151 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010152 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010153#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010154 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010155 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010156#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010157 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010158 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010159 if (varnest != 0) {
10160 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010161 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010162 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010163 }
10164 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010165 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010166 out = stackblock();
10167 if (eofmark == NULL) {
10168 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010169 && quotef == 0
10170 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010171 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010172 PARSEREDIR();
10173 return lasttoken = TREDIR;
10174 } else {
10175 pungetc();
10176 }
10177 }
10178 quoteflag = quotef;
10179 backquotelist = bqlist;
10180 grabstackblock(len);
10181 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010182 lasttoken = TWORD;
10183 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010184/* end of readtoken routine */
10185
Eric Andersencb57d552001-06-28 07:25:16 +000010186/*
10187 * Check to see whether we are at the end of the here document. When this
10188 * is called, c is set to the first character of the next input line. If
10189 * we are at the end of the here document, this routine sets the c to PEOF.
10190 */
Eric Andersenc470f442003-07-28 09:56:35 +000010191checkend: {
10192 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010193#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010194 if (c == PEOA) {
10195 c = pgetc2();
10196 }
10197#endif
10198 if (striptabs) {
10199 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010200 c = pgetc2();
10201 }
Eric Andersenc470f442003-07-28 09:56:35 +000010202 }
10203 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010204 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010205 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010206
Eric Andersenc470f442003-07-28 09:56:35 +000010207 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010208 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010209 if (*p == '\n' && *q == '\0') {
10210 c = PEOF;
10211 plinno++;
10212 needprompt = doprompt;
10213 } else {
10214 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010215 }
10216 }
10217 }
10218 }
Eric Andersenc470f442003-07-28 09:56:35 +000010219 goto checkend_return;
10220}
Eric Andersencb57d552001-06-28 07:25:16 +000010221
Eric Andersencb57d552001-06-28 07:25:16 +000010222/*
10223 * Parse a redirection operator. The variable "out" points to a string
10224 * specifying the fd to be redirected. The variable "c" contains the
10225 * first character of the redirection operator.
10226 */
Eric Andersenc470f442003-07-28 09:56:35 +000010227parseredir: {
10228 char fd = *out;
10229 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010230
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010231 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010232 if (c == '>') {
10233 np->nfile.fd = 1;
10234 c = pgetc();
10235 if (c == '>')
10236 np->type = NAPPEND;
10237 else if (c == '|')
10238 np->type = NCLOBBER;
10239 else if (c == '&')
10240 np->type = NTOFD;
10241 else {
10242 np->type = NTO;
10243 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010244 }
Eric Andersenc470f442003-07-28 09:56:35 +000010245 } else { /* c == '<' */
10246 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010247 c = pgetc();
10248 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010249 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010250 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010251 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010252 np->nfile.fd = 0;
10253 }
10254 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010255 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010256 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010257 c = pgetc();
10258 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010259 heredoc->striptabs = 1;
10260 } else {
10261 heredoc->striptabs = 0;
10262 pungetc();
10263 }
10264 break;
10265
10266 case '&':
10267 np->type = NFROMFD;
10268 break;
10269
10270 case '>':
10271 np->type = NFROMTO;
10272 break;
10273
10274 default:
10275 np->type = NFROM;
10276 pungetc();
10277 break;
10278 }
Eric Andersencb57d552001-06-28 07:25:16 +000010279 }
Eric Andersenc470f442003-07-28 09:56:35 +000010280 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010281 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010282 redirnode = np;
10283 goto parseredir_return;
10284}
Eric Andersencb57d552001-06-28 07:25:16 +000010285
Eric Andersencb57d552001-06-28 07:25:16 +000010286/*
10287 * Parse a substitution. At this point, we have read the dollar sign
10288 * and nothing else.
10289 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010290
10291/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10292 * (assuming ascii char codes, as the original implementation did) */
10293#define is_special(c) \
10294 ((((unsigned int)c) - 33 < 32) \
10295 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010296parsesub: {
10297 int subtype;
10298 int typeloc;
10299 int flags;
10300 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010301 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010302
Eric Andersenc470f442003-07-28 09:56:35 +000010303 c = pgetc();
10304 if (
10305 c <= PEOA_OR_PEOF ||
10306 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10307 ) {
10308 USTPUTC('$', out);
10309 pungetc();
10310 } else if (c == '(') { /* $(command) or $((arith)) */
10311 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010312#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010313 PARSEARITH();
10314#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010315 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010316#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010317 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010318 pungetc();
10319 PARSEBACKQNEW();
10320 }
10321 } else {
10322 USTPUTC(CTLVAR, out);
10323 typeloc = out - (char *)stackblock();
10324 USTPUTC(VSNORMAL, out);
10325 subtype = VSNORMAL;
10326 if (c == '{') {
10327 c = pgetc();
10328 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010329 c = pgetc();
10330 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010331 c = '#';
10332 else
10333 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010334 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010335 subtype = 0;
10336 }
10337 if (c > PEOA_OR_PEOF && is_name(c)) {
10338 do {
10339 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010340 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010341 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010342 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010343 do {
10344 STPUTC(c, out);
10345 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010346 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010347 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010348 USTPUTC(c, out);
10349 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010350 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010351 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010352
Eric Andersenc470f442003-07-28 09:56:35 +000010353 STPUTC('=', out);
10354 flags = 0;
10355 if (subtype == 0) {
10356 switch (c) {
10357 case ':':
10358 flags = VSNUL;
10359 c = pgetc();
10360 /*FALLTHROUGH*/
10361 default:
10362 p = strchr(types, c);
10363 if (p == NULL)
10364 goto badsub;
10365 subtype = p - types + VSNORMAL;
10366 break;
10367 case '%':
10368 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010369 {
10370 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010371 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010372 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010373 c = pgetc();
10374 if (c == cc)
10375 subtype++;
10376 else
10377 pungetc();
10378 break;
10379 }
10380 }
Eric Andersenc470f442003-07-28 09:56:35 +000010381 } else {
10382 pungetc();
10383 }
10384 if (dblquote || arinest)
10385 flags |= VSQUOTE;
10386 *((char *)stackblock() + typeloc) = subtype | flags;
10387 if (subtype != VSNORMAL) {
10388 varnest++;
10389 if (dblquote || arinest) {
10390 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 }
10392 }
10393 }
Eric Andersenc470f442003-07-28 09:56:35 +000010394 goto parsesub_return;
10395}
Eric Andersencb57d552001-06-28 07:25:16 +000010396
Eric Andersencb57d552001-06-28 07:25:16 +000010397/*
10398 * Called to parse command substitutions. Newstyle is set if the command
10399 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10400 * list of commands (passed by reference), and savelen is the number of
10401 * characters on the top of the stack which must be preserved.
10402 */
Eric Andersenc470f442003-07-28 09:56:35 +000010403parsebackq: {
10404 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010405 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010406 union node *n;
10407 char *volatile str;
10408 struct jmploc jmploc;
10409 struct jmploc *volatile savehandler;
10410 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010411 smallint saveprompt = 0;
10412
Eric Andersencb57d552001-06-28 07:25:16 +000010413#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010414 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010415#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010416 savepbq = parsebackquote;
10417 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010418 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010419 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010420 exception_handler = savehandler;
10421 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010422 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010423 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010424 str = NULL;
10425 savelen = out - (char *)stackblock();
10426 if (savelen > 0) {
10427 str = ckmalloc(savelen);
10428 memcpy(str, stackblock(), savelen);
10429 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010430 savehandler = exception_handler;
10431 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010432 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010433 if (oldstyle) {
10434 /* We must read until the closing backquote, giving special
10435 treatment to some slashes, and then push the string and
10436 reread it as input, interpreting it normally. */
10437 char *pout;
10438 int pc;
10439 size_t psavelen;
10440 char *pstr;
10441
10442
10443 STARTSTACKSTR(pout);
10444 for (;;) {
10445 if (needprompt) {
10446 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010447 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010448 pc = pgetc();
10449 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010450 case '`':
10451 goto done;
10452
10453 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010454 pc = pgetc();
10455 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010456 plinno++;
10457 if (doprompt)
10458 setprompt(2);
10459 /*
10460 * If eating a newline, avoid putting
10461 * the newline into the new character
10462 * stream (via the STPUTC after the
10463 * switch).
10464 */
10465 continue;
10466 }
10467 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010468 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010469 STPUTC('\\', pout);
10470 if (pc > PEOA_OR_PEOF) {
10471 break;
10472 }
10473 /* fall through */
10474
10475 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010476#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010477 case PEOA:
10478#endif
10479 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010480 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010481
10482 case '\n':
10483 plinno++;
10484 needprompt = doprompt;
10485 break;
10486
10487 default:
10488 break;
10489 }
10490 STPUTC(pc, pout);
10491 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010492 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010493 STPUTC('\0', pout);
10494 psavelen = pout - (char *)stackblock();
10495 if (psavelen > 0) {
10496 pstr = grabstackstr(pout);
10497 setinputstring(pstr);
10498 }
10499 }
10500 nlpp = &bqlist;
10501 while (*nlpp)
10502 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010503 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010504 (*nlpp)->next = NULL;
10505 parsebackquote = oldstyle;
10506
10507 if (oldstyle) {
10508 saveprompt = doprompt;
10509 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010510 }
10511
Eric Andersenc470f442003-07-28 09:56:35 +000010512 n = list(2);
10513
10514 if (oldstyle)
10515 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010516 else if (readtoken() != TRP)
10517 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010518
10519 (*nlpp)->n = n;
10520 if (oldstyle) {
10521 /*
10522 * Start reading from old file again, ignoring any pushed back
10523 * tokens left from the backquote parsing
10524 */
10525 popfile();
10526 tokpushback = 0;
10527 }
10528 while (stackblocksize() <= savelen)
10529 growstackblock();
10530 STARTSTACKSTR(out);
10531 if (str) {
10532 memcpy(out, str, savelen);
10533 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010534 INT_OFF;
10535 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010536 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010537 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010538 }
10539 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010540 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010541 if (arinest || dblquote)
10542 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10543 else
10544 USTPUTC(CTLBACKQ, out);
10545 if (oldstyle)
10546 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010547 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010548}
10549
Denis Vlasenko131ae172007-02-18 13:00:19 +000010550#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010551/*
10552 * Parse an arithmetic expansion (indicate start of one and set state)
10553 */
Eric Andersenc470f442003-07-28 09:56:35 +000010554parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010555 if (++arinest == 1) {
10556 prevsyntax = syntax;
10557 syntax = ARISYNTAX;
10558 USTPUTC(CTLARI, out);
10559 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010560 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010561 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010562 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010563 } else {
10564 /*
10565 * we collapse embedded arithmetic expansion to
10566 * parenthesis, which should be equivalent
10567 */
10568 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010569 }
Eric Andersenc470f442003-07-28 09:56:35 +000010570 goto parsearith_return;
10571}
10572#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010573
Eric Andersenc470f442003-07-28 09:56:35 +000010574} /* end of readtoken */
10575
Eric Andersencb57d552001-06-28 07:25:16 +000010576/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010577 * Read the next input token.
10578 * If the token is a word, we set backquotelist to the list of cmds in
10579 * backquotes. We set quoteflag to true if any part of the word was
10580 * quoted.
10581 * If the token is TREDIR, then we set redirnode to a structure containing
10582 * the redirection.
10583 * In all cases, the variable startlinno is set to the number of the line
10584 * on which the token starts.
10585 *
10586 * [Change comment: here documents and internal procedures]
10587 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10588 * word parsing code into a separate routine. In this case, readtoken
10589 * doesn't need to have any internal procedures, but parseword does.
10590 * We could also make parseoperator in essence the main routine, and
10591 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010592 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010593#define NEW_xxreadtoken
10594#ifdef NEW_xxreadtoken
10595/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010596static const char xxreadtoken_chars[7] ALIGN1 = {
10597 '\n', '(', ')', '&', '|', ';', 0
10598};
Eric Andersencb57d552001-06-28 07:25:16 +000010599
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010600static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010601 TNL, TLP, TRP, /* only single occurrence allowed */
10602 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10603 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010604 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010605};
10606
10607#define xxreadtoken_doubles \
10608 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10609#define xxreadtoken_singles \
10610 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10611
10612static int
10613xxreadtoken(void)
10614{
10615 int c;
10616
10617 if (tokpushback) {
10618 tokpushback = 0;
10619 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010620 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010621 if (needprompt) {
10622 setprompt(2);
10623 }
10624 startlinno = plinno;
10625 for (;;) { /* until token or start of word found */
10626 c = pgetc_macro();
10627
10628 if ((c != ' ') && (c != '\t')
10629#if ENABLE_ASH_ALIAS
10630 && (c != PEOA)
10631#endif
10632 ) {
10633 if (c == '#') {
10634 while ((c = pgetc()) != '\n' && c != PEOF);
10635 pungetc();
10636 } else if (c == '\\') {
10637 if (pgetc() != '\n') {
10638 pungetc();
10639 goto READTOKEN1;
10640 }
10641 startlinno = ++plinno;
10642 if (doprompt)
10643 setprompt(2);
10644 } else {
10645 const char *p
10646 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10647
10648 if (c != PEOF) {
10649 if (c == '\n') {
10650 plinno++;
10651 needprompt = doprompt;
10652 }
10653
10654 p = strchr(xxreadtoken_chars, c);
10655 if (p == NULL) {
10656 READTOKEN1:
10657 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10658 }
10659
10660 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10661 if (pgetc() == *p) { /* double occurrence? */
10662 p += xxreadtoken_doubles + 1;
10663 } else {
10664 pungetc();
10665 }
10666 }
10667 }
10668 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10669 }
10670 }
10671 } /* for */
10672}
10673#else
10674#define RETURN(token) return lasttoken = token
10675static int
10676xxreadtoken(void)
10677{
10678 int c;
10679
10680 if (tokpushback) {
10681 tokpushback = 0;
10682 return lasttoken;
10683 }
10684 if (needprompt) {
10685 setprompt(2);
10686 }
10687 startlinno = plinno;
10688 for (;;) { /* until token or start of word found */
10689 c = pgetc_macro();
10690 switch (c) {
10691 case ' ': case '\t':
10692#if ENABLE_ASH_ALIAS
10693 case PEOA:
10694#endif
10695 continue;
10696 case '#':
10697 while ((c = pgetc()) != '\n' && c != PEOF);
10698 pungetc();
10699 continue;
10700 case '\\':
10701 if (pgetc() == '\n') {
10702 startlinno = ++plinno;
10703 if (doprompt)
10704 setprompt(2);
10705 continue;
10706 }
10707 pungetc();
10708 goto breakloop;
10709 case '\n':
10710 plinno++;
10711 needprompt = doprompt;
10712 RETURN(TNL);
10713 case PEOF:
10714 RETURN(TEOF);
10715 case '&':
10716 if (pgetc() == '&')
10717 RETURN(TAND);
10718 pungetc();
10719 RETURN(TBACKGND);
10720 case '|':
10721 if (pgetc() == '|')
10722 RETURN(TOR);
10723 pungetc();
10724 RETURN(TPIPE);
10725 case ';':
10726 if (pgetc() == ';')
10727 RETURN(TENDCASE);
10728 pungetc();
10729 RETURN(TSEMI);
10730 case '(':
10731 RETURN(TLP);
10732 case ')':
10733 RETURN(TRP);
10734 default:
10735 goto breakloop;
10736 }
10737 }
10738 breakloop:
10739 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10740#undef RETURN
10741}
10742#endif /* NEW_xxreadtoken */
10743
10744static int
10745readtoken(void)
10746{
10747 int t;
10748#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010749 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010750#endif
10751
10752#if ENABLE_ASH_ALIAS
10753 top:
10754#endif
10755
10756 t = xxreadtoken();
10757
10758 /*
10759 * eat newlines
10760 */
10761 if (checkkwd & CHKNL) {
10762 while (t == TNL) {
10763 parseheredoc();
10764 t = xxreadtoken();
10765 }
10766 }
10767
10768 if (t != TWORD || quoteflag) {
10769 goto out;
10770 }
10771
10772 /*
10773 * check for keywords
10774 */
10775 if (checkkwd & CHKKWD) {
10776 const char *const *pp;
10777
10778 pp = findkwd(wordtext);
10779 if (pp) {
10780 lasttoken = t = pp - tokname_array;
10781 TRACE(("keyword %s recognized\n", tokname(t)));
10782 goto out;
10783 }
10784 }
10785
10786 if (checkkwd & CHKALIAS) {
10787#if ENABLE_ASH_ALIAS
10788 struct alias *ap;
10789 ap = lookupalias(wordtext, 1);
10790 if (ap != NULL) {
10791 if (*ap->val) {
10792 pushstring(ap->val, ap);
10793 }
10794 goto top;
10795 }
10796#endif
10797 }
10798 out:
10799 checkkwd = 0;
10800#if DEBUG
10801 if (!alreadyseen)
10802 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10803 else
10804 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10805#endif
10806 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010807}
10808
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010809static char
10810peektoken(void)
10811{
10812 int t;
10813
10814 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010815 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010816 return tokname_array[t][0];
10817}
Eric Andersencb57d552001-06-28 07:25:16 +000010818
10819/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010820 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10821 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010822 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010823static union node *
10824parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010825{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010827
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010828 tokpushback = 0;
10829 doprompt = interact;
10830 if (doprompt)
10831 setprompt(doprompt);
10832 needprompt = 0;
10833 t = readtoken();
10834 if (t == TEOF)
10835 return NEOF;
10836 if (t == TNL)
10837 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010838 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010839 return list(1);
10840}
10841
10842/*
10843 * Input any here documents.
10844 */
10845static void
10846parseheredoc(void)
10847{
10848 struct heredoc *here;
10849 union node *n;
10850
10851 here = heredoclist;
10852 heredoclist = 0;
10853
10854 while (here) {
10855 if (needprompt) {
10856 setprompt(2);
10857 }
10858 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10859 here->eofmark, here->striptabs);
10860 n = stalloc(sizeof(struct narg));
10861 n->narg.type = NARG;
10862 n->narg.next = NULL;
10863 n->narg.text = wordtext;
10864 n->narg.backquote = backquotelist;
10865 here->here->nhere.doc = n;
10866 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010867 }
Eric Andersencb57d552001-06-28 07:25:16 +000010868}
10869
10870
10871/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010872 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010873 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010874#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010875static const char *
10876expandstr(const char *ps)
10877{
10878 union node n;
10879
10880 /* XXX Fix (char *) cast. */
10881 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010882 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010883 popfile();
10884
10885 n.narg.type = NARG;
10886 n.narg.next = NULL;
10887 n.narg.text = wordtext;
10888 n.narg.backquote = backquotelist;
10889
10890 expandarg(&n, NULL, 0);
10891 return stackblock();
10892}
10893#endif
10894
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010895/*
10896 * Execute a command or commands contained in a string.
10897 */
10898static int
10899evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010900{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010901 union node *n;
10902 struct stackmark smark;
10903 int skip;
10904
10905 setinputstring(s);
10906 setstackmark(&smark);
10907
10908 skip = 0;
10909 while ((n = parsecmd(0)) != NEOF) {
10910 evaltree(n, 0);
10911 popstackmark(&smark);
10912 skip = evalskip;
10913 if (skip)
10914 break;
10915 }
10916 popfile();
10917
10918 skip &= mask;
10919 evalskip = skip;
10920 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010921}
10922
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010923/*
10924 * The eval command.
10925 */
10926static int
10927evalcmd(int argc, char **argv)
10928{
10929 char *p;
10930 char *concat;
10931 char **ap;
10932
10933 if (argc > 1) {
10934 p = argv[1];
10935 if (argc > 2) {
10936 STARTSTACKSTR(concat);
10937 ap = argv + 2;
10938 for (;;) {
10939 concat = stack_putstr(p, concat);
10940 p = *ap++;
10941 if (p == NULL)
10942 break;
10943 STPUTC(' ', concat);
10944 }
10945 STPUTC('\0', concat);
10946 p = grabstackstr(concat);
10947 }
10948 evalstring(p, ~SKIPEVAL);
10949
10950 }
10951 return exitstatus;
10952}
10953
10954/*
10955 * Read and execute commands. "Top" is nonzero for the top level command
10956 * loop; it turns on prompting if the shell is interactive.
10957 */
10958static int
10959cmdloop(int top)
10960{
10961 union node *n;
10962 struct stackmark smark;
10963 int inter;
10964 int numeof = 0;
10965
10966 TRACE(("cmdloop(%d) called\n", top));
10967 for (;;) {
10968 int skip;
10969
10970 setstackmark(&smark);
10971#if JOBS
10972 if (jobctl)
10973 showjobs(stderr, SHOW_CHANGED);
10974#endif
10975 inter = 0;
10976 if (iflag && top) {
10977 inter++;
10978#if ENABLE_ASH_MAIL
10979 chkmail();
10980#endif
10981 }
10982 n = parsecmd(inter);
10983 /* showtree(n); DEBUG */
10984 if (n == NEOF) {
10985 if (!top || numeof >= 50)
10986 break;
10987 if (!stoppedjobs()) {
10988 if (!Iflag)
10989 break;
10990 out2str("\nUse \"exit\" to leave shell.\n");
10991 }
10992 numeof++;
10993 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010994 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10995 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010996 numeof = 0;
10997 evaltree(n, 0);
10998 }
10999 popstackmark(&smark);
11000 skip = evalskip;
11001
11002 if (skip) {
11003 evalskip = 0;
11004 return skip & SKIPEVAL;
11005 }
11006 }
11007 return 0;
11008}
11009
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011010/*
11011 * Take commands from a file. To be compatible we should do a path
11012 * search for the file, which is necessary to find sub-commands.
11013 */
11014static char *
11015find_dot_file(char *name)
11016{
11017 char *fullname;
11018 const char *path = pathval();
11019 struct stat statb;
11020
11021 /* don't try this for absolute or relative paths */
11022 if (strchr(name, '/'))
11023 return name;
11024
11025 while ((fullname = padvance(&path, name)) != NULL) {
11026 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11027 /*
11028 * Don't bother freeing here, since it will
11029 * be freed by the caller.
11030 */
11031 return fullname;
11032 }
11033 stunalloc(fullname);
11034 }
11035
11036 /* not found in the PATH */
11037 ash_msg_and_raise_error("%s: not found", name);
11038 /* NOTREACHED */
11039}
11040
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011041static int
11042dotcmd(int argc, char **argv)
11043{
11044 struct strlist *sp;
11045 volatile struct shparam saveparam;
11046 int status = 0;
11047
11048 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011049 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011050
11051 if (argc >= 2) { /* That's what SVR2 does */
11052 char *fullname;
11053
11054 fullname = find_dot_file(argv[1]);
11055
11056 if (argc > 2) {
11057 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011058 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011059 shellparam.nparam = argc - 2;
11060 shellparam.p = argv + 2;
11061 };
11062
11063 setinputfile(fullname, INPUT_PUSH_FILE);
11064 commandname = fullname;
11065 cmdloop(0);
11066 popfile();
11067
11068 if (argc > 2) {
11069 freeparam(&shellparam);
11070 shellparam = saveparam;
11071 };
11072 status = exitstatus;
11073 }
11074 return status;
11075}
11076
11077static int
11078exitcmd(int argc, char **argv)
11079{
11080 if (stoppedjobs())
11081 return 0;
11082 if (argc > 1)
11083 exitstatus = number(argv[1]);
11084 raise_exception(EXEXIT);
11085 /* NOTREACHED */
11086}
11087
11088#if ENABLE_ASH_BUILTIN_ECHO
11089static int
11090echocmd(int argc, char **argv)
11091{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011092 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011093}
11094#endif
11095
11096#if ENABLE_ASH_BUILTIN_TEST
11097static int
11098testcmd(int argc, char **argv)
11099{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011100 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011101}
11102#endif
11103
11104/*
11105 * Read a file containing shell functions.
11106 */
11107static void
11108readcmdfile(char *name)
11109{
11110 setinputfile(name, INPUT_PUSH_FILE);
11111 cmdloop(0);
11112 popfile();
11113}
11114
11115
Denis Vlasenkocc571512007-02-23 21:10:35 +000011116/* ============ find_command inplementation */
11117
11118/*
11119 * Resolve a command name. If you change this routine, you may have to
11120 * change the shellexec routine as well.
11121 */
11122static void
11123find_command(char *name, struct cmdentry *entry, int act, const char *path)
11124{
11125 struct tblentry *cmdp;
11126 int idx;
11127 int prev;
11128 char *fullname;
11129 struct stat statb;
11130 int e;
11131 int updatetbl;
11132 struct builtincmd *bcmd;
11133
11134 /* If name contains a slash, don't use PATH or hash table */
11135 if (strchr(name, '/') != NULL) {
11136 entry->u.index = -1;
11137 if (act & DO_ABS) {
11138 while (stat(name, &statb) < 0) {
11139#ifdef SYSV
11140 if (errno == EINTR)
11141 continue;
11142#endif
11143 entry->cmdtype = CMDUNKNOWN;
11144 return;
11145 }
11146 }
11147 entry->cmdtype = CMDNORMAL;
11148 return;
11149 }
11150
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011151/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011152
11153 updatetbl = (path == pathval());
11154 if (!updatetbl) {
11155 act |= DO_ALTPATH;
11156 if (strstr(path, "%builtin") != NULL)
11157 act |= DO_ALTBLTIN;
11158 }
11159
11160 /* If name is in the table, check answer will be ok */
11161 cmdp = cmdlookup(name, 0);
11162 if (cmdp != NULL) {
11163 int bit;
11164
11165 switch (cmdp->cmdtype) {
11166 default:
11167#if DEBUG
11168 abort();
11169#endif
11170 case CMDNORMAL:
11171 bit = DO_ALTPATH;
11172 break;
11173 case CMDFUNCTION:
11174 bit = DO_NOFUNC;
11175 break;
11176 case CMDBUILTIN:
11177 bit = DO_ALTBLTIN;
11178 break;
11179 }
11180 if (act & bit) {
11181 updatetbl = 0;
11182 cmdp = NULL;
11183 } else if (cmdp->rehash == 0)
11184 /* if not invalidated by cd, we're done */
11185 goto success;
11186 }
11187
11188 /* If %builtin not in path, check for builtin next */
11189 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011190 if (bcmd) {
11191 if (IS_BUILTIN_REGULAR(bcmd))
11192 goto builtin_success;
11193 if (act & DO_ALTPATH) {
11194 if (!(act & DO_ALTBLTIN))
11195 goto builtin_success;
11196 } else if (builtinloc <= 0) {
11197 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011198 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011199 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011200
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011201#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011202 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011203 entry->cmdtype = CMDNORMAL;
11204 entry->u.index = -1;
11205 return;
11206 }
11207#endif
11208
Denis Vlasenkocc571512007-02-23 21:10:35 +000011209 /* We have to search path. */
11210 prev = -1; /* where to start */
11211 if (cmdp && cmdp->rehash) { /* doing a rehash */
11212 if (cmdp->cmdtype == CMDBUILTIN)
11213 prev = builtinloc;
11214 else
11215 prev = cmdp->param.index;
11216 }
11217
11218 e = ENOENT;
11219 idx = -1;
11220 loop:
11221 while ((fullname = padvance(&path, name)) != NULL) {
11222 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011223 /* NB: code below will still use fullname
11224 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011225 idx++;
11226 if (pathopt) {
11227 if (prefix(pathopt, "builtin")) {
11228 if (bcmd)
11229 goto builtin_success;
11230 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011231 } else if (!(act & DO_NOFUNC)
11232 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011233 /* handled below */
11234 } else {
11235 /* ignore unimplemented options */
11236 continue;
11237 }
11238 }
11239 /* if rehash, don't redo absolute path names */
11240 if (fullname[0] == '/' && idx <= prev) {
11241 if (idx < prev)
11242 continue;
11243 TRACE(("searchexec \"%s\": no change\n", name));
11244 goto success;
11245 }
11246 while (stat(fullname, &statb) < 0) {
11247#ifdef SYSV
11248 if (errno == EINTR)
11249 continue;
11250#endif
11251 if (errno != ENOENT && errno != ENOTDIR)
11252 e = errno;
11253 goto loop;
11254 }
11255 e = EACCES; /* if we fail, this will be the error */
11256 if (!S_ISREG(statb.st_mode))
11257 continue;
11258 if (pathopt) { /* this is a %func directory */
11259 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011260 /* NB: stalloc will return space pointed by fullname
11261 * (because we don't have any intervening allocations
11262 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011263 readcmdfile(fullname);
11264 cmdp = cmdlookup(name, 0);
11265 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11266 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11267 stunalloc(fullname);
11268 goto success;
11269 }
11270 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11271 if (!updatetbl) {
11272 entry->cmdtype = CMDNORMAL;
11273 entry->u.index = idx;
11274 return;
11275 }
11276 INT_OFF;
11277 cmdp = cmdlookup(name, 1);
11278 cmdp->cmdtype = CMDNORMAL;
11279 cmdp->param.index = idx;
11280 INT_ON;
11281 goto success;
11282 }
11283
11284 /* We failed. If there was an entry for this command, delete it */
11285 if (cmdp && updatetbl)
11286 delete_cmd_entry();
11287 if (act & DO_ERR)
11288 ash_msg("%s: %s", name, errmsg(e, "not found"));
11289 entry->cmdtype = CMDUNKNOWN;
11290 return;
11291
11292 builtin_success:
11293 if (!updatetbl) {
11294 entry->cmdtype = CMDBUILTIN;
11295 entry->u.cmd = bcmd;
11296 return;
11297 }
11298 INT_OFF;
11299 cmdp = cmdlookup(name, 1);
11300 cmdp->cmdtype = CMDBUILTIN;
11301 cmdp->param.cmd = bcmd;
11302 INT_ON;
11303 success:
11304 cmdp->rehash = 0;
11305 entry->cmdtype = cmdp->cmdtype;
11306 entry->u = cmdp->param;
11307}
11308
11309
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011310/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011311
Eric Andersencb57d552001-06-28 07:25:16 +000011312/*
Eric Andersencb57d552001-06-28 07:25:16 +000011313 * The trap builtin.
11314 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011315static int
Eric Andersenc470f442003-07-28 09:56:35 +000011316trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011317{
11318 char *action;
11319 char **ap;
11320 int signo;
11321
Eric Andersenc470f442003-07-28 09:56:35 +000011322 nextopt(nullstr);
11323 ap = argptr;
11324 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011325 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011326 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011327 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011328
Rob Landleyc9c1a412006-07-12 19:17:55 +000011329 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011330 out1fmt("trap -- %s %s\n",
11331 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011332 }
11333 }
11334 return 0;
11335 }
Eric Andersenc470f442003-07-28 09:56:35 +000011336 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011337 action = NULL;
11338 else
11339 action = *ap++;
11340 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011341 signo = get_signum(*ap);
11342 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011343 ash_msg_and_raise_error("%s: bad trap", *ap);
11344 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011345 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011346 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011347 action = NULL;
11348 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011349 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011350 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011351 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011352 trap[signo] = action;
11353 if (signo != 0)
11354 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011355 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011356 ap++;
11357 }
11358 return 0;
11359}
11360
Eric Andersenc470f442003-07-28 09:56:35 +000011361
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011362/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011363
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011364#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011365/*
11366 * Lists available builtins
11367 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011368static int
11369helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011370{
11371 int col, i;
11372
11373 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011374 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011375 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011376 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011377 if (col > 60) {
11378 out1fmt("\n");
11379 col = 0;
11380 }
11381 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011382#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011383 {
11384 const char *a = applet_names;
11385 while (*a) {
11386 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11387 if (col > 60) {
11388 out1fmt("\n");
11389 col = 0;
11390 }
11391 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011392 }
11393 }
11394#endif
11395 out1fmt("\n\n");
11396 return EXIT_SUCCESS;
11397}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011398#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011399
Eric Andersencb57d552001-06-28 07:25:16 +000011400/*
Eric Andersencb57d552001-06-28 07:25:16 +000011401 * The export and readonly commands.
11402 */
Eric Andersenc470f442003-07-28 09:56:35 +000011403static int
11404exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011405{
11406 struct var *vp;
11407 char *name;
11408 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011409 char **aptr;
11410 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011411
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011412 if (nextopt("p") != 'p') {
11413 aptr = argptr;
11414 name = *aptr;
11415 if (name) {
11416 do {
11417 p = strchr(name, '=');
11418 if (p != NULL) {
11419 p++;
11420 } else {
11421 vp = *findvar(hashvar(name), name);
11422 if (vp) {
11423 vp->flags |= flag;
11424 continue;
11425 }
Eric Andersencb57d552001-06-28 07:25:16 +000011426 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011427 setvar(name, p, flag);
11428 } while ((name = *++aptr) != NULL);
11429 return 0;
11430 }
Eric Andersencb57d552001-06-28 07:25:16 +000011431 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011432 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011433 return 0;
11434}
11435
Eric Andersencb57d552001-06-28 07:25:16 +000011436/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011437 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011438 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011439static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011440unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011441{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011442 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011444 cmdp = cmdlookup(name, 0);
11445 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11446 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011447}
11448
Eric Andersencb57d552001-06-28 07:25:16 +000011449/*
Eric Andersencb57d552001-06-28 07:25:16 +000011450 * The unset builtin command. We unset the function before we unset the
11451 * variable to allow a function to be unset when there is a readonly variable
11452 * with the same name.
11453 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011454static int
Eric Andersenc470f442003-07-28 09:56:35 +000011455unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011456{
11457 char **ap;
11458 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011459 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011460 int ret = 0;
11461
11462 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011463 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011464 }
Eric Andersencb57d552001-06-28 07:25:16 +000011465
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011466 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011467 if (flag != 'f') {
11468 i = unsetvar(*ap);
11469 ret |= i;
11470 if (!(i & 2))
11471 continue;
11472 }
11473 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011474 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011475 }
Eric Andersenc470f442003-07-28 09:56:35 +000011476 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011477}
11478
11479
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011480/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011481
Eric Andersenc470f442003-07-28 09:56:35 +000011482#include <sys/times.h>
11483
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011484static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011485 ' ', offsetof(struct tms, tms_utime),
11486 '\n', offsetof(struct tms, tms_stime),
11487 ' ', offsetof(struct tms, tms_cutime),
11488 '\n', offsetof(struct tms, tms_cstime),
11489 0
11490};
Eric Andersencb57d552001-06-28 07:25:16 +000011491
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011492static int
11493timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011494{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011495 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011496 const unsigned char *p;
11497 struct tms buf;
11498
11499 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011500 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011501
11502 p = timescmd_str;
11503 do {
11504 t = *(clock_t *)(((char *) &buf) + p[1]);
11505 s = t / clk_tck;
11506 out1fmt("%ldm%ld.%.3lds%c",
11507 s/60, s%60,
11508 ((t - s * clk_tck) * 1000) / clk_tck,
11509 p[0]);
11510 } while (*(p += 2));
11511
Eric Andersencb57d552001-06-28 07:25:16 +000011512 return 0;
11513}
11514
Denis Vlasenko131ae172007-02-18 13:00:19 +000011515#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011516static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011517dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011518{
Eric Andersened9ecf72004-06-22 08:29:45 +000011519 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011520 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011521
Denis Vlasenkob012b102007-02-19 22:43:01 +000011522 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011523 result = arith(s, &errcode);
11524 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011525 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011526 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011527 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011529 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011530 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011531 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011532 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011533 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011534
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011535 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011536}
Eric Andersenc470f442003-07-28 09:56:35 +000011537
Eric Andersenc470f442003-07-28 09:56:35 +000011538/*
Eric Andersen90898442003-08-06 11:20:52 +000011539 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11540 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11541 *
11542 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011543 */
11544static int
Eric Andersen90898442003-08-06 11:20:52 +000011545letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011546{
Eric Andersenc470f442003-07-28 09:56:35 +000011547 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011548 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011549
Eric Andersen90898442003-08-06 11:20:52 +000011550 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011551 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011552 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011553 for (ap = argv + 1; *ap; ap++) {
11554 i = dash_arith(*ap);
11555 }
Eric Andersenc470f442003-07-28 09:56:35 +000011556
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011557 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011558}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011559#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011560
Eric Andersenc470f442003-07-28 09:56:35 +000011561
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011562/* ============ miscbltin.c
11563 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011564 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011565 */
11566
11567#undef rflag
11568
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011569#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011570typedef enum __rlimit_resource rlim_t;
11571#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011572
Eric Andersenc470f442003-07-28 09:56:35 +000011573/*
11574 * The read builtin. The -e option causes backslashes to escape the
11575 * following character.
11576 *
11577 * This uses unbuffered input, which may be avoidable in some cases.
11578 */
Eric Andersenc470f442003-07-28 09:56:35 +000011579static int
11580readcmd(int argc, char **argv)
11581{
11582 char **ap;
11583 int backslash;
11584 char c;
11585 int rflag;
11586 char *prompt;
11587 const char *ifs;
11588 char *p;
11589 int startword;
11590 int status;
11591 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011592#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011593 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011594 int nchars = 0;
11595 int silent = 0;
11596 struct termios tty, old_tty;
11597#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011598#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011599 fd_set set;
11600 struct timeval ts;
11601
11602 ts.tv_sec = ts.tv_usec = 0;
11603#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011604
11605 rflag = 0;
11606 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011607#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011608 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011609#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011610 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011611#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011612 while ((i = nextopt("p:rt:")) != '\0')
11613#else
11614 while ((i = nextopt("p:r")) != '\0')
11615#endif
11616 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011617 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011618 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011619 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011620 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011621#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011622 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011623 nchars = bb_strtou(optionarg, NULL, 10);
11624 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011625 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011626 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011627 break;
11628 case 's':
11629 silent = 1;
11630 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011631#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011633 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011634 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011635 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011636 /* EINVAL means number is ok, but not terminated by NUL */
11637 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011638 char *p2;
11639 if (*++p) {
11640 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011641 ts.tv_usec = bb_strtou(p, &p2, 10);
11642 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011643 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011644 scale = p2 - p;
11645 /* normalize to usec */
11646 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011647 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011648 while (scale++ < 6)
11649 ts.tv_usec *= 10;
11650 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011651 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011652 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011653 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011654 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011655 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011656 }
Paul Fox02eb9342005-09-07 16:56:02 +000011657 break;
11658#endif
11659 case 'r':
11660 rflag = 1;
11661 break;
11662 default:
11663 break;
11664 }
Eric Andersenc470f442003-07-28 09:56:35 +000011665 }
11666 if (prompt && isatty(0)) {
11667 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011668 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011669 ap = argptr;
11670 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011671 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011672 ifs = bltinlookup("IFS");
11673 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011674 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011675#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011676 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011677 if (tcgetattr(0, &tty) != 0) {
11678 /* Not a tty */
11679 n_flag = 0;
11680 silent = 0;
11681 } else {
11682 old_tty = tty;
11683 if (n_flag) {
11684 tty.c_lflag &= ~ICANON;
11685 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11686 }
11687 if (silent) {
11688 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11689 }
11690 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011691 }
Paul Fox02eb9342005-09-07 16:56:02 +000011692 }
11693#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011694#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011695 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011696 FD_ZERO(&set);
11697 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011698
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011699 /* poll-based wait produces bigger code, using select */
11700 i = select(1, &set, NULL, NULL, &ts);
11701 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011702#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011703 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011704 tcsetattr(0, TCSANOW, &old_tty);
11705#endif
11706 return 1;
11707 }
11708 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011709#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011710 status = 0;
11711 startword = 1;
11712 backslash = 0;
11713 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011714 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011715 if (read(0, &c, 1) != 1) {
11716 status = 1;
11717 break;
11718 }
11719 if (c == '\0')
11720 continue;
11721 if (backslash) {
11722 backslash = 0;
11723 if (c != '\n')
11724 goto put;
11725 continue;
11726 }
11727 if (!rflag && c == '\\') {
11728 backslash++;
11729 continue;
11730 }
11731 if (c == '\n')
11732 break;
11733 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11734 continue;
11735 }
11736 startword = 0;
11737 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11738 STACKSTRNUL(p);
11739 setvar(*ap, stackblock(), 0);
11740 ap++;
11741 startword = 1;
11742 STARTSTACKSTR(p);
11743 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011744 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011745 STPUTC(c, p);
11746 }
11747 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011748/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011749#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011750 while (!n_flag || --nchars);
11751#else
11752 while (1);
11753#endif
11754
11755#if ENABLE_ASH_READ_NCHARS
11756 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011757 tcsetattr(0, TCSANOW, &old_tty);
11758#endif
11759
Eric Andersenc470f442003-07-28 09:56:35 +000011760 STACKSTRNUL(p);
11761 /* Remove trailing blanks */
11762 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11763 *p = '\0';
11764 setvar(*ap, stackblock(), 0);
11765 while (*++ap != NULL)
11766 setvar(*ap, nullstr, 0);
11767 return status;
11768}
11769
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011770static int
11771umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011772{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011773 static const char permuser[3] ALIGN1 = "ugo";
11774 static const char permmode[3] ALIGN1 = "rwx";
11775 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011776 S_IRUSR, S_IWUSR, S_IXUSR,
11777 S_IRGRP, S_IWGRP, S_IXGRP,
11778 S_IROTH, S_IWOTH, S_IXOTH
11779 };
11780
11781 char *ap;
11782 mode_t mask;
11783 int i;
11784 int symbolic_mode = 0;
11785
11786 while (nextopt("S") != '\0') {
11787 symbolic_mode = 1;
11788 }
11789
Denis Vlasenkob012b102007-02-19 22:43:01 +000011790 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011791 mask = umask(0);
11792 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011793 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011794
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011795 ap = *argptr;
11796 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011797 if (symbolic_mode) {
11798 char buf[18];
11799 char *p = buf;
11800
11801 for (i = 0; i < 3; i++) {
11802 int j;
11803
11804 *p++ = permuser[i];
11805 *p++ = '=';
11806 for (j = 0; j < 3; j++) {
11807 if ((mask & permmask[3 * i + j]) == 0) {
11808 *p++ = permmode[j];
11809 }
11810 }
11811 *p++ = ',';
11812 }
11813 *--p = 0;
11814 puts(buf);
11815 } else {
11816 out1fmt("%.4o\n", mask);
11817 }
11818 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011819 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011820 mask = 0;
11821 do {
11822 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011823 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011824 mask = (mask << 3) + (*ap - '0');
11825 } while (*++ap != '\0');
11826 umask(mask);
11827 } else {
11828 mask = ~mask & 0777;
11829 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011830 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011831 }
11832 umask(~mask & 0777);
11833 }
11834 }
11835 return 0;
11836}
11837
11838/*
11839 * ulimit builtin
11840 *
11841 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11842 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11843 * ash by J.T. Conklin.
11844 *
11845 * Public domain.
11846 */
11847
11848struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011849 uint8_t cmd; /* RLIMIT_xxx fit into it */
11850 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011851 char option;
11852};
11853
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011854static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011855#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011856 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011857#endif
11858#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011859 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011860#endif
11861#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011862 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011863#endif
11864#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011865 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011866#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011867#ifdef RLIMIT_CORE
11868 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011869#endif
11870#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011871 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011872#endif
11873#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011874 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011875#endif
11876#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011877 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011878#endif
11879#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011880 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011881#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011882#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011883 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011884#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011885#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011886 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011887#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011888};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011889static const char limits_name[] =
11890#ifdef RLIMIT_CPU
11891 "time(seconds)" "\0"
11892#endif
11893#ifdef RLIMIT_FSIZE
11894 "file(blocks)" "\0"
11895#endif
11896#ifdef RLIMIT_DATA
11897 "data(kb)" "\0"
11898#endif
11899#ifdef RLIMIT_STACK
11900 "stack(kb)" "\0"
11901#endif
11902#ifdef RLIMIT_CORE
11903 "coredump(blocks)" "\0"
11904#endif
11905#ifdef RLIMIT_RSS
11906 "memory(kb)" "\0"
11907#endif
11908#ifdef RLIMIT_MEMLOCK
11909 "locked memory(kb)" "\0"
11910#endif
11911#ifdef RLIMIT_NPROC
11912 "process" "\0"
11913#endif
11914#ifdef RLIMIT_NOFILE
11915 "nofiles" "\0"
11916#endif
11917#ifdef RLIMIT_AS
11918 "vmemory(kb)" "\0"
11919#endif
11920#ifdef RLIMIT_LOCKS
11921 "locks" "\0"
11922#endif
11923;
Eric Andersenc470f442003-07-28 09:56:35 +000011924
Glenn L McGrath76620622004-01-13 10:19:37 +000011925enum limtype { SOFT = 0x1, HARD = 0x2 };
11926
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011927static void
11928printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011929 const struct limits *l)
11930{
11931 rlim_t val;
11932
11933 val = limit->rlim_max;
11934 if (how & SOFT)
11935 val = limit->rlim_cur;
11936
11937 if (val == RLIM_INFINITY)
11938 out1fmt("unlimited\n");
11939 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011940 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011941 out1fmt("%lld\n", (long long) val);
11942 }
11943}
11944
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011945static int
Eric Andersenc470f442003-07-28 09:56:35 +000011946ulimitcmd(int argc, char **argv)
11947{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011948 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011949 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011950 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011951 const struct limits *l;
11952 int set, all = 0;
11953 int optc, what;
11954 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011955
11956 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011957 while ((optc = nextopt("HSa"
11958#ifdef RLIMIT_CPU
11959 "t"
11960#endif
11961#ifdef RLIMIT_FSIZE
11962 "f"
11963#endif
11964#ifdef RLIMIT_DATA
11965 "d"
11966#endif
11967#ifdef RLIMIT_STACK
11968 "s"
11969#endif
11970#ifdef RLIMIT_CORE
11971 "c"
11972#endif
11973#ifdef RLIMIT_RSS
11974 "m"
11975#endif
11976#ifdef RLIMIT_MEMLOCK
11977 "l"
11978#endif
11979#ifdef RLIMIT_NPROC
11980 "p"
11981#endif
11982#ifdef RLIMIT_NOFILE
11983 "n"
11984#endif
11985#ifdef RLIMIT_AS
11986 "v"
11987#endif
11988#ifdef RLIMIT_LOCKS
11989 "w"
11990#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011991 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011992 switch (optc) {
11993 case 'H':
11994 how = HARD;
11995 break;
11996 case 'S':
11997 how = SOFT;
11998 break;
11999 case 'a':
12000 all = 1;
12001 break;
12002 default:
12003 what = optc;
12004 }
12005
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012006 for (l = limits_tbl; l->option != what; l++)
12007 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012008
12009 set = *argptr ? 1 : 0;
12010 if (set) {
12011 char *p = *argptr;
12012
12013 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012014 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012015 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012016 val = RLIM_INFINITY;
12017 else {
12018 val = (rlim_t) 0;
12019
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012020 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012021 val = (val * 10) + (long)(c - '0');
12022 if (val < (rlim_t) 0)
12023 break;
12024 }
12025 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012026 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012027 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012028 }
12029 }
12030 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012031 const char *lname = limits_name;
12032 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012033 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012034 out1fmt("%-20s ", lname);
12035 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012036 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012037 }
12038 return 0;
12039 }
12040
12041 getrlimit(l->cmd, &limit);
12042 if (set) {
12043 if (how & HARD)
12044 limit.rlim_max = val;
12045 if (how & SOFT)
12046 limit.rlim_cur = val;
12047 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012048 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012049 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012050 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012051 }
12052 return 0;
12053}
12054
Eric Andersen90898442003-08-06 11:20:52 +000012055
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012056/* ============ Math support */
12057
Denis Vlasenko131ae172007-02-18 13:00:19 +000012058#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012059
12060/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12061
12062 Permission is hereby granted, free of charge, to any person obtaining
12063 a copy of this software and associated documentation files (the
12064 "Software"), to deal in the Software without restriction, including
12065 without limitation the rights to use, copy, modify, merge, publish,
12066 distribute, sublicense, and/or sell copies of the Software, and to
12067 permit persons to whom the Software is furnished to do so, subject to
12068 the following conditions:
12069
12070 The above copyright notice and this permission notice shall be
12071 included in all copies or substantial portions of the Software.
12072
12073 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12074 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12075 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12076 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12077 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12078 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12079 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12080*/
12081
12082/* This is my infix parser/evaluator. It is optimized for size, intended
12083 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012084 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012085 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012086 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012087 * be that which POSIX specifies for shells. */
12088
12089/* The code uses a simple two-stack algorithm. See
12090 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012091 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012092 * this is based (this code differs in that it applies operators immediately
12093 * to the stack instead of adding them to a queue to end up with an
12094 * expression). */
12095
12096/* To use the routine, call it with an expression string and error return
12097 * pointer */
12098
12099/*
12100 * Aug 24, 2001 Manuel Novoa III
12101 *
12102 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12103 *
12104 * 1) In arith_apply():
12105 * a) Cached values of *numptr and &(numptr[-1]).
12106 * b) Removed redundant test for zero denominator.
12107 *
12108 * 2) In arith():
12109 * a) Eliminated redundant code for processing operator tokens by moving
12110 * to a table-based implementation. Also folded handling of parens
12111 * into the table.
12112 * b) Combined all 3 loops which called arith_apply to reduce generated
12113 * code size at the cost of speed.
12114 *
12115 * 3) The following expressions were treated as valid by the original code:
12116 * 1() , 0! , 1 ( *3 ) .
12117 * These bugs have been fixed by internally enclosing the expression in
12118 * parens and then checking that all binary ops and right parens are
12119 * preceded by a valid expression (NUM_TOKEN).
12120 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012121 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012122 * ctype's isspace() if it is used by another busybox applet or if additional
12123 * whitespace chars should be considered. Look below the "#include"s for a
12124 * precompiler test.
12125 */
12126
12127/*
12128 * Aug 26, 2001 Manuel Novoa III
12129 *
12130 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12131 *
12132 * Merge in Aaron's comments previously posted to the busybox list,
12133 * modified slightly to take account of my changes to the code.
12134 *
12135 */
12136
12137/*
12138 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12139 *
12140 * - allow access to variable,
12141 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12142 * - realize assign syntax (VAR=expr, +=, *= etc)
12143 * - realize exponentiation (** operator)
12144 * - realize comma separated - expr, expr
12145 * - realise ++expr --expr expr++ expr--
12146 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012147 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012148 * - was restored loses XOR operator
12149 * - remove one goto label, added three ;-)
12150 * - protect $((num num)) as true zero expr (Manuel`s error)
12151 * - always use special isspace(), see comment from bash ;-)
12152 */
12153
Eric Andersen90898442003-08-06 11:20:52 +000012154#define arith_isspace(arithval) \
12155 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12156
Eric Andersen90898442003-08-06 11:20:52 +000012157typedef unsigned char operator;
12158
12159/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012160 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012161 * precedence. The ID portion is so that multiple operators can have the
12162 * same precedence, ensuring that the leftmost one is evaluated first.
12163 * Consider * and /. */
12164
12165#define tok_decl(prec,id) (((id)<<5)|(prec))
12166#define PREC(op) ((op) & 0x1F)
12167
12168#define TOK_LPAREN tok_decl(0,0)
12169
12170#define TOK_COMMA tok_decl(1,0)
12171
12172#define TOK_ASSIGN tok_decl(2,0)
12173#define TOK_AND_ASSIGN tok_decl(2,1)
12174#define TOK_OR_ASSIGN tok_decl(2,2)
12175#define TOK_XOR_ASSIGN tok_decl(2,3)
12176#define TOK_PLUS_ASSIGN tok_decl(2,4)
12177#define TOK_MINUS_ASSIGN tok_decl(2,5)
12178#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12179#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12180
12181#define TOK_MUL_ASSIGN tok_decl(3,0)
12182#define TOK_DIV_ASSIGN tok_decl(3,1)
12183#define TOK_REM_ASSIGN tok_decl(3,2)
12184
12185/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012186#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012187
12188/* conditional is right associativity too */
12189#define TOK_CONDITIONAL tok_decl(4,0)
12190#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12191
12192#define TOK_OR tok_decl(5,0)
12193
12194#define TOK_AND tok_decl(6,0)
12195
12196#define TOK_BOR tok_decl(7,0)
12197
12198#define TOK_BXOR tok_decl(8,0)
12199
12200#define TOK_BAND tok_decl(9,0)
12201
12202#define TOK_EQ tok_decl(10,0)
12203#define TOK_NE tok_decl(10,1)
12204
12205#define TOK_LT tok_decl(11,0)
12206#define TOK_GT tok_decl(11,1)
12207#define TOK_GE tok_decl(11,2)
12208#define TOK_LE tok_decl(11,3)
12209
12210#define TOK_LSHIFT tok_decl(12,0)
12211#define TOK_RSHIFT tok_decl(12,1)
12212
12213#define TOK_ADD tok_decl(13,0)
12214#define TOK_SUB tok_decl(13,1)
12215
12216#define TOK_MUL tok_decl(14,0)
12217#define TOK_DIV tok_decl(14,1)
12218#define TOK_REM tok_decl(14,2)
12219
12220/* exponent is right associativity */
12221#define TOK_EXPONENT tok_decl(15,1)
12222
12223/* For now unary operators. */
12224#define UNARYPREC 16
12225#define TOK_BNOT tok_decl(UNARYPREC,0)
12226#define TOK_NOT tok_decl(UNARYPREC,1)
12227
12228#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12229#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12230
12231#define PREC_PRE (UNARYPREC+2)
12232
12233#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12234#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12235
12236#define PREC_POST (UNARYPREC+3)
12237
12238#define TOK_POST_INC tok_decl(PREC_POST, 0)
12239#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12240
12241#define SPEC_PREC (UNARYPREC+4)
12242
12243#define TOK_NUM tok_decl(SPEC_PREC, 0)
12244#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12245
12246#define NUMPTR (*numstackptr)
12247
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012248static int
12249tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012250{
12251 operator prec = PREC(op);
12252
12253 convert_prec_is_assing(prec);
12254 return (prec == PREC(TOK_ASSIGN) ||
12255 prec == PREC_PRE || prec == PREC_POST);
12256}
12257
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012258static int
12259is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012260{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012261 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12262 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012263}
12264
Eric Andersen90898442003-08-06 11:20:52 +000012265typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012266 arith_t val;
12267 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012268 char contidional_second_val_initialized;
12269 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012270 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012271} v_n_t;
12272
Eric Andersen90898442003-08-06 11:20:52 +000012273typedef struct CHK_VAR_RECURSIVE_LOOPED {
12274 const char *var;
12275 struct CHK_VAR_RECURSIVE_LOOPED *next;
12276} chk_var_recursive_looped_t;
12277
12278static chk_var_recursive_looped_t *prev_chk_var_recursive;
12279
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012280static int
12281arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012282{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012283 if (t->var) {
12284 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012286 if (p) {
12287 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012288
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012289 /* recursive try as expression */
12290 chk_var_recursive_looped_t *cur;
12291 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012292
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12294 if (strcmp(cur->var, t->var) == 0) {
12295 /* expression recursion loop detected */
12296 return -5;
12297 }
12298 }
12299 /* save current lookuped var name */
12300 cur = prev_chk_var_recursive;
12301 cur_save.var = t->var;
12302 cur_save.next = cur;
12303 prev_chk_var_recursive = &cur_save;
12304
12305 t->val = arith (p, &errcode);
12306 /* restore previous ptr after recursiving */
12307 prev_chk_var_recursive = cur;
12308 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012309 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 /* allow undefined var as 0 */
12311 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012312 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012313 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012314}
12315
12316/* "applying" a token means performing it on the top elements on the integer
12317 * stack. For a unary operator it will only change the top element, but a
12318 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012319static int
12320arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012321{
Eric Andersen90898442003-08-06 11:20:52 +000012322 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012323 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012324 int ret_arith_lookup_val;
12325
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012326 /* There is no operator that can work without arguments */
12327 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012328 numptr_m1 = NUMPTR - 1;
12329
12330 /* check operand is var with noninteger value */
12331 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012332 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012333 return ret_arith_lookup_val;
12334
12335 rez = numptr_m1->val;
12336 if (op == TOK_UMINUS)
12337 rez *= -1;
12338 else if (op == TOK_NOT)
12339 rez = !rez;
12340 else if (op == TOK_BNOT)
12341 rez = ~rez;
12342 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12343 rez++;
12344 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12345 rez--;
12346 else if (op != TOK_UPLUS) {
12347 /* Binary operators */
12348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 /* check and binary operators need two arguments */
12350 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012351
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012352 /* ... and they pop one */
12353 --NUMPTR;
12354 numptr_val = rez;
12355 if (op == TOK_CONDITIONAL) {
12356 if (! numptr_m1->contidional_second_val_initialized) {
12357 /* protect $((expr1 ? expr2)) without ": expr" */
12358 goto err;
12359 }
12360 rez = numptr_m1->contidional_second_val;
12361 } else if (numptr_m1->contidional_second_val_initialized) {
12362 /* protect $((expr1 : expr2)) without "expr ? " */
12363 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012364 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012365 numptr_m1 = NUMPTR - 1;
12366 if (op != TOK_ASSIGN) {
12367 /* check operand is var with noninteger value for not '=' */
12368 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12369 if (ret_arith_lookup_val)
12370 return ret_arith_lookup_val;
12371 }
12372 if (op == TOK_CONDITIONAL) {
12373 numptr_m1->contidional_second_val = rez;
12374 }
12375 rez = numptr_m1->val;
12376 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012377 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012378 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012379 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012381 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012382 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012383 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012384 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012385 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012387 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012388 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012389 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012390 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012391 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012392 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012393 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012394 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012395 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012397 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012399 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012400 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012401 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012403 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012405 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012407 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012408 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012409 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012410 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012411 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012412 /* protect $((expr : expr)) without "expr ? " */
12413 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012414 }
12415 numptr_m1->contidional_second_val_initialized = op;
12416 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012417 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012418 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012419 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012420 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012421 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012422 return -3; /* exponent less than 0 */
12423 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012424 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012425
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012426 if (numptr_val)
12427 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012428 c *= rez;
12429 rez = c;
12430 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012431 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012432 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012433 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012434 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012435 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012436 rez %= numptr_val;
12437 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012439 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012440
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012442 /* Hmm, 1=2 ? */
12443 goto err;
12444 }
12445 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012446#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012447 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012448#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012449 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012450#endif
Eric Andersen90898442003-08-06 11:20:52 +000012451 setvar(numptr_m1->var, buf, 0);
12452 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012454 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012456 rez++;
12457 }
12458 numptr_m1->val = rez;
12459 /* protect geting var value, is number now */
12460 numptr_m1->var = NULL;
12461 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012462 err:
12463 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012464}
12465
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012466/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012467static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012468 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12469 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12470 '<','<', 0, TOK_LSHIFT,
12471 '>','>', 0, TOK_RSHIFT,
12472 '|','|', 0, TOK_OR,
12473 '&','&', 0, TOK_AND,
12474 '!','=', 0, TOK_NE,
12475 '<','=', 0, TOK_LE,
12476 '>','=', 0, TOK_GE,
12477 '=','=', 0, TOK_EQ,
12478 '|','=', 0, TOK_OR_ASSIGN,
12479 '&','=', 0, TOK_AND_ASSIGN,
12480 '*','=', 0, TOK_MUL_ASSIGN,
12481 '/','=', 0, TOK_DIV_ASSIGN,
12482 '%','=', 0, TOK_REM_ASSIGN,
12483 '+','=', 0, TOK_PLUS_ASSIGN,
12484 '-','=', 0, TOK_MINUS_ASSIGN,
12485 '-','-', 0, TOK_POST_DEC,
12486 '^','=', 0, TOK_XOR_ASSIGN,
12487 '+','+', 0, TOK_POST_INC,
12488 '*','*', 0, TOK_EXPONENT,
12489 '!', 0, TOK_NOT,
12490 '<', 0, TOK_LT,
12491 '>', 0, TOK_GT,
12492 '=', 0, TOK_ASSIGN,
12493 '|', 0, TOK_BOR,
12494 '&', 0, TOK_BAND,
12495 '*', 0, TOK_MUL,
12496 '/', 0, TOK_DIV,
12497 '%', 0, TOK_REM,
12498 '+', 0, TOK_ADD,
12499 '-', 0, TOK_SUB,
12500 '^', 0, TOK_BXOR,
12501 /* uniq */
12502 '~', 0, TOK_BNOT,
12503 ',', 0, TOK_COMMA,
12504 '?', 0, TOK_CONDITIONAL,
12505 ':', 0, TOK_CONDITIONAL_SEP,
12506 ')', 0, TOK_RPAREN,
12507 '(', 0, TOK_LPAREN,
12508 0
12509};
12510/* ptr to ")" */
12511#define endexpression &op_tokens[sizeof(op_tokens)-7]
12512
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012513static arith_t
12514arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012515{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012516 char arithval; /* Current character under analysis */
12517 operator lasttok, op;
12518 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012519
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012520 const char *p = endexpression;
12521 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012522
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012524
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 /* Stack of integers */
12526 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12527 * in any given correct or incorrect expression is left as an exercise to
12528 * the reader. */
12529 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12530 *numstackptr = numstack;
12531 /* Stack of operator tokens */
12532 operator *stack = alloca((datasizes) * sizeof(operator)),
12533 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012534
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012535 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12536 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012537
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012538 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012539 arithval = *expr;
12540 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012541 if (p == endexpression) {
12542 /* Null expression. */
12543 return 0;
12544 }
12545
12546 /* This is only reached after all tokens have been extracted from the
12547 * input stream. If there are still tokens on the operator stack, they
12548 * are to be applied in order. At the end, there should be a final
12549 * result on the integer stack */
12550
12551 if (expr != endexpression + 1) {
12552 /* If we haven't done so already, */
12553 /* append a closing right paren */
12554 expr = endexpression;
12555 /* and let the loop process it. */
12556 continue;
12557 }
12558 /* At this point, we're done with the expression. */
12559 if (numstackptr != numstack+1) {
12560 /* ... but if there isn't, it's bad */
12561 err:
12562 return (*perrcode = -1);
12563 }
12564 if (numstack->var) {
12565 /* expression is $((var)) only, lookup now */
12566 errcode = arith_lookup_val(numstack);
12567 }
12568 ret:
12569 *perrcode = errcode;
12570 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012571 }
12572
Eric Andersen90898442003-08-06 11:20:52 +000012573 /* Continue processing the expression. */
12574 if (arith_isspace(arithval)) {
12575 /* Skip whitespace */
12576 goto prologue;
12577 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012578 p = endofname(expr);
12579 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012580 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012581
12582 numstackptr->var = alloca(var_name_size);
12583 safe_strncpy(numstackptr->var, expr, var_name_size);
12584 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012585 num:
Eric Andersen90898442003-08-06 11:20:52 +000012586 numstackptr->contidional_second_val_initialized = 0;
12587 numstackptr++;
12588 lasttok = TOK_NUM;
12589 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012590 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012591 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012592 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012593#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012594 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012595#else
12596 numstackptr->val = strtol(expr, (char **) &expr, 0);
12597#endif
Eric Andersen90898442003-08-06 11:20:52 +000012598 goto num;
12599 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012600 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012601 const char *o;
12602
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012603 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012604 /* strange operator not found */
12605 goto err;
12606 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012607 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012608 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012609 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012610 /* found */
12611 expr = o - 1;
12612 break;
12613 }
12614 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012615 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012616 p++;
12617 /* skip zero delim */
12618 p++;
12619 }
12620 op = p[1];
12621
12622 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012623 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12624 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012625
12626 /* Plus and minus are binary (not unary) _only_ if the last
12627 * token was as number, or a right paren (which pretends to be
12628 * a number, since it evaluates to one). Think about it.
12629 * It makes sense. */
12630 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012631 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012632 case TOK_ADD:
12633 op = TOK_UPLUS;
12634 break;
12635 case TOK_SUB:
12636 op = TOK_UMINUS;
12637 break;
12638 case TOK_POST_INC:
12639 op = TOK_PRE_INC;
12640 break;
12641 case TOK_POST_DEC:
12642 op = TOK_PRE_DEC;
12643 break;
Eric Andersen90898442003-08-06 11:20:52 +000012644 }
12645 }
12646 /* We don't want a unary operator to cause recursive descent on the
12647 * stack, because there can be many in a row and it could cause an
12648 * operator to be evaluated before its argument is pushed onto the
12649 * integer stack. */
12650 /* But for binary operators, "apply" everything on the operator
12651 * stack until we find an operator with a lesser priority than the
12652 * one we have just extracted. */
12653 /* Left paren is given the lowest priority so it will never be
12654 * "applied" in this way.
12655 * if associativity is right and priority eq, applied also skip
12656 */
12657 prec = PREC(op);
12658 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12659 /* not left paren or unary */
12660 if (lasttok != TOK_NUM) {
12661 /* binary op must be preceded by a num */
12662 goto err;
12663 }
12664 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012665 if (op == TOK_RPAREN) {
12666 /* The algorithm employed here is simple: while we don't
12667 * hit an open paren nor the bottom of the stack, pop
12668 * tokens and apply them */
12669 if (stackptr[-1] == TOK_LPAREN) {
12670 --stackptr;
12671 /* Any operator directly after a */
12672 lasttok = TOK_NUM;
12673 /* close paren should consider itself binary */
12674 goto prologue;
12675 }
12676 } else {
12677 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012678
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012679 convert_prec_is_assing(prec);
12680 convert_prec_is_assing(prev_prec);
12681 if (prev_prec < prec)
12682 break;
12683 /* check right assoc */
12684 if (prev_prec == prec && is_right_associativity(prec))
12685 break;
12686 }
12687 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12688 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012689 }
12690 if (op == TOK_RPAREN) {
12691 goto err;
12692 }
12693 }
12694
12695 /* Push this operator to the stack and remember it. */
12696 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012697 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012698 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012699 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012700}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012701#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012702
12703
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012704/* ============ main() and helpers */
12705
12706/*
12707 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012708 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012709static void exitshell(void) ATTRIBUTE_NORETURN;
12710static void
12711exitshell(void)
12712{
12713 struct jmploc loc;
12714 char *p;
12715 int status;
12716
12717 status = exitstatus;
12718 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12719 if (setjmp(loc.loc)) {
12720 if (exception == EXEXIT)
12721/* dash bug: it just does _exit(exitstatus) here
12722 * but we have to do setjobctl(0) first!
12723 * (bug is still not fixed in dash-0.5.3 - if you run dash
12724 * under Midnight Commander, on exit from dash MC is backgrounded) */
12725 status = exitstatus;
12726 goto out;
12727 }
12728 exception_handler = &loc;
12729 p = trap[0];
12730 if (p) {
12731 trap[0] = NULL;
12732 evalstring(p, 0);
12733 }
12734 flush_stdout_stderr();
12735 out:
12736 setjobctl(0);
12737 _exit(status);
12738 /* NOTREACHED */
12739}
12740
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012741static void
12742init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012743{
12744 /* from input.c: */
12745 basepf.nextc = basepf.buf = basebuf;
12746
12747 /* from trap.c: */
12748 signal(SIGCHLD, SIG_DFL);
12749
12750 /* from var.c: */
12751 {
12752 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012753 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012754 const char *p;
12755 struct stat st1, st2;
12756
12757 initvar();
12758 for (envp = environ; envp && *envp; envp++) {
12759 if (strchr(*envp, '=')) {
12760 setvareq(*envp, VEXPORT|VTEXTFIXED);
12761 }
12762 }
12763
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012764 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012765 setvar("PPID", ppid, 0);
12766
12767 p = lookupvar("PWD");
12768 if (p)
12769 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12770 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12771 p = '\0';
12772 setpwd(p, 0);
12773 }
12774}
12775
12776/*
12777 * Process the shell command line arguments.
12778 */
12779static void
12780procargs(int argc, char **argv)
12781{
12782 int i;
12783 const char *xminusc;
12784 char **xargv;
12785
12786 xargv = argv;
12787 arg0 = xargv[0];
12788 if (argc > 0)
12789 xargv++;
12790 for (i = 0; i < NOPTS; i++)
12791 optlist[i] = 2;
12792 argptr = xargv;
12793 options(1);
12794 xargv = argptr;
12795 xminusc = minusc;
12796 if (*xargv == NULL) {
12797 if (xminusc)
12798 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12799 sflag = 1;
12800 }
12801 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12802 iflag = 1;
12803 if (mflag == 2)
12804 mflag = iflag;
12805 for (i = 0; i < NOPTS; i++)
12806 if (optlist[i] == 2)
12807 optlist[i] = 0;
12808#if DEBUG == 2
12809 debug = 1;
12810#endif
12811 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12812 if (xminusc) {
12813 minusc = *xargv++;
12814 if (*xargv)
12815 goto setarg0;
12816 } else if (!sflag) {
12817 setinputfile(*xargv, 0);
12818 setarg0:
12819 arg0 = *xargv++;
12820 commandname = arg0;
12821 }
12822
12823 shellparam.p = xargv;
12824#if ENABLE_ASH_GETOPTS
12825 shellparam.optind = 1;
12826 shellparam.optoff = -1;
12827#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012828 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012829 while (*xargv) {
12830 shellparam.nparam++;
12831 xargv++;
12832 }
12833 optschanged();
12834}
12835
12836/*
12837 * Read /etc/profile or .profile.
12838 */
12839static void
12840read_profile(const char *name)
12841{
12842 int skip;
12843
12844 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12845 return;
12846 skip = cmdloop(0);
12847 popfile();
12848 if (skip)
12849 exitshell();
12850}
12851
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012852/*
12853 * This routine is called when an error or an interrupt occurs in an
12854 * interactive shell and control is returned to the main command loop.
12855 */
12856static void
12857reset(void)
12858{
12859 /* from eval.c: */
12860 evalskip = 0;
12861 loopnest = 0;
12862 /* from input.c: */
12863 parselleft = parsenleft = 0; /* clear input buffer */
12864 popallfiles();
12865 /* from parser.c: */
12866 tokpushback = 0;
12867 checkkwd = 0;
12868 /* from redir.c: */
12869 clearredir(0);
12870}
12871
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872#if PROFILE
12873static short profile_buf[16384];
12874extern int etext();
12875#endif
12876
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012877/*
12878 * Main routine. We initialize things, parse the arguments, execute
12879 * profiles if we're a login shell, and then call cmdloop to execute
12880 * commands. The setjmp call sets up the location to jump to when an
12881 * exception occurs. When an exception occurs the variable "state"
12882 * is used to figure out how far we had gotten.
12883 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012884int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012885int ash_main(int argc, char **argv)
12886{
12887 char *shinit;
12888 volatile int state;
12889 struct jmploc jmploc;
12890 struct stackmark smark;
12891
Denis Vlasenko01631112007-12-16 17:20:38 +000012892 /* Initialize global data */
12893 INIT_G_misc();
12894 INIT_G_memstack();
12895 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012896#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012897 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012898#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012899 INIT_G_cmdtable();
12900
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012901#if PROFILE
12902 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12903#endif
12904
12905#if ENABLE_FEATURE_EDITING
12906 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12907#endif
12908 state = 0;
12909 if (setjmp(jmploc.loc)) {
12910 int e;
12911 int s;
12912
12913 reset();
12914
12915 e = exception;
12916 if (e == EXERROR)
12917 exitstatus = 2;
12918 s = state;
12919 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12920 exitshell();
12921
12922 if (e == EXINT) {
12923 outcslow('\n', stderr);
12924 }
12925 popstackmark(&smark);
12926 FORCE_INT_ON; /* enable interrupts */
12927 if (s == 1)
12928 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012929 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012930 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012931 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012933 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012934 }
12935 exception_handler = &jmploc;
12936#if DEBUG
12937 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012938 trace_puts("Shell args: ");
12939 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012940#endif
12941 rootpid = getpid();
12942
12943#if ENABLE_ASH_RANDOM_SUPPORT
12944 rseed = rootpid + time(NULL);
12945#endif
12946 init();
12947 setstackmark(&smark);
12948 procargs(argc, argv);
12949#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12950 if (iflag) {
12951 const char *hp = lookupvar("HISTFILE");
12952
12953 if (hp == NULL) {
12954 hp = lookupvar("HOME");
12955 if (hp != NULL) {
12956 char *defhp = concat_path_file(hp, ".ash_history");
12957 setvar("HISTFILE", defhp, 0);
12958 free(defhp);
12959 }
12960 }
12961 }
12962#endif
12963 if (argv[0] && argv[0][0] == '-')
12964 isloginsh = 1;
12965 if (isloginsh) {
12966 state = 1;
12967 read_profile("/etc/profile");
12968 state1:
12969 state = 2;
12970 read_profile(".profile");
12971 }
12972 state2:
12973 state = 3;
12974 if (
12975#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012976 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012977#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012978 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012979 ) {
12980 shinit = lookupvar("ENV");
12981 if (shinit != NULL && *shinit != '\0') {
12982 read_profile(shinit);
12983 }
12984 }
12985 state3:
12986 state = 4;
12987 if (minusc)
12988 evalstring(minusc, 0);
12989
12990 if (sflag || minusc == NULL) {
12991#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12992 if ( iflag ) {
12993 const char *hp = lookupvar("HISTFILE");
12994
12995 if (hp != NULL)
12996 line_input_state->hist_file = hp;
12997 }
12998#endif
12999 state4: /* XXX ??? - why isn't this before the "if" statement */
13000 cmdloop(1);
13001 }
13002#if PROFILE
13003 monitor(0);
13004#endif
13005#ifdef GPROF
13006 {
13007 extern void _mcleanup(void);
13008 _mcleanup();
13009 }
13010#endif
13011 exitshell();
13012 /* NOTREACHED */
13013}
13014
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013015#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013016const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013017int main(int argc, char **argv)
13018{
13019 return ash_main(argc, argv);
13020}
13021#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013022
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023
Eric Andersendf82f612001-06-28 07:46:40 +000013024/*-
13025 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013026 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013027 *
13028 * This code is derived from software contributed to Berkeley by
13029 * Kenneth Almquist.
13030 *
13031 * Redistribution and use in source and binary forms, with or without
13032 * modification, are permitted provided that the following conditions
13033 * are met:
13034 * 1. Redistributions of source code must retain the above copyright
13035 * notice, this list of conditions and the following disclaimer.
13036 * 2. Redistributions in binary form must reproduce the above copyright
13037 * notice, this list of conditions and the following disclaimer in the
13038 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013039 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013040 * may be used to endorse or promote products derived from this software
13041 * without specific prior written permission.
13042 *
13043 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13044 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13045 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13046 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13047 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13048 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13049 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13050 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13051 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13052 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13053 * SUCH DAMAGE.
13054 */