blob: a877b5bab49604ec032aecce721f651ef285ab4b [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;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000167
168// disabled by vda: cannot understand how it was supposed to work -
169// cannot fix bugs. That's why you have to explain your non-trivial designs!
170// /* do we generate EXSIG events */
171// int exsig; /* counter */
172 volatile int suppressint; /* counter */
173 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
174 /* last pending signal */
175 volatile /*sig_atomic_t*/ smallint pendingsig;
176 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000177 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000178#define EXINT 0 /* SIGINT received */
179#define EXERROR 1 /* a generic error */
180#define EXSHELLPROC 2 /* execute a shell procedure */
181#define EXEXEC 3 /* command execution failed */
182#define EXEXIT 4 /* exit the shell */
183#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000184
Denis Vlasenko01631112007-12-16 17:20:38 +0000185 /* trap handler commands */
186 char *trap[NSIG];
187 smallint isloginsh;
188 char nullstr[1]; /* zero length string */
189 /*
190 * Sigmode records the current value of the signal handlers for the various
191 * modes. A value of zero means that the current handler is not known.
192 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
193 */
194 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000195#define S_DFL 1 /* default signal handling (SIG_DFL) */
196#define S_CATCH 2 /* signal is caught */
197#define S_IGN 3 /* signal is ignored (SIG_IGN) */
198#define S_HARD_IGN 4 /* signal is ignored permenantly */
199#define S_RESET 5 /* temporary - to reset a hard ignored sig */
200
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /* indicates specified signal received */
202 char gotsig[NSIG - 1];
203};
204/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
205static struct globals_misc *const ptr_to_globals_misc __attribute__ ((section (".data")));
206#define G_misc (*ptr_to_globals_misc)
207#define rootpid (G_misc.rootpid )
208#define shlvl (G_misc.shlvl )
209#define minusc (G_misc.minusc )
210#define curdir (G_misc.curdir )
211#define physdir (G_misc.physdir )
212#define arg0 (G_misc.arg0 )
213#define exception_handler (G_misc.exception_handler)
214#define exception (G_misc.exception )
215#define suppressint (G_misc.suppressint )
216#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000217//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000218#define pendingsig (G_misc.pendingsig )
219#define trap (G_misc.trap )
220#define isloginsh (G_misc.isloginsh)
221#define nullstr (G_misc.nullstr )
222#define sigmode (G_misc.sigmode )
223#define gotsig (G_misc.gotsig )
224#define INIT_G_misc() do { \
225 (*(struct globals_misc**)&ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
226 curdir = nullstr; \
227 physdir = nullstr; \
228} while (0)
229
230
231/* ============ Interrupts / exceptions */
232
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000233/*
Eric Andersen2870d962001-07-02 17:27:21 +0000234 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000235 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000236 * much more efficient and portable. (But hacking the kernel is so much
237 * more fun than worrying about efficiency and portability. :-))
238 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000239#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000240 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000241 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000242 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000243 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000244
245/*
246 * Called to raise an exception. Since C doesn't include exceptions, we
247 * just do a longjmp to the exception handler. The type of exception is
248 * stored in the global variable "exception".
249 */
250static void raise_exception(int) ATTRIBUTE_NORETURN;
251static void
252raise_exception(int e)
253{
254#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000255 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000256 abort();
257#endif
258 INT_OFF;
259 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000260 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000261}
262
263/*
264 * Called from trap.c when a SIGINT is received. (If the user specifies
265 * that SIGINT is to be trapped or ignored using the trap builtin, then
266 * this routine is not called.) Suppressint is nonzero when interrupts
267 * are held using the INT_OFF macro. (The test for iflag is just
268 * defensive programming.)
269 */
270static void raise_interrupt(void) ATTRIBUTE_NORETURN;
271static void
272raise_interrupt(void)
273{
274 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000275 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000276
277 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000278 /* Signal is not automatically unmasked after it is raised,
279 * do it ourself - unmask all signals */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000280 sigemptyset(&mask);
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000281 sigprocmask(SIG_SETMASK, &mask, NULL);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000282 /* pendingsig = 0; - now done in onsig() */
283
Denis Vlasenkob012b102007-02-19 22:43:01 +0000284 i = EXSIG;
285 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
286 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000287 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000288 signal(SIGINT, SIG_DFL);
289 raise(SIGINT);
290 }
291 i = EXINT;
292 }
293 raise_exception(i);
294 /* NOTREACHED */
295}
296
297#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000298static void
299int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000300{
301 if (--suppressint == 0 && intpending) {
302 raise_interrupt();
303 }
304}
305#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000306static void
307force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000308{
309 suppressint = 0;
310 if (intpending)
311 raise_interrupt();
312}
313#define FORCE_INT_ON force_int_on()
314#else
315#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000316 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000317 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000318 if (--suppressint == 0 && intpending) \
319 raise_interrupt(); \
320 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000321#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000322 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323 xbarrier(); \
324 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000325 if (intpending) \
326 raise_interrupt(); \
327 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000328#endif /* ASH_OPTIMIZE_FOR_SIZE */
329
330#define SAVE_INT(v) ((v) = suppressint)
331
332#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000333 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000334 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000335 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000336 if (suppressint == 0 && intpending) \
337 raise_interrupt(); \
338 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000339
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000340/*
341 * Ignore a signal. Only one usage site - in forkchild()
342 */
343static void
344ignoresig(int signo)
345{
346 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
347 signal(signo, SIG_IGN);
348 }
349 sigmode[signo - 1] = S_HARD_IGN;
350}
351
352/*
353 * Signal handler. Only one usage site - in setsignal()
354 */
355static void
356onsig(int signo)
357{
358 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000359 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000360
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000361 if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000362 if (!suppressint) {
363 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000364 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000365 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000366 intpending = 1;
367 }
368}
369
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000370
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000371/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000372
Eric Andersenc470f442003-07-28 09:56:35 +0000373static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000374outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000375{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000376 INT_OFF;
377 fputs(p, file);
378 INT_ON;
379}
380
381static void
382flush_stdout_stderr(void)
383{
384 INT_OFF;
385 fflush(stdout);
386 fflush(stderr);
387 INT_ON;
388}
389
390static void
391flush_stderr(void)
392{
393 INT_OFF;
394 fflush(stderr);
395 INT_ON;
396}
397
398static void
399outcslow(int c, FILE *dest)
400{
401 INT_OFF;
402 putc(c, dest);
403 fflush(dest);
404 INT_ON;
405}
406
407static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
408static int
409out1fmt(const char *fmt, ...)
410{
411 va_list ap;
412 int r;
413
414 INT_OFF;
415 va_start(ap, fmt);
416 r = vprintf(fmt, ap);
417 va_end(ap);
418 INT_ON;
419 return r;
420}
421
422static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
423static int
424fmtstr(char *outbuf, size_t length, const char *fmt, ...)
425{
426 va_list ap;
427 int ret;
428
429 va_start(ap, fmt);
430 INT_OFF;
431 ret = vsnprintf(outbuf, length, fmt, ap);
432 va_end(ap);
433 INT_ON;
434 return ret;
435}
436
437static void
438out1str(const char *p)
439{
440 outstr(p, stdout);
441}
442
443static void
444out2str(const char *p)
445{
446 outstr(p, stderr);
447 flush_stderr();
448}
449
450
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000451/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000452
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000453/* control characters in argument strings */
454#define CTLESC '\201' /* escape next character */
455#define CTLVAR '\202' /* variable defn */
456#define CTLENDVAR '\203'
457#define CTLBACKQ '\204'
458#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
459/* CTLBACKQ | CTLQUOTE == '\205' */
460#define CTLARI '\206' /* arithmetic expression */
461#define CTLENDARI '\207'
462#define CTLQUOTEMARK '\210'
463
464/* variable substitution byte (follows CTLVAR) */
465#define VSTYPE 0x0f /* type of variable substitution */
466#define VSNUL 0x10 /* colon--treat the empty string as unset */
467#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
468
469/* values of VSTYPE field */
470#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
471#define VSMINUS 0x2 /* ${var-text} */
472#define VSPLUS 0x3 /* ${var+text} */
473#define VSQUESTION 0x4 /* ${var?message} */
474#define VSASSIGN 0x5 /* ${var=text} */
475#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
476#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
477#define VSTRIMLEFT 0x8 /* ${var#pattern} */
478#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
479#define VSLENGTH 0xa /* ${#var} */
480
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000481static const char dolatstr[] ALIGN1 = {
482 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
483};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000484
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000485#define NCMD 0
486#define NPIPE 1
487#define NREDIR 2
488#define NBACKGND 3
489#define NSUBSHELL 4
490#define NAND 5
491#define NOR 6
492#define NSEMI 7
493#define NIF 8
494#define NWHILE 9
495#define NUNTIL 10
496#define NFOR 11
497#define NCASE 12
498#define NCLIST 13
499#define NDEFUN 14
500#define NARG 15
501#define NTO 16
502#define NCLOBBER 17
503#define NFROM 18
504#define NFROMTO 19
505#define NAPPEND 20
506#define NTOFD 21
507#define NFROMFD 22
508#define NHERE 23
509#define NXHERE 24
510#define NNOT 25
511
512union node;
513
514struct ncmd {
515 int type;
516 union node *assign;
517 union node *args;
518 union node *redirect;
519};
520
521struct npipe {
522 int type;
523 int backgnd;
524 struct nodelist *cmdlist;
525};
526
527struct nredir {
528 int type;
529 union node *n;
530 union node *redirect;
531};
532
533struct nbinary {
534 int type;
535 union node *ch1;
536 union node *ch2;
537};
538
539struct nif {
540 int type;
541 union node *test;
542 union node *ifpart;
543 union node *elsepart;
544};
545
546struct nfor {
547 int type;
548 union node *args;
549 union node *body;
550 char *var;
551};
552
553struct ncase {
554 int type;
555 union node *expr;
556 union node *cases;
557};
558
559struct nclist {
560 int type;
561 union node *next;
562 union node *pattern;
563 union node *body;
564};
565
566struct narg {
567 int type;
568 union node *next;
569 char *text;
570 struct nodelist *backquote;
571};
572
573struct nfile {
574 int type;
575 union node *next;
576 int fd;
577 union node *fname;
578 char *expfname;
579};
580
581struct ndup {
582 int type;
583 union node *next;
584 int fd;
585 int dupfd;
586 union node *vname;
587};
588
589struct nhere {
590 int type;
591 union node *next;
592 int fd;
593 union node *doc;
594};
595
596struct nnot {
597 int type;
598 union node *com;
599};
600
601union node {
602 int type;
603 struct ncmd ncmd;
604 struct npipe npipe;
605 struct nredir nredir;
606 struct nbinary nbinary;
607 struct nif nif;
608 struct nfor nfor;
609 struct ncase ncase;
610 struct nclist nclist;
611 struct narg narg;
612 struct nfile nfile;
613 struct ndup ndup;
614 struct nhere nhere;
615 struct nnot nnot;
616};
617
618struct nodelist {
619 struct nodelist *next;
620 union node *n;
621};
622
623struct funcnode {
624 int count;
625 union node n;
626};
627
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000628/*
629 * Free a parse tree.
630 */
631static void
632freefunc(struct funcnode *f)
633{
634 if (f && --f->count < 0)
635 free(f);
636}
637
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000638
639/* ============ Debugging output */
640
641#if DEBUG
642
643static FILE *tracefile;
644
645static void
646trace_printf(const char *fmt, ...)
647{
648 va_list va;
649
650 if (debug != 1)
651 return;
652 va_start(va, fmt);
653 vfprintf(tracefile, fmt, va);
654 va_end(va);
655}
656
657static void
658trace_vprintf(const char *fmt, va_list va)
659{
660 if (debug != 1)
661 return;
662 vfprintf(tracefile, fmt, va);
663}
664
665static void
666trace_puts(const char *s)
667{
668 if (debug != 1)
669 return;
670 fputs(s, tracefile);
671}
672
673static void
674trace_puts_quoted(char *s)
675{
676 char *p;
677 char c;
678
679 if (debug != 1)
680 return;
681 putc('"', tracefile);
682 for (p = s; *p; p++) {
683 switch (*p) {
684 case '\n': c = 'n'; goto backslash;
685 case '\t': c = 't'; goto backslash;
686 case '\r': c = 'r'; goto backslash;
687 case '"': c = '"'; goto backslash;
688 case '\\': c = '\\'; goto backslash;
689 case CTLESC: c = 'e'; goto backslash;
690 case CTLVAR: c = 'v'; goto backslash;
691 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
692 case CTLBACKQ: c = 'q'; goto backslash;
693 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
694 backslash:
695 putc('\\', tracefile);
696 putc(c, tracefile);
697 break;
698 default:
699 if (*p >= ' ' && *p <= '~')
700 putc(*p, tracefile);
701 else {
702 putc('\\', tracefile);
703 putc(*p >> 6 & 03, tracefile);
704 putc(*p >> 3 & 07, tracefile);
705 putc(*p & 07, tracefile);
706 }
707 break;
708 }
709 }
710 putc('"', tracefile);
711}
712
713static void
714trace_puts_args(char **ap)
715{
716 if (debug != 1)
717 return;
718 if (!*ap)
719 return;
720 while (1) {
721 trace_puts_quoted(*ap);
722 if (!*++ap) {
723 putc('\n', tracefile);
724 break;
725 }
726 putc(' ', tracefile);
727 }
728}
729
730static void
731opentrace(void)
732{
733 char s[100];
734#ifdef O_APPEND
735 int flags;
736#endif
737
738 if (debug != 1) {
739 if (tracefile)
740 fflush(tracefile);
741 /* leave open because libedit might be using it */
742 return;
743 }
744 strcpy(s, "./trace");
745 if (tracefile) {
746 if (!freopen(s, "a", tracefile)) {
747 fprintf(stderr, "Can't re-open %s\n", s);
748 debug = 0;
749 return;
750 }
751 } else {
752 tracefile = fopen(s, "a");
753 if (tracefile == NULL) {
754 fprintf(stderr, "Can't open %s\n", s);
755 debug = 0;
756 return;
757 }
758 }
759#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000760 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000761 if (flags >= 0)
762 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
763#endif
764 setlinebuf(tracefile);
765 fputs("\nTracing started.\n", tracefile);
766}
767
768static void
769indent(int amount, char *pfx, FILE *fp)
770{
771 int i;
772
773 for (i = 0; i < amount; i++) {
774 if (pfx && i == amount - 1)
775 fputs(pfx, fp);
776 putc('\t', fp);
777 }
778}
779
780/* little circular references here... */
781static void shtree(union node *n, int ind, char *pfx, FILE *fp);
782
783static void
784sharg(union node *arg, FILE *fp)
785{
786 char *p;
787 struct nodelist *bqlist;
788 int subtype;
789
790 if (arg->type != NARG) {
791 out1fmt("<node type %d>\n", arg->type);
792 abort();
793 }
794 bqlist = arg->narg.backquote;
795 for (p = arg->narg.text; *p; p++) {
796 switch (*p) {
797 case CTLESC:
798 putc(*++p, fp);
799 break;
800 case CTLVAR:
801 putc('$', fp);
802 putc('{', fp);
803 subtype = *++p;
804 if (subtype == VSLENGTH)
805 putc('#', fp);
806
807 while (*p != '=')
808 putc(*p++, fp);
809
810 if (subtype & VSNUL)
811 putc(':', fp);
812
813 switch (subtype & VSTYPE) {
814 case VSNORMAL:
815 putc('}', fp);
816 break;
817 case VSMINUS:
818 putc('-', fp);
819 break;
820 case VSPLUS:
821 putc('+', fp);
822 break;
823 case VSQUESTION:
824 putc('?', fp);
825 break;
826 case VSASSIGN:
827 putc('=', fp);
828 break;
829 case VSTRIMLEFT:
830 putc('#', fp);
831 break;
832 case VSTRIMLEFTMAX:
833 putc('#', fp);
834 putc('#', fp);
835 break;
836 case VSTRIMRIGHT:
837 putc('%', fp);
838 break;
839 case VSTRIMRIGHTMAX:
840 putc('%', fp);
841 putc('%', fp);
842 break;
843 case VSLENGTH:
844 break;
845 default:
846 out1fmt("<subtype %d>", subtype);
847 }
848 break;
849 case CTLENDVAR:
850 putc('}', fp);
851 break;
852 case CTLBACKQ:
853 case CTLBACKQ|CTLQUOTE:
854 putc('$', fp);
855 putc('(', fp);
856 shtree(bqlist->n, -1, NULL, fp);
857 putc(')', fp);
858 break;
859 default:
860 putc(*p, fp);
861 break;
862 }
863 }
864}
865
866static void
867shcmd(union node *cmd, FILE *fp)
868{
869 union node *np;
870 int first;
871 const char *s;
872 int dftfd;
873
874 first = 1;
875 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000876 if (!first)
877 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000878 sharg(np, fp);
879 first = 0;
880 }
881 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000882 if (!first)
883 putc(' ', fp);
884 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000885 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000886 case NTO: s = ">>"+1; dftfd = 1; break;
887 case NCLOBBER: s = ">|"; dftfd = 1; break;
888 case NAPPEND: s = ">>"; dftfd = 1; break;
889 case NTOFD: s = ">&"; dftfd = 1; break;
890 case NFROM: s = "<"; break;
891 case NFROMFD: s = "<&"; break;
892 case NFROMTO: s = "<>"; break;
893 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000894 }
895 if (np->nfile.fd != dftfd)
896 fprintf(fp, "%d", np->nfile.fd);
897 fputs(s, fp);
898 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
899 fprintf(fp, "%d", np->ndup.dupfd);
900 } else {
901 sharg(np->nfile.fname, fp);
902 }
903 first = 0;
904 }
905}
906
907static void
908shtree(union node *n, int ind, char *pfx, FILE *fp)
909{
910 struct nodelist *lp;
911 const char *s;
912
913 if (n == NULL)
914 return;
915
916 indent(ind, pfx, fp);
917 switch (n->type) {
918 case NSEMI:
919 s = "; ";
920 goto binop;
921 case NAND:
922 s = " && ";
923 goto binop;
924 case NOR:
925 s = " || ";
926 binop:
927 shtree(n->nbinary.ch1, ind, NULL, fp);
928 /* if (ind < 0) */
929 fputs(s, fp);
930 shtree(n->nbinary.ch2, ind, NULL, fp);
931 break;
932 case NCMD:
933 shcmd(n, fp);
934 if (ind >= 0)
935 putc('\n', fp);
936 break;
937 case NPIPE:
938 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
939 shcmd(lp->n, fp);
940 if (lp->next)
941 fputs(" | ", fp);
942 }
943 if (n->npipe.backgnd)
944 fputs(" &", fp);
945 if (ind >= 0)
946 putc('\n', fp);
947 break;
948 default:
949 fprintf(fp, "<node type %d>", n->type);
950 if (ind >= 0)
951 putc('\n', fp);
952 break;
953 }
954}
955
956static void
957showtree(union node *n)
958{
959 trace_puts("showtree called\n");
960 shtree(n, 1, NULL, stdout);
961}
962
963#define TRACE(param) trace_printf param
964#define TRACEV(param) trace_vprintf param
965
966#else
967
968#define TRACE(param)
969#define TRACEV(param)
970
971#endif /* DEBUG */
972
973
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000974/* ============ Parser data */
975
976/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000977 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
978 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000979struct strlist {
980 struct strlist *next;
981 char *text;
982};
983
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000984#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000985struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000986#endif
987
Denis Vlasenkob012b102007-02-19 22:43:01 +0000988struct strpush {
989 struct strpush *prev; /* preceding string on stack */
990 char *prevstring;
991 int prevnleft;
992#if ENABLE_ASH_ALIAS
993 struct alias *ap; /* if push was associated with an alias */
994#endif
995 char *string; /* remember the string since it may change */
996};
997
998struct parsefile {
999 struct parsefile *prev; /* preceding file on stack */
1000 int linno; /* current line */
1001 int fd; /* file descriptor (or -1 if string) */
1002 int nleft; /* number of chars left in this line */
1003 int lleft; /* number of chars left in this buffer */
1004 char *nextc; /* next char in buffer */
1005 char *buf; /* input buffer */
1006 struct strpush *strpush; /* for pushing strings at this level */
1007 struct strpush basestrpush; /* so pushing one is fast */
1008};
1009
1010static struct parsefile basepf; /* top level input file */
1011static struct parsefile *parsefile = &basepf; /* current input file */
1012static int startlinno; /* line # where last token started */
1013static char *commandname; /* currently executing command */
1014static struct strlist *cmdenviron; /* environment for builtin command */
1015static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001016
1017
1018/* ============ Message printing */
1019
1020static void
1021ash_vmsg(const char *msg, va_list ap)
1022{
1023 fprintf(stderr, "%s: ", arg0);
1024 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001025 if (strcmp(arg0, commandname))
1026 fprintf(stderr, "%s: ", commandname);
1027 if (!iflag || parsefile->fd)
1028 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001029 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001030 vfprintf(stderr, msg, ap);
1031 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001032}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001033
1034/*
1035 * Exverror is called to raise the error exception. If the second argument
1036 * is not NULL then error prints an error message using printf style
1037 * formatting. It then raises the error exception.
1038 */
1039static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1040static void
1041ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001042{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001043#if DEBUG
1044 if (msg) {
1045 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1046 TRACEV((msg, ap));
1047 TRACE(("\") pid=%d\n", getpid()));
1048 } else
1049 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1050 if (msg)
1051#endif
1052 ash_vmsg(msg, ap);
1053
1054 flush_stdout_stderr();
1055 raise_exception(cond);
1056 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001057}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001058
1059static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1060static void
1061ash_msg_and_raise_error(const char *msg, ...)
1062{
1063 va_list ap;
1064
1065 va_start(ap, msg);
1066 ash_vmsg_and_raise(EXERROR, msg, ap);
1067 /* NOTREACHED */
1068 va_end(ap);
1069}
1070
1071static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1072static void
1073ash_msg_and_raise(int cond, const char *msg, ...)
1074{
1075 va_list ap;
1076
1077 va_start(ap, msg);
1078 ash_vmsg_and_raise(cond, msg, ap);
1079 /* NOTREACHED */
1080 va_end(ap);
1081}
1082
1083/*
1084 * error/warning routines for external builtins
1085 */
1086static void
1087ash_msg(const char *fmt, ...)
1088{
1089 va_list ap;
1090
1091 va_start(ap, fmt);
1092 ash_vmsg(fmt, ap);
1093 va_end(ap);
1094}
1095
1096/*
1097 * Return a string describing an error. The returned string may be a
1098 * pointer to a static buffer that will be overwritten on the next call.
1099 * Action describes the operation that got the error.
1100 */
1101static const char *
1102errmsg(int e, const char *em)
1103{
1104 if (e == ENOENT || e == ENOTDIR) {
1105 return em;
1106 }
1107 return strerror(e);
1108}
1109
1110
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001111/* ============ Memory allocation */
1112
1113/*
1114 * It appears that grabstackstr() will barf with such alignments
1115 * because stalloc() will return a string allocated in a new stackblock.
1116 */
1117#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1118enum {
1119 /* Most machines require the value returned from malloc to be aligned
1120 * in some way. The following macro will get this right
1121 * on many machines. */
1122 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1123 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001124 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001125};
1126
1127struct stack_block {
1128 struct stack_block *prev;
1129 char space[MINSIZE];
1130};
1131
1132struct stackmark {
1133 struct stack_block *stackp;
1134 char *stacknxt;
1135 size_t stacknleft;
1136 struct stackmark *marknext;
1137};
1138
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001139
Denis Vlasenko01631112007-12-16 17:20:38 +00001140struct globals_memstack {
1141 struct stack_block *g_stackp; // = &stackbase;
1142 struct stackmark *markp;
1143 char *g_stacknxt; // = stackbase.space;
1144 char *sstrend; // = stackbase.space + MINSIZE;
1145 size_t g_stacknleft; // = MINSIZE;
1146 int herefd; // = -1;
1147 struct stack_block stackbase;
1148};
1149/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1150static struct globals_memstack *const ptr_to_globals_memstack __attribute__ ((section (".data")));
1151#define G_memstack (*ptr_to_globals_memstack)
1152#define g_stackp (G_memstack.g_stackp )
1153#define markp (G_memstack.markp )
1154#define g_stacknxt (G_memstack.g_stacknxt )
1155#define sstrend (G_memstack.sstrend )
1156#define g_stacknleft (G_memstack.g_stacknleft)
1157#define herefd (G_memstack.herefd )
1158#define stackbase (G_memstack.stackbase )
1159#define INIT_G_memstack() do { \
1160 (*(struct globals_memstack**)&ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1161 g_stackp = &stackbase; \
1162 g_stacknxt = stackbase.space; \
1163 g_stacknleft = MINSIZE; \
1164 sstrend = stackbase.space + MINSIZE; \
1165 herefd = -1; \
1166} while (0)
1167
1168#define stackblock() ((void *)g_stacknxt)
1169#define stackblocksize() g_stacknleft
1170
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001171
1172static void *
1173ckrealloc(void * p, size_t nbytes)
1174{
1175 p = realloc(p, nbytes);
1176 if (!p)
1177 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1178 return p;
1179}
1180
1181static void *
1182ckmalloc(size_t nbytes)
1183{
1184 return ckrealloc(NULL, nbytes);
1185}
1186
1187/*
1188 * Make a copy of a string in safe storage.
1189 */
1190static char *
1191ckstrdup(const char *s)
1192{
1193 char *p = strdup(s);
1194 if (!p)
1195 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1196 return p;
1197}
1198
1199/*
1200 * Parse trees for commands are allocated in lifo order, so we use a stack
1201 * to make this more efficient, and also to avoid all sorts of exception
1202 * handling code to handle interrupts in the middle of a parse.
1203 *
1204 * The size 504 was chosen because the Ultrix malloc handles that size
1205 * well.
1206 */
1207static void *
1208stalloc(size_t nbytes)
1209{
1210 char *p;
1211 size_t aligned;
1212
1213 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001214 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001215 size_t len;
1216 size_t blocksize;
1217 struct stack_block *sp;
1218
1219 blocksize = aligned;
1220 if (blocksize < MINSIZE)
1221 blocksize = MINSIZE;
1222 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1223 if (len < blocksize)
1224 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1225 INT_OFF;
1226 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001227 sp->prev = g_stackp;
1228 g_stacknxt = sp->space;
1229 g_stacknleft = blocksize;
1230 sstrend = g_stacknxt + blocksize;
1231 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001232 INT_ON;
1233 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001234 p = g_stacknxt;
1235 g_stacknxt += aligned;
1236 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001237 return p;
1238}
1239
1240static void
1241stunalloc(void *p)
1242{
1243#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001244 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001245 write(2, "stunalloc\n", 10);
1246 abort();
1247 }
1248#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001249 g_stacknleft += g_stacknxt - (char *)p;
1250 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001251}
1252
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001253/*
1254 * Like strdup but works with the ash stack.
1255 */
1256static char *
1257ststrdup(const char *p)
1258{
1259 size_t len = strlen(p) + 1;
1260 return memcpy(stalloc(len), p, len);
1261}
1262
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001263static void
1264setstackmark(struct stackmark *mark)
1265{
Denis Vlasenko01631112007-12-16 17:20:38 +00001266 mark->stackp = g_stackp;
1267 mark->stacknxt = g_stacknxt;
1268 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001269 mark->marknext = markp;
1270 markp = mark;
1271}
1272
1273static void
1274popstackmark(struct stackmark *mark)
1275{
1276 struct stack_block *sp;
1277
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001278 if (!mark->stackp)
1279 return;
1280
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001281 INT_OFF;
1282 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001283 while (g_stackp != mark->stackp) {
1284 sp = g_stackp;
1285 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001286 free(sp);
1287 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001288 g_stacknxt = mark->stacknxt;
1289 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001290 sstrend = mark->stacknxt + mark->stacknleft;
1291 INT_ON;
1292}
1293
1294/*
1295 * When the parser reads in a string, it wants to stick the string on the
1296 * stack and only adjust the stack pointer when it knows how big the
1297 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1298 * of space on top of the stack and stackblocklen returns the length of
1299 * this block. Growstackblock will grow this space by at least one byte,
1300 * possibly moving it (like realloc). Grabstackblock actually allocates the
1301 * part of the block that has been used.
1302 */
1303static void
1304growstackblock(void)
1305{
1306 size_t newlen;
1307
Denis Vlasenko01631112007-12-16 17:20:38 +00001308 newlen = g_stacknleft * 2;
1309 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001310 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1311 if (newlen < 128)
1312 newlen += 128;
1313
Denis Vlasenko01631112007-12-16 17:20:38 +00001314 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001315 struct stack_block *oldstackp;
1316 struct stackmark *xmark;
1317 struct stack_block *sp;
1318 struct stack_block *prevstackp;
1319 size_t grosslen;
1320
1321 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001322 oldstackp = g_stackp;
1323 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001324 prevstackp = sp->prev;
1325 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1326 sp = ckrealloc(sp, grosslen);
1327 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001328 g_stackp = sp;
1329 g_stacknxt = sp->space;
1330 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 sstrend = sp->space + newlen;
1332
1333 /*
1334 * Stack marks pointing to the start of the old block
1335 * must be relocated to point to the new block
1336 */
1337 xmark = markp;
1338 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001339 xmark->stackp = g_stackp;
1340 xmark->stacknxt = g_stacknxt;
1341 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001342 xmark = xmark->marknext;
1343 }
1344 INT_ON;
1345 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001346 char *oldspace = g_stacknxt;
1347 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348 char *p = stalloc(newlen);
1349
1350 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001351 g_stacknxt = memcpy(p, oldspace, oldlen);
1352 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001353 }
1354}
1355
1356static void
1357grabstackblock(size_t len)
1358{
1359 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001360 g_stacknxt += len;
1361 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001362}
1363
1364/*
1365 * The following routines are somewhat easier to use than the above.
1366 * The user declares a variable of type STACKSTR, which may be declared
1367 * to be a register. The macro STARTSTACKSTR initializes things. Then
1368 * the user uses the macro STPUTC to add characters to the string. In
1369 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1370 * grown as necessary. When the user is done, she can just leave the
1371 * string there and refer to it using stackblock(). Or she can allocate
1372 * the space for it using grabstackstr(). If it is necessary to allow
1373 * someone else to use the stack temporarily and then continue to grow
1374 * the string, the user should use grabstack to allocate the space, and
1375 * then call ungrabstr(p) to return to the previous mode of operation.
1376 *
1377 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1378 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1379 * is space for at least one character.
1380 */
1381static void *
1382growstackstr(void)
1383{
1384 size_t len = stackblocksize();
1385 if (herefd >= 0 && len >= 1024) {
1386 full_write(herefd, stackblock(), len);
1387 return stackblock();
1388 }
1389 growstackblock();
1390 return stackblock() + len;
1391}
1392
1393/*
1394 * Called from CHECKSTRSPACE.
1395 */
1396static char *
1397makestrspace(size_t newlen, char *p)
1398{
Denis Vlasenko01631112007-12-16 17:20:38 +00001399 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001400 size_t size = stackblocksize();
1401
1402 for (;;) {
1403 size_t nleft;
1404
1405 size = stackblocksize();
1406 nleft = size - len;
1407 if (nleft >= newlen)
1408 break;
1409 growstackblock();
1410 }
1411 return stackblock() + len;
1412}
1413
1414static char *
1415stack_nputstr(const char *s, size_t n, char *p)
1416{
1417 p = makestrspace(n, p);
1418 p = memcpy(p, s, n) + n;
1419 return p;
1420}
1421
1422static char *
1423stack_putstr(const char *s, char *p)
1424{
1425 return stack_nputstr(s, strlen(s), p);
1426}
1427
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001428static char *
1429_STPUTC(int c, char *p)
1430{
1431 if (p == sstrend)
1432 p = growstackstr();
1433 *p++ = c;
1434 return p;
1435}
1436
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001437#define STARTSTACKSTR(p) ((p) = stackblock())
1438#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001439#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001440 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001441 char *q = (p); \
1442 size_t l = (n); \
1443 size_t m = sstrend - q; \
1444 if (l > m) \
1445 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001446 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001447#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001448#define STACKSTRNUL(p) \
1449 do { \
1450 if ((p) == sstrend) \
1451 p = growstackstr(); \
1452 *p = '\0'; \
1453 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001454#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001455#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001456#define STADJUST(amount, p) (p += (amount))
1457
1458#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1459#define ungrabstackstr(s, p) stunalloc((s))
1460#define stackstrend() ((void *)sstrend)
1461
1462
1463/* ============ String helpers */
1464
1465/*
1466 * prefix -- see if pfx is a prefix of string.
1467 */
1468static char *
1469prefix(const char *string, const char *pfx)
1470{
1471 while (*pfx) {
1472 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001473 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001474 }
1475 return (char *) string;
1476}
1477
1478/*
1479 * Check for a valid number. This should be elsewhere.
1480 */
1481static int
1482is_number(const char *p)
1483{
1484 do {
1485 if (!isdigit(*p))
1486 return 0;
1487 } while (*++p != '\0');
1488 return 1;
1489}
1490
1491/*
1492 * Convert a string of digits to an integer, printing an error message on
1493 * failure.
1494 */
1495static int
1496number(const char *s)
1497{
1498 if (!is_number(s))
1499 ash_msg_and_raise_error(illnum, s);
1500 return atoi(s);
1501}
1502
1503/*
1504 * Produce a possibly single quoted string suitable as input to the shell.
1505 * The return string is allocated on the stack.
1506 */
1507static char *
1508single_quote(const char *s)
1509{
1510 char *p;
1511
1512 STARTSTACKSTR(p);
1513
1514 do {
1515 char *q;
1516 size_t len;
1517
1518 len = strchrnul(s, '\'') - s;
1519
1520 q = p = makestrspace(len + 3, p);
1521
1522 *q++ = '\'';
1523 q = memcpy(q, s, len) + len;
1524 *q++ = '\'';
1525 s += len;
1526
1527 STADJUST(q - p, p);
1528
1529 len = strspn(s, "'");
1530 if (!len)
1531 break;
1532
1533 q = p = makestrspace(len + 3, p);
1534
1535 *q++ = '"';
1536 q = memcpy(q, s, len) + len;
1537 *q++ = '"';
1538 s += len;
1539
1540 STADJUST(q - p, p);
1541 } while (*s);
1542
1543 USTPUTC(0, p);
1544
1545 return stackblock();
1546}
1547
1548
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001549/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001550
1551static char **argptr; /* argument list for builtin commands */
1552static char *optionarg; /* set by nextopt (like getopt) */
1553static char *optptr; /* used by nextopt */
1554
1555/*
1556 * XXX - should get rid of. have all builtins use getopt(3). the
1557 * library getopt must have the BSD extension static variable "optreset"
1558 * otherwise it can't be used within the shell safely.
1559 *
1560 * Standard option processing (a la getopt) for builtin routines. The
1561 * only argument that is passed to nextopt is the option string; the
1562 * other arguments are unnecessary. It return the character, or '\0' on
1563 * end of input.
1564 */
1565static int
1566nextopt(const char *optstring)
1567{
1568 char *p;
1569 const char *q;
1570 char c;
1571
1572 p = optptr;
1573 if (p == NULL || *p == '\0') {
1574 p = *argptr;
1575 if (p == NULL || *p != '-' || *++p == '\0')
1576 return '\0';
1577 argptr++;
1578 if (LONE_DASH(p)) /* check for "--" */
1579 return '\0';
1580 }
1581 c = *p++;
1582 for (q = optstring; *q != c; ) {
1583 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001584 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001585 if (*++q == ':')
1586 q++;
1587 }
1588 if (*++q == ':') {
1589 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001590 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001591 optionarg = p;
1592 p = NULL;
1593 }
1594 optptr = p;
1595 return c;
1596}
1597
1598
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001599/* ============ Math support definitions */
1600
1601#if ENABLE_ASH_MATH_SUPPORT_64
1602typedef int64_t arith_t;
1603#define arith_t_type long long
1604#else
1605typedef long arith_t;
1606#define arith_t_type long
1607#endif
1608
1609#if ENABLE_ASH_MATH_SUPPORT
1610static arith_t dash_arith(const char *);
1611static arith_t arith(const char *expr, int *perrcode);
1612#endif
1613
1614#if ENABLE_ASH_RANDOM_SUPPORT
1615static unsigned long rseed;
1616#ifndef DYNAMIC_VAR
1617#define DYNAMIC_VAR
1618#endif
1619#endif
1620
1621
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001622/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001623
Denis Vlasenko01631112007-12-16 17:20:38 +00001624/*
1625 * The parsefile structure pointed to by the global variable parsefile
1626 * contains information about the current file being read.
1627 */
1628struct redirtab {
1629 struct redirtab *next;
1630 int renamed[10];
1631 int nullredirs;
1632};
1633
1634struct shparam {
1635 int nparam; /* # of positional parameters (without $0) */
1636#if ENABLE_ASH_GETOPTS
1637 int optind; /* next parameter to be processed by getopts */
1638 int optoff; /* used by getopts */
1639#endif
1640 unsigned char malloced; /* if parameter list dynamically allocated */
1641 char **p; /* parameter list */
1642};
1643
1644/*
1645 * Free the list of positional parameters.
1646 */
1647static void
1648freeparam(volatile struct shparam *param)
1649{
1650 char **ap;
1651
1652 if (param->malloced) {
1653 for (ap = param->p; *ap; ap++)
1654 free(*ap);
1655 free(param->p);
1656 }
1657}
1658
1659#if ENABLE_ASH_GETOPTS
1660static void getoptsreset(const char *value);
1661#endif
1662
1663struct var {
1664 struct var *next; /* next entry in hash list */
1665 int flags; /* flags are defined above */
1666 const char *text; /* name=value */
1667 void (*func)(const char *); /* function to be called when */
1668 /* the variable gets set/unset */
1669};
1670
1671struct localvar {
1672 struct localvar *next; /* next local variable in list */
1673 struct var *vp; /* the variable that was made local */
1674 int flags; /* saved flags */
1675 const char *text; /* saved text */
1676};
1677
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001678/* flags */
1679#define VEXPORT 0x01 /* variable is exported */
1680#define VREADONLY 0x02 /* variable cannot be modified */
1681#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1682#define VTEXTFIXED 0x08 /* text is statically allocated */
1683#define VSTACK 0x10 /* text is allocated on the stack */
1684#define VUNSET 0x20 /* the variable is not set */
1685#define VNOFUNC 0x40 /* don't call the callback function */
1686#define VNOSET 0x80 /* do not set variable - just readonly test */
1687#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1688#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001689# define VDYNAMIC 0x200 /* dynamic variable */
1690#else
1691# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#endif
1693
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001694#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001695static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001696#define defifs (defifsvar + 4)
1697#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001698static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699#endif
1700
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001701
Denis Vlasenko01631112007-12-16 17:20:38 +00001702/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001703#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001704static void
1705change_lc_all(const char *value)
1706{
1707 if (value && *value != '\0')
1708 setlocale(LC_ALL, value);
1709}
1710static void
1711change_lc_ctype(const char *value)
1712{
1713 if (value && *value != '\0')
1714 setlocale(LC_CTYPE, value);
1715}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001716#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001717#if ENABLE_ASH_MAIL
1718static void chkmail(void);
1719static void changemail(const char *);
1720#endif
1721static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001722#if ENABLE_ASH_RANDOM_SUPPORT
1723static void change_random(const char *);
1724#endif
1725
Denis Vlasenko01631112007-12-16 17:20:38 +00001726static const struct {
1727 int flags;
1728 const char *text;
1729 void (*func)(const char *);
1730} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001732 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001734 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001736#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001737 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1738 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001740 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1741 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1742 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1743 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001745 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#endif
1747#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001748 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#endif
1750#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001751 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1752 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#endif
1754#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001755 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001756#endif
1757};
1758
Denis Vlasenko01631112007-12-16 17:20:38 +00001759
1760struct globals_var {
1761 struct shparam shellparam; /* $@ current positional parameters */
1762 struct redirtab *redirlist;
1763 int g_nullredirs;
1764 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1765 struct var *vartab[VTABSIZE];
1766 struct var varinit[ARRAY_SIZE(varinit_data)];
1767};
1768/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1769static struct globals_var *const ptr_to_globals_var __attribute__ ((section (".data")));
1770#define G_var (*ptr_to_globals_var)
1771#define shellparam (G_var.shellparam )
1772#define redirlist (G_var.redirlist )
1773#define g_nullredirs (G_var.g_nullredirs )
1774#define preverrout_fd (G_var.preverrout_fd)
1775#define vartab (G_var.vartab )
1776#define varinit (G_var.varinit )
1777#define INIT_G_var() do { \
1778 int i; \
1779 (*(struct globals_var**)&ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1780 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1781 varinit[i].flags = varinit_data[i].flags; \
1782 varinit[i].text = varinit_data[i].text; \
1783 varinit[i].func = varinit_data[i].func; \
1784 } \
1785} while (0)
1786
1787#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001788#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001789#define vmail (&vifs)[1]
1790#define vmpath (&vmail)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001792#define vmpath vifs
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001794#define vpath (&vmpath)[1]
1795#define vps1 (&vpath)[1]
1796#define vps2 (&vps1)[1]
1797#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001798#define voptind (&vps4)[1]
1799#if ENABLE_ASH_GETOPTS
1800#define vrandom (&voptind)[1]
1801#else
1802#define vrandom (&vps4)[1]
1803#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001804
1805/*
1806 * The following macros access the values of the above variables.
1807 * They have to skip over the name. They return the null string
1808 * for unset variables.
1809 */
1810#define ifsval() (vifs.text + 4)
1811#define ifsset() ((vifs.flags & VUNSET) == 0)
1812#define mailval() (vmail.text + 5)
1813#define mpathval() (vmpath.text + 9)
1814#define pathval() (vpath.text + 5)
1815#define ps1val() (vps1.text + 4)
1816#define ps2val() (vps2.text + 4)
1817#define ps4val() (vps4.text + 4)
1818#define optindval() (voptind.text + 7)
1819
1820#define mpathset() ((vmpath.flags & VUNSET) == 0)
1821
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001822
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001823#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1824#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1825
Denis Vlasenko01631112007-12-16 17:20:38 +00001826#if ENABLE_ASH_GETOPTS
1827static void
1828getoptsreset(const char *value)
1829{
1830 shellparam.optind = number(value);
1831 shellparam.optoff = -1;
1832}
1833#endif
1834
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001835/*
1836 * Return of a legal variable name (a letter or underscore followed by zero or
1837 * more letters, underscores, and digits).
1838 */
1839static char *
1840endofname(const char *name)
1841{
1842 char *p;
1843
1844 p = (char *) name;
1845 if (!is_name(*p))
1846 return p;
1847 while (*++p) {
1848 if (!is_in_name(*p))
1849 break;
1850 }
1851 return p;
1852}
1853
1854/*
1855 * Compares two strings up to the first = or '\0'. The first
1856 * string must be terminated by '='; the second may be terminated by
1857 * either '=' or '\0'.
1858 */
1859static int
1860varcmp(const char *p, const char *q)
1861{
1862 int c, d;
1863
1864 while ((c = *p) == (d = *q)) {
1865 if (!c || c == '=')
1866 goto out;
1867 p++;
1868 q++;
1869 }
1870 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001871 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001872 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001873 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001874 out:
1875 return c - d;
1876}
1877
1878static int
1879varequal(const char *a, const char *b)
1880{
1881 return !varcmp(a, b);
1882}
1883
1884/*
1885 * Find the appropriate entry in the hash table from the name.
1886 */
1887static struct var **
1888hashvar(const char *p)
1889{
1890 unsigned hashval;
1891
1892 hashval = ((unsigned char) *p) << 4;
1893 while (*p && *p != '=')
1894 hashval += (unsigned char) *p++;
1895 return &vartab[hashval % VTABSIZE];
1896}
1897
1898static int
1899vpcmp(const void *a, const void *b)
1900{
1901 return varcmp(*(const char **)a, *(const char **)b);
1902}
1903
1904/*
1905 * This routine initializes the builtin variables.
1906 */
1907static void
1908initvar(void)
1909{
1910 struct var *vp;
1911 struct var *end;
1912 struct var **vpp;
1913
1914 /*
1915 * PS1 depends on uid
1916 */
1917#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1918 vps1.text = "PS1=\\w \\$ ";
1919#else
1920 if (!geteuid())
1921 vps1.text = "PS1=# ";
1922#endif
1923 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001924 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001925 do {
1926 vpp = hashvar(vp->text);
1927 vp->next = *vpp;
1928 *vpp = vp;
1929 } while (++vp < end);
1930}
1931
1932static struct var **
1933findvar(struct var **vpp, const char *name)
1934{
1935 for (; *vpp; vpp = &(*vpp)->next) {
1936 if (varequal((*vpp)->text, name)) {
1937 break;
1938 }
1939 }
1940 return vpp;
1941}
1942
1943/*
1944 * Find the value of a variable. Returns NULL if not set.
1945 */
1946static char *
1947lookupvar(const char *name)
1948{
1949 struct var *v;
1950
1951 v = *findvar(hashvar(name), name);
1952 if (v) {
1953#ifdef DYNAMIC_VAR
1954 /*
1955 * Dynamic variables are implemented roughly the same way they are
1956 * in bash. Namely, they're "special" so long as they aren't unset.
1957 * As soon as they're unset, they're no longer dynamic, and dynamic
1958 * lookup will no longer happen at that point. -- PFM.
1959 */
1960 if ((v->flags & VDYNAMIC))
1961 (*v->func)(NULL);
1962#endif
1963 if (!(v->flags & VUNSET))
1964 return strchrnul(v->text, '=') + 1;
1965 }
1966 return NULL;
1967}
1968
1969/*
1970 * Search the environment of a builtin command.
1971 */
1972static char *
1973bltinlookup(const char *name)
1974{
1975 struct strlist *sp;
1976
1977 for (sp = cmdenviron; sp; sp = sp->next) {
1978 if (varequal(sp->text, name))
1979 return strchrnul(sp->text, '=') + 1;
1980 }
1981 return lookupvar(name);
1982}
1983
1984/*
1985 * Same as setvar except that the variable and value are passed in
1986 * the first argument as name=value. Since the first argument will
1987 * be actually stored in the table, it should not be a string that
1988 * will go away.
1989 * Called with interrupts off.
1990 */
1991static void
1992setvareq(char *s, int flags)
1993{
1994 struct var *vp, **vpp;
1995
1996 vpp = hashvar(s);
1997 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1998 vp = *findvar(vpp, s);
1999 if (vp) {
2000 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2001 const char *n;
2002
2003 if (flags & VNOSAVE)
2004 free(s);
2005 n = vp->text;
2006 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2007 }
2008
2009 if (flags & VNOSET)
2010 return;
2011
2012 if (vp->func && (flags & VNOFUNC) == 0)
2013 (*vp->func)(strchrnul(s, '=') + 1);
2014
2015 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2016 free((char*)vp->text);
2017
2018 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2019 } else {
2020 if (flags & VNOSET)
2021 return;
2022 /* not found */
2023 vp = ckmalloc(sizeof(*vp));
2024 vp->next = *vpp;
2025 vp->func = NULL;
2026 *vpp = vp;
2027 }
2028 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2029 s = ckstrdup(s);
2030 vp->text = s;
2031 vp->flags = flags;
2032}
2033
2034/*
2035 * Set the value of a variable. The flags argument is ored with the
2036 * flags of the variable. If val is NULL, the variable is unset.
2037 */
2038static void
2039setvar(const char *name, const char *val, int flags)
2040{
2041 char *p, *q;
2042 size_t namelen;
2043 char *nameeq;
2044 size_t vallen;
2045
2046 q = endofname(name);
2047 p = strchrnul(q, '=');
2048 namelen = p - name;
2049 if (!namelen || p != q)
2050 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2051 vallen = 0;
2052 if (val == NULL) {
2053 flags |= VUNSET;
2054 } else {
2055 vallen = strlen(val);
2056 }
2057 INT_OFF;
2058 nameeq = ckmalloc(namelen + vallen + 2);
2059 p = memcpy(nameeq, name, namelen) + namelen;
2060 if (val) {
2061 *p++ = '=';
2062 p = memcpy(p, val, vallen) + vallen;
2063 }
2064 *p = '\0';
2065 setvareq(nameeq, flags | VNOSAVE);
2066 INT_ON;
2067}
2068
2069#if ENABLE_ASH_GETOPTS
2070/*
2071 * Safe version of setvar, returns 1 on success 0 on failure.
2072 */
2073static int
2074setvarsafe(const char *name, const char *val, int flags)
2075{
2076 int err;
2077 volatile int saveint;
2078 struct jmploc *volatile savehandler = exception_handler;
2079 struct jmploc jmploc;
2080
2081 SAVE_INT(saveint);
2082 if (setjmp(jmploc.loc))
2083 err = 1;
2084 else {
2085 exception_handler = &jmploc;
2086 setvar(name, val, flags);
2087 err = 0;
2088 }
2089 exception_handler = savehandler;
2090 RESTORE_INT(saveint);
2091 return err;
2092}
2093#endif
2094
2095/*
2096 * Unset the specified variable.
2097 */
2098static int
2099unsetvar(const char *s)
2100{
2101 struct var **vpp;
2102 struct var *vp;
2103 int retval;
2104
2105 vpp = findvar(hashvar(s), s);
2106 vp = *vpp;
2107 retval = 2;
2108 if (vp) {
2109 int flags = vp->flags;
2110
2111 retval = 1;
2112 if (flags & VREADONLY)
2113 goto out;
2114#ifdef DYNAMIC_VAR
2115 vp->flags &= ~VDYNAMIC;
2116#endif
2117 if (flags & VUNSET)
2118 goto ok;
2119 if ((flags & VSTRFIXED) == 0) {
2120 INT_OFF;
2121 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2122 free((char*)vp->text);
2123 *vpp = vp->next;
2124 free(vp);
2125 INT_ON;
2126 } else {
2127 setvar(s, 0, 0);
2128 vp->flags &= ~VEXPORT;
2129 }
2130 ok:
2131 retval = 0;
2132 }
2133 out:
2134 return retval;
2135}
2136
2137/*
2138 * Process a linked list of variable assignments.
2139 */
2140static void
2141listsetvar(struct strlist *list_set_var, int flags)
2142{
2143 struct strlist *lp = list_set_var;
2144
2145 if (!lp)
2146 return;
2147 INT_OFF;
2148 do {
2149 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002150 lp = lp->next;
2151 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002152 INT_ON;
2153}
2154
2155/*
2156 * Generate a list of variables satisfying the given conditions.
2157 */
2158static char **
2159listvars(int on, int off, char ***end)
2160{
2161 struct var **vpp;
2162 struct var *vp;
2163 char **ep;
2164 int mask;
2165
2166 STARTSTACKSTR(ep);
2167 vpp = vartab;
2168 mask = on | off;
2169 do {
2170 for (vp = *vpp; vp; vp = vp->next) {
2171 if ((vp->flags & mask) == on) {
2172 if (ep == stackstrend())
2173 ep = growstackstr();
2174 *ep++ = (char *) vp->text;
2175 }
2176 }
2177 } while (++vpp < vartab + VTABSIZE);
2178 if (ep == stackstrend())
2179 ep = growstackstr();
2180 if (end)
2181 *end = ep;
2182 *ep++ = NULL;
2183 return grabstackstr(ep);
2184}
2185
2186
2187/* ============ Path search helper
2188 *
2189 * The variable path (passed by reference) should be set to the start
2190 * of the path before the first call; padvance will update
2191 * this value as it proceeds. Successive calls to padvance will return
2192 * the possible path expansions in sequence. If an option (indicated by
2193 * a percent sign) appears in the path entry then the global variable
2194 * pathopt will be set to point to it; otherwise pathopt will be set to
2195 * NULL.
2196 */
2197static const char *pathopt; /* set by padvance */
2198
2199static char *
2200padvance(const char **path, const char *name)
2201{
2202 const char *p;
2203 char *q;
2204 const char *start;
2205 size_t len;
2206
2207 if (*path == NULL)
2208 return NULL;
2209 start = *path;
2210 for (p = start; *p && *p != ':' && *p != '%'; p++);
2211 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2212 while (stackblocksize() < len)
2213 growstackblock();
2214 q = stackblock();
2215 if (p != start) {
2216 memcpy(q, start, p - start);
2217 q += p - start;
2218 *q++ = '/';
2219 }
2220 strcpy(q, name);
2221 pathopt = NULL;
2222 if (*p == '%') {
2223 pathopt = ++p;
2224 while (*p && *p != ':') p++;
2225 }
2226 if (*p == ':')
2227 *path = p + 1;
2228 else
2229 *path = NULL;
2230 return stalloc(len);
2231}
2232
2233
2234/* ============ Prompt */
2235
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002236static smallint doprompt; /* if set, prompt the user */
2237static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002238
2239#if ENABLE_FEATURE_EDITING
2240static line_input_t *line_input_state;
2241static const char *cmdedit_prompt;
2242static void
2243putprompt(const char *s)
2244{
2245 if (ENABLE_ASH_EXPAND_PRMT) {
2246 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002247 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002248 return;
2249 }
2250 cmdedit_prompt = s;
2251}
2252#else
2253static void
2254putprompt(const char *s)
2255{
2256 out2str(s);
2257}
2258#endif
2259
2260#if ENABLE_ASH_EXPAND_PRMT
2261/* expandstr() needs parsing machinery, so it is far away ahead... */
2262static const char *expandstr(const char *ps);
2263#else
2264#define expandstr(s) s
2265#endif
2266
2267static void
2268setprompt(int whichprompt)
2269{
2270 const char *prompt;
2271#if ENABLE_ASH_EXPAND_PRMT
2272 struct stackmark smark;
2273#endif
2274
2275 needprompt = 0;
2276
2277 switch (whichprompt) {
2278 case 1:
2279 prompt = ps1val();
2280 break;
2281 case 2:
2282 prompt = ps2val();
2283 break;
2284 default: /* 0 */
2285 prompt = nullstr;
2286 }
2287#if ENABLE_ASH_EXPAND_PRMT
2288 setstackmark(&smark);
2289 stalloc(stackblocksize());
2290#endif
2291 putprompt(expandstr(prompt));
2292#if ENABLE_ASH_EXPAND_PRMT
2293 popstackmark(&smark);
2294#endif
2295}
2296
2297
2298/* ============ The cd and pwd commands */
2299
2300#define CD_PHYSICAL 1
2301#define CD_PRINT 2
2302
2303static int docd(const char *, int);
2304
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002305static int
2306cdopt(void)
2307{
2308 int flags = 0;
2309 int i, j;
2310
2311 j = 'L';
2312 while ((i = nextopt("LP"))) {
2313 if (i != j) {
2314 flags ^= CD_PHYSICAL;
2315 j = i;
2316 }
2317 }
2318
2319 return flags;
2320}
2321
2322/*
2323 * Update curdir (the name of the current directory) in response to a
2324 * cd command.
2325 */
2326static const char *
2327updatepwd(const char *dir)
2328{
2329 char *new;
2330 char *p;
2331 char *cdcomppath;
2332 const char *lim;
2333
2334 cdcomppath = ststrdup(dir);
2335 STARTSTACKSTR(new);
2336 if (*dir != '/') {
2337 if (curdir == nullstr)
2338 return 0;
2339 new = stack_putstr(curdir, new);
2340 }
2341 new = makestrspace(strlen(dir) + 2, new);
2342 lim = stackblock() + 1;
2343 if (*dir != '/') {
2344 if (new[-1] != '/')
2345 USTPUTC('/', new);
2346 if (new > lim && *lim == '/')
2347 lim++;
2348 } else {
2349 USTPUTC('/', new);
2350 cdcomppath++;
2351 if (dir[1] == '/' && dir[2] != '/') {
2352 USTPUTC('/', new);
2353 cdcomppath++;
2354 lim++;
2355 }
2356 }
2357 p = strtok(cdcomppath, "/");
2358 while (p) {
2359 switch (*p) {
2360 case '.':
2361 if (p[1] == '.' && p[2] == '\0') {
2362 while (new > lim) {
2363 STUNPUTC(new);
2364 if (new[-1] == '/')
2365 break;
2366 }
2367 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002368 }
2369 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002370 break;
2371 /* fall through */
2372 default:
2373 new = stack_putstr(p, new);
2374 USTPUTC('/', new);
2375 }
2376 p = strtok(0, "/");
2377 }
2378 if (new > lim)
2379 STUNPUTC(new);
2380 *new = 0;
2381 return stackblock();
2382}
2383
2384/*
2385 * Find out what the current directory is. If we already know the current
2386 * directory, this routine returns immediately.
2387 */
2388static char *
2389getpwd(void)
2390{
Denis Vlasenko01631112007-12-16 17:20:38 +00002391 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002392 return dir ? dir : nullstr;
2393}
2394
2395static void
2396setpwd(const char *val, int setold)
2397{
2398 char *oldcur, *dir;
2399
2400 oldcur = dir = curdir;
2401
2402 if (setold) {
2403 setvar("OLDPWD", oldcur, VEXPORT);
2404 }
2405 INT_OFF;
2406 if (physdir != nullstr) {
2407 if (physdir != oldcur)
2408 free(physdir);
2409 physdir = nullstr;
2410 }
2411 if (oldcur == val || !val) {
2412 char *s = getpwd();
2413 physdir = s;
2414 if (!val)
2415 dir = s;
2416 } else
2417 dir = ckstrdup(val);
2418 if (oldcur != dir && oldcur != nullstr) {
2419 free(oldcur);
2420 }
2421 curdir = dir;
2422 INT_ON;
2423 setvar("PWD", dir, VEXPORT);
2424}
2425
2426static void hashcd(void);
2427
2428/*
2429 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2430 * know that the current directory has changed.
2431 */
2432static int
2433docd(const char *dest, int flags)
2434{
2435 const char *dir = 0;
2436 int err;
2437
2438 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2439
2440 INT_OFF;
2441 if (!(flags & CD_PHYSICAL)) {
2442 dir = updatepwd(dest);
2443 if (dir)
2444 dest = dir;
2445 }
2446 err = chdir(dest);
2447 if (err)
2448 goto out;
2449 setpwd(dir, 1);
2450 hashcd();
2451 out:
2452 INT_ON;
2453 return err;
2454}
2455
2456static int
2457cdcmd(int argc, char **argv)
2458{
2459 const char *dest;
2460 const char *path;
2461 const char *p;
2462 char c;
2463 struct stat statb;
2464 int flags;
2465
2466 flags = cdopt();
2467 dest = *argptr;
2468 if (!dest)
2469 dest = bltinlookup(homestr);
2470 else if (LONE_DASH(dest)) {
2471 dest = bltinlookup("OLDPWD");
2472 flags |= CD_PRINT;
2473 }
2474 if (!dest)
2475 dest = nullstr;
2476 if (*dest == '/')
2477 goto step7;
2478 if (*dest == '.') {
2479 c = dest[1];
2480 dotdot:
2481 switch (c) {
2482 case '\0':
2483 case '/':
2484 goto step6;
2485 case '.':
2486 c = dest[2];
2487 if (c != '.')
2488 goto dotdot;
2489 }
2490 }
2491 if (!*dest)
2492 dest = ".";
2493 path = bltinlookup("CDPATH");
2494 if (!path) {
2495 step6:
2496 step7:
2497 p = dest;
2498 goto docd;
2499 }
2500 do {
2501 c = *path;
2502 p = padvance(&path, dest);
2503 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2504 if (c && c != ':')
2505 flags |= CD_PRINT;
2506 docd:
2507 if (!docd(p, flags))
2508 goto out;
2509 break;
2510 }
2511 } while (path);
2512 ash_msg_and_raise_error("can't cd to %s", dest);
2513 /* NOTREACHED */
2514 out:
2515 if (flags & CD_PRINT)
2516 out1fmt(snlfmt, curdir);
2517 return 0;
2518}
2519
2520static int
2521pwdcmd(int argc, char **argv)
2522{
2523 int flags;
2524 const char *dir = curdir;
2525
2526 flags = cdopt();
2527 if (flags) {
2528 if (physdir == nullstr)
2529 setpwd(dir, 0);
2530 dir = physdir;
2531 }
2532 out1fmt(snlfmt, dir);
2533 return 0;
2534}
2535
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002536
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002537/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002538
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002539#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002540#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002541
Eric Andersenc470f442003-07-28 09:56:35 +00002542/* Syntax classes */
2543#define CWORD 0 /* character is nothing special */
2544#define CNL 1 /* newline character */
2545#define CBACK 2 /* a backslash character */
2546#define CSQUOTE 3 /* single quote */
2547#define CDQUOTE 4 /* double quote */
2548#define CENDQUOTE 5 /* a terminating quote */
2549#define CBQUOTE 6 /* backwards single quote */
2550#define CVAR 7 /* a dollar sign */
2551#define CENDVAR 8 /* a '}' character */
2552#define CLP 9 /* a left paren in arithmetic */
2553#define CRP 10 /* a right paren in arithmetic */
2554#define CENDFILE 11 /* end of file */
2555#define CCTL 12 /* like CWORD, except it must be escaped */
2556#define CSPCL 13 /* these terminate a word */
2557#define CIGN 14 /* character should be ignored */
2558
Denis Vlasenko131ae172007-02-18 13:00:19 +00002559#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002560#define SYNBASE 130
2561#define PEOF -130
2562#define PEOA -129
2563#define PEOA_OR_PEOF PEOA
2564#else
2565#define SYNBASE 129
2566#define PEOF -129
2567#define PEOA_OR_PEOF PEOF
2568#endif
2569
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002570/* number syntax index */
2571#define BASESYNTAX 0 /* not in quotes */
2572#define DQSYNTAX 1 /* in double quotes */
2573#define SQSYNTAX 2 /* in single quotes */
2574#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002575#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002576
Denis Vlasenko131ae172007-02-18 13:00:19 +00002577#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002578#define USE_SIT_FUNCTION
2579#endif
2580
Denis Vlasenko131ae172007-02-18 13:00:19 +00002581#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002582static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002583#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002584 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002585#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002586 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2587 { CNL, CNL, CNL, CNL }, /* 2, \n */
2588 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2589 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2590 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2591 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2592 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2593 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2594 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2595 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2596 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002597#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002598 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2599 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2600 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002601#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002602};
Eric Andersenc470f442003-07-28 09:56:35 +00002603#else
2604static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002605#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002607#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002608 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2609 { CNL, CNL, CNL }, /* 2, \n */
2610 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2611 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2612 { CVAR, CVAR, CWORD }, /* 5, $ */
2613 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2614 { CSPCL, CWORD, CWORD }, /* 7, ( */
2615 { CSPCL, CWORD, CWORD }, /* 8, ) */
2616 { CBACK, CBACK, CCTL }, /* 9, \ */
2617 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2618 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002619#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002620 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2621 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2622 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002623#endif
2624};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002625#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002626
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002627#ifdef USE_SIT_FUNCTION
2628
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002629static int
2630SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002631{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002632 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002633#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002634 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002635 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2636 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2637 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2638 11, 3 /* "}~" */
2639 };
2640#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002641 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002642 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2643 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2644 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2645 10, 2 /* "}~" */
2646 };
2647#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002648 const char *s;
2649 int indx;
2650
Eric Andersenc470f442003-07-28 09:56:35 +00002651 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002652 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002653#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002654 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002655 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002656 else
2657#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002658#define U_C(c) ((unsigned char)(c))
2659
2660 if ((unsigned char)c >= (unsigned char)(CTLESC)
2661 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2662 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002663 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002664 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002665 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002666 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002667 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002668 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669 }
2670 return S_I_T[indx][syntax];
2671}
2672
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002673#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002674
Denis Vlasenko131ae172007-02-18 13:00:19 +00002675#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002676#define CSPCL_CIGN_CIGN_CIGN 0
2677#define CSPCL_CWORD_CWORD_CWORD 1
2678#define CNL_CNL_CNL_CNL 2
2679#define CWORD_CCTL_CCTL_CWORD 3
2680#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2681#define CVAR_CVAR_CWORD_CVAR 5
2682#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2683#define CSPCL_CWORD_CWORD_CLP 7
2684#define CSPCL_CWORD_CWORD_CRP 8
2685#define CBACK_CBACK_CCTL_CBACK 9
2686#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2687#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2688#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2689#define CWORD_CWORD_CWORD_CWORD 13
2690#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002691#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002692#define CSPCL_CWORD_CWORD_CWORD 0
2693#define CNL_CNL_CNL_CNL 1
2694#define CWORD_CCTL_CCTL_CWORD 2
2695#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2696#define CVAR_CVAR_CWORD_CVAR 4
2697#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2698#define CSPCL_CWORD_CWORD_CLP 6
2699#define CSPCL_CWORD_CWORD_CRP 7
2700#define CBACK_CBACK_CCTL_CBACK 8
2701#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2702#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2703#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2704#define CWORD_CWORD_CWORD_CWORD 12
2705#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002706#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002707
2708static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002709 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002710 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002711#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002712 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2713#endif
2714 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2716 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2717 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2718 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2719 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2720 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2721 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2722 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002723 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2852 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2853 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2875 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002876 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002877 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2879 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002881 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002882 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2883 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2884 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2885 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2887 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2888 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2889 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2901 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2902 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2903 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2904 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2905 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2934 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2935 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2939 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2967 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2968 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2969 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002970};
2971
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002972#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2973
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002974#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002975
Eric Andersen2870d962001-07-02 17:27:21 +00002976
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002977/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002978
Denis Vlasenko131ae172007-02-18 13:00:19 +00002979#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002980
2981#define ALIASINUSE 1
2982#define ALIASDEAD 2
2983
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002984struct alias {
2985 struct alias *next;
2986 char *name;
2987 char *val;
2988 int flag;
2989};
2990
Denis Vlasenko01631112007-12-16 17:20:38 +00002991
2992static struct alias **atab; // [ATABSIZE];
2993#define INIT_G_alias() do { \
2994 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
2995} while (0)
2996
Eric Andersen2870d962001-07-02 17:27:21 +00002997
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002998static struct alias **
2999__lookupalias(const char *name) {
3000 unsigned int hashval;
3001 struct alias **app;
3002 const char *p;
3003 unsigned int ch;
3004
3005 p = name;
3006
3007 ch = (unsigned char)*p;
3008 hashval = ch << 4;
3009 while (ch) {
3010 hashval += ch;
3011 ch = (unsigned char)*++p;
3012 }
3013 app = &atab[hashval % ATABSIZE];
3014
3015 for (; *app; app = &(*app)->next) {
3016 if (strcmp(name, (*app)->name) == 0) {
3017 break;
3018 }
3019 }
3020
3021 return app;
3022}
3023
3024static struct alias *
3025lookupalias(const char *name, int check)
3026{
3027 struct alias *ap = *__lookupalias(name);
3028
3029 if (check && ap && (ap->flag & ALIASINUSE))
3030 return NULL;
3031 return ap;
3032}
3033
3034static struct alias *
3035freealias(struct alias *ap)
3036{
3037 struct alias *next;
3038
3039 if (ap->flag & ALIASINUSE) {
3040 ap->flag |= ALIASDEAD;
3041 return ap;
3042 }
3043
3044 next = ap->next;
3045 free(ap->name);
3046 free(ap->val);
3047 free(ap);
3048 return next;
3049}
Eric Andersencb57d552001-06-28 07:25:16 +00003050
Eric Andersenc470f442003-07-28 09:56:35 +00003051static void
3052setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003053{
3054 struct alias *ap, **app;
3055
3056 app = __lookupalias(name);
3057 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003058 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003059 if (ap) {
3060 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003061 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003062 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003063 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003064 ap->flag &= ~ALIASDEAD;
3065 } else {
3066 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003067 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003068 ap->name = ckstrdup(name);
3069 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003070 ap->flag = 0;
3071 ap->next = 0;
3072 *app = ap;
3073 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003074 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003075}
3076
Eric Andersenc470f442003-07-28 09:56:35 +00003077static int
3078unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003079{
Eric Andersencb57d552001-06-28 07:25:16 +00003080 struct alias **app;
3081
3082 app = __lookupalias(name);
3083
3084 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003085 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003086 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003087 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003088 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003089 }
3090
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003091 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003092}
3093
Eric Andersenc470f442003-07-28 09:56:35 +00003094static void
3095rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003096{
Eric Andersencb57d552001-06-28 07:25:16 +00003097 struct alias *ap, **app;
3098 int i;
3099
Denis Vlasenkob012b102007-02-19 22:43:01 +00003100 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003101 for (i = 0; i < ATABSIZE; i++) {
3102 app = &atab[i];
3103 for (ap = *app; ap; ap = *app) {
3104 *app = freealias(*app);
3105 if (ap == *app) {
3106 app = &ap->next;
3107 }
3108 }
3109 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003110 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003111}
3112
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003113static void
3114printalias(const struct alias *ap)
3115{
3116 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3117}
3118
Eric Andersencb57d552001-06-28 07:25:16 +00003119/*
3120 * TODO - sort output
3121 */
Eric Andersenc470f442003-07-28 09:56:35 +00003122static int
3123aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003124{
3125 char *n, *v;
3126 int ret = 0;
3127 struct alias *ap;
3128
3129 if (argc == 1) {
3130 int i;
3131
3132 for (i = 0; i < ATABSIZE; i++)
3133 for (ap = atab[i]; ap; ap = ap->next) {
3134 printalias(ap);
3135 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003136 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003137 }
3138 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003139 v = strchr(n+1, '=');
3140 if (v == NULL) { /* n+1: funny ksh stuff */
3141 ap = *__lookupalias(n);
3142 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003143 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003144 ret = 1;
3145 } else
3146 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003147 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003148 *v++ = '\0';
3149 setalias(n, v);
3150 }
3151 }
3152
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003153 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003154}
3155
Eric Andersenc470f442003-07-28 09:56:35 +00003156static int
3157unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003158{
3159 int i;
3160
3161 while ((i = nextopt("a")) != '\0') {
3162 if (i == 'a') {
3163 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003164 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003165 }
3166 }
3167 for (i = 0; *argptr; argptr++) {
3168 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003169 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003170 i = 1;
3171 }
3172 }
3173
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003174 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003175}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003176
Denis Vlasenko131ae172007-02-18 13:00:19 +00003177#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003178
Eric Andersenc470f442003-07-28 09:56:35 +00003179
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003180/* ============ jobs.c */
3181
3182/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3183#define FORK_FG 0
3184#define FORK_BG 1
3185#define FORK_NOJOB 2
3186
3187/* mode flags for showjob(s) */
3188#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3189#define SHOW_PID 0x04 /* include process pid */
3190#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3191
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003192/*
3193 * A job structure contains information about a job. A job is either a
3194 * single process or a set of processes contained in a pipeline. In the
3195 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3196 * array of pids.
3197 */
3198
3199struct procstat {
3200 pid_t pid; /* process id */
3201 int status; /* last process status from wait() */
3202 char *cmd; /* text of command being run */
3203};
3204
3205struct job {
3206 struct procstat ps0; /* status of process */
3207 struct procstat *ps; /* status or processes when more than one */
3208#if JOBS
3209 int stopstatus; /* status of a stopped job */
3210#endif
3211 uint32_t
3212 nprocs: 16, /* number of processes */
3213 state: 8,
3214#define JOBRUNNING 0 /* at least one proc running */
3215#define JOBSTOPPED 1 /* all procs are stopped */
3216#define JOBDONE 2 /* all procs are completed */
3217#if JOBS
3218 sigint: 1, /* job was killed by SIGINT */
3219 jobctl: 1, /* job running under job control */
3220#endif
3221 waited: 1, /* true if this entry has been waited for */
3222 used: 1, /* true if this entry is in used */
3223 changed: 1; /* true if status has changed */
3224 struct job *prev_job; /* previous job */
3225};
3226
3227static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003228static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003229
3230static struct job *makejob(union node *, int);
3231static int forkshell(struct job *, union node *, int);
3232static int waitforjob(struct job *);
3233
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003234#if !JOBS
3235enum { jobctl = 0 };
3236#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003237#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003238static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003239static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003240#endif
3241
3242/*
3243 * Set the signal handler for the specified signal. The routine figures
3244 * out what it should be set to.
3245 */
3246static void
3247setsignal(int signo)
3248{
3249 int action;
3250 char *t, tsig;
3251 struct sigaction act;
3252
3253 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003254 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003255 if (t == NULL)
3256 action = S_DFL;
3257 else if (*t != '\0')
3258 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259 if (rootshell && action == S_DFL) {
3260 switch (signo) {
3261 case SIGINT:
3262 if (iflag || minusc || sflag == 0)
3263 action = S_CATCH;
3264 break;
3265 case SIGQUIT:
3266#if DEBUG
3267 if (debug)
3268 break;
3269#endif
3270 /* FALLTHROUGH */
3271 case SIGTERM:
3272 if (iflag)
3273 action = S_IGN;
3274 break;
3275#if JOBS
3276 case SIGTSTP:
3277 case SIGTTOU:
3278 if (mflag)
3279 action = S_IGN;
3280 break;
3281#endif
3282 }
3283 }
3284
3285 t = &sigmode[signo - 1];
3286 tsig = *t;
3287 if (tsig == 0) {
3288 /*
3289 * current setting unknown
3290 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003291 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003292 /*
3293 * Pretend it worked; maybe we should give a warning
3294 * here, but other shells don't. We don't alter
3295 * sigmode, so that we retry every time.
3296 */
3297 return;
3298 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003299 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003300 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003301 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003302 if (mflag
3303 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3304 ) {
3305 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003306 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003307 }
3308 }
3309 if (tsig == S_HARD_IGN || tsig == action)
3310 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003311 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003312 switch (action) {
3313 case S_CATCH:
3314 act.sa_handler = onsig;
3315 break;
3316 case S_IGN:
3317 act.sa_handler = SIG_IGN;
3318 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003319 }
3320 *t = action;
3321 act.sa_flags = 0;
3322 sigfillset(&act.sa_mask);
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003323 sigaction(signo, &act, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003324}
3325
3326/* mode flags for set_curjob */
3327#define CUR_DELETE 2
3328#define CUR_RUNNING 1
3329#define CUR_STOPPED 0
3330
3331/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003332#define DOWAIT_NONBLOCK WNOHANG
3333#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334
3335#if JOBS
3336/* pgrp of shell on invocation */
3337static int initialpgrp;
3338static int ttyfd = -1;
3339#endif
3340/* array of jobs */
3341static struct job *jobtab;
3342/* size of array */
3343static unsigned njobs;
3344/* current job */
3345static struct job *curjob;
3346/* number of presumed living untracked jobs */
3347static int jobless;
3348
3349static void
3350set_curjob(struct job *jp, unsigned mode)
3351{
3352 struct job *jp1;
3353 struct job **jpp, **curp;
3354
3355 /* first remove from list */
3356 jpp = curp = &curjob;
3357 do {
3358 jp1 = *jpp;
3359 if (jp1 == jp)
3360 break;
3361 jpp = &jp1->prev_job;
3362 } while (1);
3363 *jpp = jp1->prev_job;
3364
3365 /* Then re-insert in correct position */
3366 jpp = curp;
3367 switch (mode) {
3368 default:
3369#if DEBUG
3370 abort();
3371#endif
3372 case CUR_DELETE:
3373 /* job being deleted */
3374 break;
3375 case CUR_RUNNING:
3376 /* newly created job or backgrounded job,
3377 put after all stopped jobs. */
3378 do {
3379 jp1 = *jpp;
3380#if JOBS
3381 if (!jp1 || jp1->state != JOBSTOPPED)
3382#endif
3383 break;
3384 jpp = &jp1->prev_job;
3385 } while (1);
3386 /* FALLTHROUGH */
3387#if JOBS
3388 case CUR_STOPPED:
3389#endif
3390 /* newly stopped job - becomes curjob */
3391 jp->prev_job = *jpp;
3392 *jpp = jp;
3393 break;
3394 }
3395}
3396
3397#if JOBS || DEBUG
3398static int
3399jobno(const struct job *jp)
3400{
3401 return jp - jobtab + 1;
3402}
3403#endif
3404
3405/*
3406 * Convert a job name to a job structure.
3407 */
3408static struct job *
3409getjob(const char *name, int getctl)
3410{
3411 struct job *jp;
3412 struct job *found;
3413 const char *err_msg = "No such job: %s";
3414 unsigned num;
3415 int c;
3416 const char *p;
3417 char *(*match)(const char *, const char *);
3418
3419 jp = curjob;
3420 p = name;
3421 if (!p)
3422 goto currentjob;
3423
3424 if (*p != '%')
3425 goto err;
3426
3427 c = *++p;
3428 if (!c)
3429 goto currentjob;
3430
3431 if (!p[1]) {
3432 if (c == '+' || c == '%') {
3433 currentjob:
3434 err_msg = "No current job";
3435 goto check;
3436 }
3437 if (c == '-') {
3438 if (jp)
3439 jp = jp->prev_job;
3440 err_msg = "No previous job";
3441 check:
3442 if (!jp)
3443 goto err;
3444 goto gotit;
3445 }
3446 }
3447
3448 if (is_number(p)) {
3449 num = atoi(p);
3450 if (num < njobs) {
3451 jp = jobtab + num - 1;
3452 if (jp->used)
3453 goto gotit;
3454 goto err;
3455 }
3456 }
3457
3458 match = prefix;
3459 if (*p == '?') {
3460 match = strstr;
3461 p++;
3462 }
3463
3464 found = 0;
3465 while (1) {
3466 if (!jp)
3467 goto err;
3468 if (match(jp->ps[0].cmd, p)) {
3469 if (found)
3470 goto err;
3471 found = jp;
3472 err_msg = "%s: ambiguous";
3473 }
3474 jp = jp->prev_job;
3475 }
3476
3477 gotit:
3478#if JOBS
3479 err_msg = "job %s not created under job control";
3480 if (getctl && jp->jobctl == 0)
3481 goto err;
3482#endif
3483 return jp;
3484 err:
3485 ash_msg_and_raise_error(err_msg, name);
3486}
3487
3488/*
3489 * Mark a job structure as unused.
3490 */
3491static void
3492freejob(struct job *jp)
3493{
3494 struct procstat *ps;
3495 int i;
3496
3497 INT_OFF;
3498 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3499 if (ps->cmd != nullstr)
3500 free(ps->cmd);
3501 }
3502 if (jp->ps != &jp->ps0)
3503 free(jp->ps);
3504 jp->used = 0;
3505 set_curjob(jp, CUR_DELETE);
3506 INT_ON;
3507}
3508
3509#if JOBS
3510static void
3511xtcsetpgrp(int fd, pid_t pgrp)
3512{
3513 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003514 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515}
3516
3517/*
3518 * Turn job control on and off.
3519 *
3520 * Note: This code assumes that the third arg to ioctl is a character
3521 * pointer, which is true on Berkeley systems but not System V. Since
3522 * System V doesn't have job control yet, this isn't a problem now.
3523 *
3524 * Called with interrupts off.
3525 */
3526static void
3527setjobctl(int on)
3528{
3529 int fd;
3530 int pgrp;
3531
3532 if (on == jobctl || rootshell == 0)
3533 return;
3534 if (on) {
3535 int ofd;
3536 ofd = fd = open(_PATH_TTY, O_RDWR);
3537 if (fd < 0) {
3538 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3539 * That sometimes helps to acquire controlling tty.
3540 * Obviously, a workaround for bugs when someone
3541 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003542 fd = 2;
3543 while (!isatty(fd))
3544 if (--fd < 0)
3545 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003546 }
3547 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003548 if (ofd >= 0)
3549 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003550 if (fd < 0)
3551 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003552 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003553 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003554 do { /* while we are in the background */
3555 pgrp = tcgetpgrp(fd);
3556 if (pgrp < 0) {
3557 out:
3558 ash_msg("can't access tty; job control turned off");
3559 mflag = on = 0;
3560 goto close;
3561 }
3562 if (pgrp == getpgrp())
3563 break;
3564 killpg(0, SIGTTIN);
3565 } while (1);
3566 initialpgrp = pgrp;
3567
3568 setsignal(SIGTSTP);
3569 setsignal(SIGTTOU);
3570 setsignal(SIGTTIN);
3571 pgrp = rootpid;
3572 setpgid(0, pgrp);
3573 xtcsetpgrp(fd, pgrp);
3574 } else {
3575 /* turning job control off */
3576 fd = ttyfd;
3577 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003578 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003579 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003580 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003581 setpgid(0, pgrp);
3582 setsignal(SIGTSTP);
3583 setsignal(SIGTTOU);
3584 setsignal(SIGTTIN);
3585 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003586 if (fd >= 0)
3587 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003588 fd = -1;
3589 }
3590 ttyfd = fd;
3591 jobctl = on;
3592}
3593
3594static int
3595killcmd(int argc, char **argv)
3596{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003597 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3598 int i = 1;
3599 do {
3600 if (argv[i][0] == '%') {
3601 struct job *jp = getjob(argv[i], 0);
3602 unsigned pid = jp->ps[0].pid;
3603 /* Enough space for ' -NNN<nul>' */
3604 argv[i] = alloca(sizeof(int)*3 + 3);
3605 /* kill_main has matching code to expect
3606 * leading space. Needed to not confuse
3607 * negative pids with "kill -SIGNAL_NO" syntax */
3608 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003609 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003610 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003611 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003612 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003613}
3614
3615static void
3616showpipe(struct job *jp, FILE *out)
3617{
3618 struct procstat *sp;
3619 struct procstat *spend;
3620
3621 spend = jp->ps + jp->nprocs;
3622 for (sp = jp->ps + 1; sp < spend; sp++)
3623 fprintf(out, " | %s", sp->cmd);
3624 outcslow('\n', out);
3625 flush_stdout_stderr();
3626}
3627
3628
3629static int
3630restartjob(struct job *jp, int mode)
3631{
3632 struct procstat *ps;
3633 int i;
3634 int status;
3635 pid_t pgid;
3636
3637 INT_OFF;
3638 if (jp->state == JOBDONE)
3639 goto out;
3640 jp->state = JOBRUNNING;
3641 pgid = jp->ps->pid;
3642 if (mode == FORK_FG)
3643 xtcsetpgrp(ttyfd, pgid);
3644 killpg(pgid, SIGCONT);
3645 ps = jp->ps;
3646 i = jp->nprocs;
3647 do {
3648 if (WIFSTOPPED(ps->status)) {
3649 ps->status = -1;
3650 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003651 ps++;
3652 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653 out:
3654 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3655 INT_ON;
3656 return status;
3657}
3658
3659static int
3660fg_bgcmd(int argc, char **argv)
3661{
3662 struct job *jp;
3663 FILE *out;
3664 int mode;
3665 int retval;
3666
3667 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3668 nextopt(nullstr);
3669 argv = argptr;
3670 out = stdout;
3671 do {
3672 jp = getjob(*argv, 1);
3673 if (mode == FORK_BG) {
3674 set_curjob(jp, CUR_RUNNING);
3675 fprintf(out, "[%d] ", jobno(jp));
3676 }
3677 outstr(jp->ps->cmd, out);
3678 showpipe(jp, out);
3679 retval = restartjob(jp, mode);
3680 } while (*argv && *++argv);
3681 return retval;
3682}
3683#endif
3684
3685static int
3686sprint_status(char *s, int status, int sigonly)
3687{
3688 int col;
3689 int st;
3690
3691 col = 0;
3692 if (!WIFEXITED(status)) {
3693#if JOBS
3694 if (WIFSTOPPED(status))
3695 st = WSTOPSIG(status);
3696 else
3697#endif
3698 st = WTERMSIG(status);
3699 if (sigonly) {
3700 if (st == SIGINT || st == SIGPIPE)
3701 goto out;
3702#if JOBS
3703 if (WIFSTOPPED(status))
3704 goto out;
3705#endif
3706 }
3707 st &= 0x7f;
3708 col = fmtstr(s, 32, strsignal(st));
3709 if (WCOREDUMP(status)) {
3710 col += fmtstr(s + col, 16, " (core dumped)");
3711 }
3712 } else if (!sigonly) {
3713 st = WEXITSTATUS(status);
3714 if (st)
3715 col = fmtstr(s, 16, "Done(%d)", st);
3716 else
3717 col = fmtstr(s, 16, "Done");
3718 }
3719 out:
3720 return col;
3721}
3722
3723/*
3724 * Do a wait system call. If job control is compiled in, we accept
3725 * stopped processes. If block is zero, we return a value of zero
3726 * rather than blocking.
3727 *
3728 * System V doesn't have a non-blocking wait system call. It does
3729 * have a SIGCLD signal that is sent to a process when one of it's
3730 * children dies. The obvious way to use SIGCLD would be to install
3731 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3732 * was received, and have waitproc bump another counter when it got
3733 * the status of a process. Waitproc would then know that a wait
3734 * system call would not block if the two counters were different.
3735 * This approach doesn't work because if a process has children that
3736 * have not been waited for, System V will send it a SIGCLD when it
3737 * installs a signal handler for SIGCLD. What this means is that when
3738 * a child exits, the shell will be sent SIGCLD signals continuously
3739 * until is runs out of stack space, unless it does a wait call before
3740 * restoring the signal handler. The code below takes advantage of
3741 * this (mis)feature by installing a signal handler for SIGCLD and
3742 * then checking to see whether it was called. If there are any
3743 * children to be waited for, it will be.
3744 *
3745 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3746 * waits at all. In this case, the user will not be informed when
3747 * a background process until the next time she runs a real program
3748 * (as opposed to running a builtin command or just typing return),
3749 * and the jobs command may give out of date information.
3750 */
3751static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003752waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003753{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003754#if JOBS
3755 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003756 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003757#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003758 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3759 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003760}
3761
3762/*
3763 * Wait for a process to terminate.
3764 */
3765static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003766dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003767{
3768 int pid;
3769 int status;
3770 struct job *jp;
3771 struct job *thisjob;
3772 int state;
3773
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003774 TRACE(("dowait(%d) called\n", wait_flags));
3775 pid = waitproc(wait_flags, &status);
3776 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003777 if (pid <= 0) {
3778 /* If we were doing blocking wait and (probably) got EINTR,
3779 * check for pending sigs received while waiting.
3780 * (NB: can be moved into callers if needed) */
3781 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3782 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003783 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003784 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785 INT_OFF;
3786 thisjob = NULL;
3787 for (jp = curjob; jp; jp = jp->prev_job) {
3788 struct procstat *sp;
3789 struct procstat *spend;
3790 if (jp->state == JOBDONE)
3791 continue;
3792 state = JOBDONE;
3793 spend = jp->ps + jp->nprocs;
3794 sp = jp->ps;
3795 do {
3796 if (sp->pid == pid) {
3797 TRACE(("Job %d: changing status of proc %d "
3798 "from 0x%x to 0x%x\n",
3799 jobno(jp), pid, sp->status, status));
3800 sp->status = status;
3801 thisjob = jp;
3802 }
3803 if (sp->status == -1)
3804 state = JOBRUNNING;
3805#if JOBS
3806 if (state == JOBRUNNING)
3807 continue;
3808 if (WIFSTOPPED(sp->status)) {
3809 jp->stopstatus = sp->status;
3810 state = JOBSTOPPED;
3811 }
3812#endif
3813 } while (++sp < spend);
3814 if (thisjob)
3815 goto gotjob;
3816 }
3817#if JOBS
3818 if (!WIFSTOPPED(status))
3819#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003820 jobless--;
3821 goto out;
3822
3823 gotjob:
3824 if (state != JOBRUNNING) {
3825 thisjob->changed = 1;
3826
3827 if (thisjob->state != state) {
3828 TRACE(("Job %d: changing state from %d to %d\n",
3829 jobno(thisjob), thisjob->state, state));
3830 thisjob->state = state;
3831#if JOBS
3832 if (state == JOBSTOPPED) {
3833 set_curjob(thisjob, CUR_STOPPED);
3834 }
3835#endif
3836 }
3837 }
3838
3839 out:
3840 INT_ON;
3841
3842 if (thisjob && thisjob == job) {
3843 char s[48 + 1];
3844 int len;
3845
3846 len = sprint_status(s, status, 1);
3847 if (len) {
3848 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003849 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003850 out2str(s);
3851 }
3852 }
3853 return pid;
3854}
3855
3856#if JOBS
3857static void
3858showjob(FILE *out, struct job *jp, int mode)
3859{
3860 struct procstat *ps;
3861 struct procstat *psend;
3862 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003863 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003864 char s[80];
3865
3866 ps = jp->ps;
3867
3868 if (mode & SHOW_PGID) {
3869 /* just output process (group) id of pipeline */
3870 fprintf(out, "%d\n", ps->pid);
3871 return;
3872 }
3873
3874 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003875 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876
3877 if (jp == curjob)
3878 s[col - 2] = '+';
3879 else if (curjob && jp == curjob->prev_job)
3880 s[col - 2] = '-';
3881
3882 if (mode & SHOW_PID)
3883 col += fmtstr(s + col, 16, "%d ", ps->pid);
3884
3885 psend = ps + jp->nprocs;
3886
3887 if (jp->state == JOBRUNNING) {
3888 strcpy(s + col, "Running");
3889 col += sizeof("Running") - 1;
3890 } else {
3891 int status = psend[-1].status;
3892 if (jp->state == JOBSTOPPED)
3893 status = jp->stopstatus;
3894 col += sprint_status(s + col, status, 0);
3895 }
3896
3897 goto start;
3898
3899 do {
3900 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003901 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003902 start:
3903 fprintf(out, "%s%*c%s",
3904 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3905 );
3906 if (!(mode & SHOW_PID)) {
3907 showpipe(jp, out);
3908 break;
3909 }
3910 if (++ps == psend) {
3911 outcslow('\n', out);
3912 break;
3913 }
3914 } while (1);
3915
3916 jp->changed = 0;
3917
3918 if (jp->state == JOBDONE) {
3919 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3920 freejob(jp);
3921 }
3922}
3923
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003924/*
3925 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3926 * statuses have changed since the last call to showjobs.
3927 */
3928static void
3929showjobs(FILE *out, int mode)
3930{
3931 struct job *jp;
3932
3933 TRACE(("showjobs(%x) called\n", mode));
3934
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003935 /* If not even one job changed, there is nothing to do */
3936 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003937 continue;
3938
3939 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003940 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003941 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003942 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 }
3944}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003945
3946static int
3947jobscmd(int argc, char **argv)
3948{
3949 int mode, m;
3950
3951 mode = 0;
3952 while ((m = nextopt("lp"))) {
3953 if (m == 'l')
3954 mode = SHOW_PID;
3955 else
3956 mode = SHOW_PGID;
3957 }
3958
3959 argv = argptr;
3960 if (*argv) {
3961 do
3962 showjob(stdout, getjob(*argv,0), mode);
3963 while (*++argv);
3964 } else
3965 showjobs(stdout, mode);
3966
3967 return 0;
3968}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969#endif /* JOBS */
3970
3971static int
3972getstatus(struct job *job)
3973{
3974 int status;
3975 int retval;
3976
3977 status = job->ps[job->nprocs - 1].status;
3978 retval = WEXITSTATUS(status);
3979 if (!WIFEXITED(status)) {
3980#if JOBS
3981 retval = WSTOPSIG(status);
3982 if (!WIFSTOPPED(status))
3983#endif
3984 {
3985 /* XXX: limits number of signals */
3986 retval = WTERMSIG(status);
3987#if JOBS
3988 if (retval == SIGINT)
3989 job->sigint = 1;
3990#endif
3991 }
3992 retval += 128;
3993 }
3994 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3995 jobno(job), job->nprocs, status, retval));
3996 return retval;
3997}
3998
3999static int
4000waitcmd(int argc, char **argv)
4001{
4002 struct job *job;
4003 int retval;
4004 struct job *jp;
4005
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004006// exsig++;
4007// xbarrier();
4008 if (pendingsig)
4009 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004010
4011 nextopt(nullstr);
4012 retval = 0;
4013
4014 argv = argptr;
4015 if (!*argv) {
4016 /* wait for all jobs */
4017 for (;;) {
4018 jp = curjob;
4019 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004020 if (!jp) /* no running procs */
4021 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004022 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
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004054 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 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++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004453 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004454 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 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004616 if (jp->sigint) /* TODO: do the same with all signals */
4617 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004618 }
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++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006271 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006272 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)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006503/* "regular" bltins always take precedence over commands,
6504 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006505#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006506#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006507
6508struct cmdentry {
6509 int cmdtype;
6510 union param {
6511 int index;
6512 const struct builtincmd *cmd;
6513 struct funcnode *func;
6514 } u;
6515};
6516/* values of cmdtype */
6517#define CMDUNKNOWN -1 /* no entry in table for command */
6518#define CMDNORMAL 0 /* command is an executable program */
6519#define CMDFUNCTION 1 /* command is a shell function */
6520#define CMDBUILTIN 2 /* command is a shell builtin */
6521
6522/* action to find_command() */
6523#define DO_ERR 0x01 /* prints errors */
6524#define DO_ABS 0x02 /* checks absolute paths */
6525#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6526#define DO_ALTPATH 0x08 /* using alternate path */
6527#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6528
6529static void find_command(char *, struct cmdentry *, int, const char *);
6530
6531
6532/* ============ Hashing commands */
6533
6534/*
6535 * When commands are first encountered, they are entered in a hash table.
6536 * This ensures that a full path search will not have to be done for them
6537 * on each invocation.
6538 *
6539 * We should investigate converting to a linear search, even though that
6540 * would make the command name "hash" a misnomer.
6541 */
6542
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006543#define ARB 1 /* actual size determined at run time */
6544
6545struct tblentry {
6546 struct tblentry *next; /* next entry in hash chain */
6547 union param param; /* definition of builtin function */
6548 short cmdtype; /* index identifying command */
6549 char rehash; /* if set, cd done since entry created */
6550 char cmdname[ARB]; /* name of command */
6551};
6552
Denis Vlasenko01631112007-12-16 17:20:38 +00006553static struct tblentry **cmdtable;
6554#define INIT_G_cmdtable() do { \
6555 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6556} while (0)
6557
6558static int builtinloc = -1; /* index in path of %builtin, or -1 */
6559
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006560
6561static void
6562tryexec(char *cmd, char **argv, char **envp)
6563{
6564 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006565
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006566#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006567 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006568 int a = find_applet_by_name(cmd);
6569 if (a >= 0) {
6570 if (APPLET_IS_NOEXEC(a))
6571 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006572 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006573 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006574 /* If they called chroot or otherwise made the binary no longer
6575 * executable, fall through */
6576 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006577 }
6578#endif
6579
6580 repeat:
6581#ifdef SYSV
6582 do {
6583 execve(cmd, argv, envp);
6584 } while (errno == EINTR);
6585#else
6586 execve(cmd, argv, envp);
6587#endif
6588 if (repeated++) {
6589 free(argv);
6590 } else if (errno == ENOEXEC) {
6591 char **ap;
6592 char **new;
6593
6594 for (ap = argv; *ap; ap++)
6595 ;
6596 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6597 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006598 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006599 ap += 2;
6600 argv++;
6601 while ((*ap++ = *argv++))
6602 ;
6603 argv = new;
6604 goto repeat;
6605 }
6606}
6607
6608/*
6609 * Exec a program. Never returns. If you change this routine, you may
6610 * have to change the find_command routine as well.
6611 */
6612#define environment() listvars(VEXPORT, VUNSET, 0)
6613static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6614static void
6615shellexec(char **argv, const char *path, int idx)
6616{
6617 char *cmdname;
6618 int e;
6619 char **envp;
6620 int exerrno;
6621
6622 clearredir(1);
6623 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006624 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006625#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006626 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006627#endif
6628 ) {
6629 tryexec(argv[0], argv, envp);
6630 e = errno;
6631 } else {
6632 e = ENOENT;
6633 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6634 if (--idx < 0 && pathopt == NULL) {
6635 tryexec(cmdname, argv, envp);
6636 if (errno != ENOENT && errno != ENOTDIR)
6637 e = errno;
6638 }
6639 stunalloc(cmdname);
6640 }
6641 }
6642
6643 /* Map to POSIX errors */
6644 switch (e) {
6645 case EACCES:
6646 exerrno = 126;
6647 break;
6648 case ENOENT:
6649 exerrno = 127;
6650 break;
6651 default:
6652 exerrno = 2;
6653 break;
6654 }
6655 exitstatus = exerrno;
6656 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6657 argv[0], e, suppressint ));
6658 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6659 /* NOTREACHED */
6660}
6661
6662static void
6663printentry(struct tblentry *cmdp)
6664{
6665 int idx;
6666 const char *path;
6667 char *name;
6668
6669 idx = cmdp->param.index;
6670 path = pathval();
6671 do {
6672 name = padvance(&path, cmdp->cmdname);
6673 stunalloc(name);
6674 } while (--idx >= 0);
6675 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6676}
6677
6678/*
6679 * Clear out command entries. The argument specifies the first entry in
6680 * PATH which has changed.
6681 */
6682static void
6683clearcmdentry(int firstchange)
6684{
6685 struct tblentry **tblp;
6686 struct tblentry **pp;
6687 struct tblentry *cmdp;
6688
6689 INT_OFF;
6690 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6691 pp = tblp;
6692 while ((cmdp = *pp) != NULL) {
6693 if ((cmdp->cmdtype == CMDNORMAL &&
6694 cmdp->param.index >= firstchange)
6695 || (cmdp->cmdtype == CMDBUILTIN &&
6696 builtinloc >= firstchange)
6697 ) {
6698 *pp = cmdp->next;
6699 free(cmdp);
6700 } else {
6701 pp = &cmdp->next;
6702 }
6703 }
6704 }
6705 INT_ON;
6706}
6707
6708/*
6709 * Locate a command in the command hash table. If "add" is nonzero,
6710 * add the command to the table if it is not already present. The
6711 * variable "lastcmdentry" is set to point to the address of the link
6712 * pointing to the entry, so that delete_cmd_entry can delete the
6713 * entry.
6714 *
6715 * Interrupts must be off if called with add != 0.
6716 */
6717static struct tblentry **lastcmdentry;
6718
6719static struct tblentry *
6720cmdlookup(const char *name, int add)
6721{
6722 unsigned int hashval;
6723 const char *p;
6724 struct tblentry *cmdp;
6725 struct tblentry **pp;
6726
6727 p = name;
6728 hashval = (unsigned char)*p << 4;
6729 while (*p)
6730 hashval += (unsigned char)*p++;
6731 hashval &= 0x7FFF;
6732 pp = &cmdtable[hashval % CMDTABLESIZE];
6733 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6734 if (strcmp(cmdp->cmdname, name) == 0)
6735 break;
6736 pp = &cmdp->next;
6737 }
6738 if (add && cmdp == NULL) {
6739 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6740 + strlen(name) + 1);
6741 cmdp->next = NULL;
6742 cmdp->cmdtype = CMDUNKNOWN;
6743 strcpy(cmdp->cmdname, name);
6744 }
6745 lastcmdentry = pp;
6746 return cmdp;
6747}
6748
6749/*
6750 * Delete the command entry returned on the last lookup.
6751 */
6752static void
6753delete_cmd_entry(void)
6754{
6755 struct tblentry *cmdp;
6756
6757 INT_OFF;
6758 cmdp = *lastcmdentry;
6759 *lastcmdentry = cmdp->next;
6760 if (cmdp->cmdtype == CMDFUNCTION)
6761 freefunc(cmdp->param.func);
6762 free(cmdp);
6763 INT_ON;
6764}
6765
6766/*
6767 * Add a new command entry, replacing any existing command entry for
6768 * the same name - except special builtins.
6769 */
6770static void
6771addcmdentry(char *name, struct cmdentry *entry)
6772{
6773 struct tblentry *cmdp;
6774
6775 cmdp = cmdlookup(name, 1);
6776 if (cmdp->cmdtype == CMDFUNCTION) {
6777 freefunc(cmdp->param.func);
6778 }
6779 cmdp->cmdtype = entry->cmdtype;
6780 cmdp->param = entry->u;
6781 cmdp->rehash = 0;
6782}
6783
6784static int
6785hashcmd(int argc, char **argv)
6786{
6787 struct tblentry **pp;
6788 struct tblentry *cmdp;
6789 int c;
6790 struct cmdentry entry;
6791 char *name;
6792
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006793 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006794 clearcmdentry(0);
6795 return 0;
6796 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006797
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006798 if (*argptr == NULL) {
6799 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6800 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6801 if (cmdp->cmdtype == CMDNORMAL)
6802 printentry(cmdp);
6803 }
6804 }
6805 return 0;
6806 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006807
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006808 c = 0;
6809 while ((name = *argptr) != NULL) {
6810 cmdp = cmdlookup(name, 0);
6811 if (cmdp != NULL
6812 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006813 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6814 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006815 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006816 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006817 find_command(name, &entry, DO_ERR, pathval());
6818 if (entry.cmdtype == CMDUNKNOWN)
6819 c = 1;
6820 argptr++;
6821 }
6822 return c;
6823}
6824
6825/*
6826 * Called when a cd is done. Marks all commands so the next time they
6827 * are executed they will be rehashed.
6828 */
6829static void
6830hashcd(void)
6831{
6832 struct tblentry **pp;
6833 struct tblentry *cmdp;
6834
6835 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6836 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006837 if (cmdp->cmdtype == CMDNORMAL
6838 || (cmdp->cmdtype == CMDBUILTIN
6839 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6840 && builtinloc > 0)
6841 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006842 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006843 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006844 }
6845 }
6846}
6847
6848/*
6849 * Fix command hash table when PATH changed.
6850 * Called before PATH is changed. The argument is the new value of PATH;
6851 * pathval() still returns the old value at this point.
6852 * Called with interrupts off.
6853 */
6854static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006855changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006856{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006857 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006858 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006859 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860 int idx_bltin;
6861
6862 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006863 firstchange = 9999; /* assume no change */
6864 idx = 0;
6865 idx_bltin = -1;
6866 for (;;) {
6867 if (*old != *new) {
6868 firstchange = idx;
6869 if ((*old == '\0' && *new == ':')
6870 || (*old == ':' && *new == '\0'))
6871 firstchange++;
6872 old = new; /* ignore subsequent differences */
6873 }
6874 if (*new == '\0')
6875 break;
6876 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6877 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006878 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006879 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006880 new++, old++;
6881 }
6882 if (builtinloc < 0 && idx_bltin >= 0)
6883 builtinloc = idx_bltin; /* zap builtins */
6884 if (builtinloc >= 0 && idx_bltin < 0)
6885 firstchange = 0;
6886 clearcmdentry(firstchange);
6887 builtinloc = idx_bltin;
6888}
6889
6890#define TEOF 0
6891#define TNL 1
6892#define TREDIR 2
6893#define TWORD 3
6894#define TSEMI 4
6895#define TBACKGND 5
6896#define TAND 6
6897#define TOR 7
6898#define TPIPE 8
6899#define TLP 9
6900#define TRP 10
6901#define TENDCASE 11
6902#define TENDBQUOTE 12
6903#define TNOT 13
6904#define TCASE 14
6905#define TDO 15
6906#define TDONE 16
6907#define TELIF 17
6908#define TELSE 18
6909#define TESAC 19
6910#define TFI 20
6911#define TFOR 21
6912#define TIF 22
6913#define TIN 23
6914#define TTHEN 24
6915#define TUNTIL 25
6916#define TWHILE 26
6917#define TBEGIN 27
6918#define TEND 28
6919
6920/* first char is indicating which tokens mark the end of a list */
6921static const char *const tokname_array[] = {
6922 "\1end of file",
6923 "\0newline",
6924 "\0redirection",
6925 "\0word",
6926 "\0;",
6927 "\0&",
6928 "\0&&",
6929 "\0||",
6930 "\0|",
6931 "\0(",
6932 "\1)",
6933 "\1;;",
6934 "\1`",
6935#define KWDOFFSET 13
6936 /* the following are keywords */
6937 "\0!",
6938 "\0case",
6939 "\1do",
6940 "\1done",
6941 "\1elif",
6942 "\1else",
6943 "\1esac",
6944 "\1fi",
6945 "\0for",
6946 "\0if",
6947 "\0in",
6948 "\1then",
6949 "\0until",
6950 "\0while",
6951 "\0{",
6952 "\1}",
6953};
6954
6955static const char *
6956tokname(int tok)
6957{
6958 static char buf[16];
6959
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006960//try this:
6961//if (tok < TSEMI) return tokname_array[tok] + 1;
6962//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6963//return buf;
6964
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006965 if (tok >= TSEMI)
6966 buf[0] = '"';
6967 sprintf(buf + (tok >= TSEMI), "%s%c",
6968 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6969 return buf;
6970}
6971
6972/* Wrapper around strcmp for qsort/bsearch/... */
6973static int
6974pstrcmp(const void *a, const void *b)
6975{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006976 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006977}
6978
6979static const char *const *
6980findkwd(const char *s)
6981{
6982 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006983 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6984 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985}
6986
6987/*
6988 * Locate and print what a word is...
6989 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006990static int
6991describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992{
6993 struct cmdentry entry;
6994 struct tblentry *cmdp;
6995#if ENABLE_ASH_ALIAS
6996 const struct alias *ap;
6997#endif
6998 const char *path = pathval();
6999
7000 if (describe_command_verbose) {
7001 out1str(command);
7002 }
7003
7004 /* First look at the keywords */
7005 if (findkwd(command)) {
7006 out1str(describe_command_verbose ? " is a shell keyword" : command);
7007 goto out;
7008 }
7009
7010#if ENABLE_ASH_ALIAS
7011 /* Then look at the aliases */
7012 ap = lookupalias(command, 0);
7013 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007014 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007015 out1str("alias ");
7016 printalias(ap);
7017 return 0;
7018 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007019 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007020 goto out;
7021 }
7022#endif
7023 /* Then check if it is a tracked alias */
7024 cmdp = cmdlookup(command, 0);
7025 if (cmdp != NULL) {
7026 entry.cmdtype = cmdp->cmdtype;
7027 entry.u = cmdp->param;
7028 } else {
7029 /* Finally use brute force */
7030 find_command(command, &entry, DO_ABS, path);
7031 }
7032
7033 switch (entry.cmdtype) {
7034 case CMDNORMAL: {
7035 int j = entry.u.index;
7036 char *p;
7037 if (j == -1) {
7038 p = command;
7039 } else {
7040 do {
7041 p = padvance(&path, command);
7042 stunalloc(p);
7043 } while (--j >= 0);
7044 }
7045 if (describe_command_verbose) {
7046 out1fmt(" is%s %s",
7047 (cmdp ? " a tracked alias for" : nullstr), p
7048 );
7049 } else {
7050 out1str(p);
7051 }
7052 break;
7053 }
7054
7055 case CMDFUNCTION:
7056 if (describe_command_verbose) {
7057 out1str(" is a shell function");
7058 } else {
7059 out1str(command);
7060 }
7061 break;
7062
7063 case CMDBUILTIN:
7064 if (describe_command_verbose) {
7065 out1fmt(" is a %sshell builtin",
7066 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7067 "special " : nullstr
7068 );
7069 } else {
7070 out1str(command);
7071 }
7072 break;
7073
7074 default:
7075 if (describe_command_verbose) {
7076 out1str(": not found\n");
7077 }
7078 return 127;
7079 }
7080 out:
7081 outstr("\n", stdout);
7082 return 0;
7083}
7084
7085static int
7086typecmd(int argc, char **argv)
7087{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007088 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007089 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007090 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007091
Denis Vlasenko46846e22007-05-20 13:08:31 +00007092 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007093 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007094 i++;
7095 verbose = 0;
7096 }
7097 while (i < argc) {
7098 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007099 }
7100 return err;
7101}
7102
7103#if ENABLE_ASH_CMDCMD
7104static int
7105commandcmd(int argc, char **argv)
7106{
7107 int c;
7108 enum {
7109 VERIFY_BRIEF = 1,
7110 VERIFY_VERBOSE = 2,
7111 } verify = 0;
7112
7113 while ((c = nextopt("pvV")) != '\0')
7114 if (c == 'V')
7115 verify |= VERIFY_VERBOSE;
7116 else if (c == 'v')
7117 verify |= VERIFY_BRIEF;
7118#if DEBUG
7119 else if (c != 'p')
7120 abort();
7121#endif
7122 if (verify)
7123 return describe_command(*argptr, verify - VERIFY_BRIEF);
7124
7125 return 0;
7126}
7127#endif
7128
7129
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007130/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007131
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007132static int funcblocksize; /* size of structures in function */
7133static int funcstringsize; /* size of strings in node */
7134static void *funcblock; /* block to allocate function from */
7135static char *funcstring; /* block to allocate strings from */
7136
Eric Andersencb57d552001-06-28 07:25:16 +00007137/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007138#define EV_EXIT 01 /* exit after evaluating tree */
7139#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7140#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007141
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007142static const short nodesize[26] = {
7143 SHELL_ALIGN(sizeof(struct ncmd)),
7144 SHELL_ALIGN(sizeof(struct npipe)),
7145 SHELL_ALIGN(sizeof(struct nredir)),
7146 SHELL_ALIGN(sizeof(struct nredir)),
7147 SHELL_ALIGN(sizeof(struct nredir)),
7148 SHELL_ALIGN(sizeof(struct nbinary)),
7149 SHELL_ALIGN(sizeof(struct nbinary)),
7150 SHELL_ALIGN(sizeof(struct nbinary)),
7151 SHELL_ALIGN(sizeof(struct nif)),
7152 SHELL_ALIGN(sizeof(struct nbinary)),
7153 SHELL_ALIGN(sizeof(struct nbinary)),
7154 SHELL_ALIGN(sizeof(struct nfor)),
7155 SHELL_ALIGN(sizeof(struct ncase)),
7156 SHELL_ALIGN(sizeof(struct nclist)),
7157 SHELL_ALIGN(sizeof(struct narg)),
7158 SHELL_ALIGN(sizeof(struct narg)),
7159 SHELL_ALIGN(sizeof(struct nfile)),
7160 SHELL_ALIGN(sizeof(struct nfile)),
7161 SHELL_ALIGN(sizeof(struct nfile)),
7162 SHELL_ALIGN(sizeof(struct nfile)),
7163 SHELL_ALIGN(sizeof(struct nfile)),
7164 SHELL_ALIGN(sizeof(struct ndup)),
7165 SHELL_ALIGN(sizeof(struct ndup)),
7166 SHELL_ALIGN(sizeof(struct nhere)),
7167 SHELL_ALIGN(sizeof(struct nhere)),
7168 SHELL_ALIGN(sizeof(struct nnot)),
7169};
7170
7171static void calcsize(union node *n);
7172
7173static void
7174sizenodelist(struct nodelist *lp)
7175{
7176 while (lp) {
7177 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7178 calcsize(lp->n);
7179 lp = lp->next;
7180 }
7181}
7182
7183static void
7184calcsize(union node *n)
7185{
7186 if (n == NULL)
7187 return;
7188 funcblocksize += nodesize[n->type];
7189 switch (n->type) {
7190 case NCMD:
7191 calcsize(n->ncmd.redirect);
7192 calcsize(n->ncmd.args);
7193 calcsize(n->ncmd.assign);
7194 break;
7195 case NPIPE:
7196 sizenodelist(n->npipe.cmdlist);
7197 break;
7198 case NREDIR:
7199 case NBACKGND:
7200 case NSUBSHELL:
7201 calcsize(n->nredir.redirect);
7202 calcsize(n->nredir.n);
7203 break;
7204 case NAND:
7205 case NOR:
7206 case NSEMI:
7207 case NWHILE:
7208 case NUNTIL:
7209 calcsize(n->nbinary.ch2);
7210 calcsize(n->nbinary.ch1);
7211 break;
7212 case NIF:
7213 calcsize(n->nif.elsepart);
7214 calcsize(n->nif.ifpart);
7215 calcsize(n->nif.test);
7216 break;
7217 case NFOR:
7218 funcstringsize += strlen(n->nfor.var) + 1;
7219 calcsize(n->nfor.body);
7220 calcsize(n->nfor.args);
7221 break;
7222 case NCASE:
7223 calcsize(n->ncase.cases);
7224 calcsize(n->ncase.expr);
7225 break;
7226 case NCLIST:
7227 calcsize(n->nclist.body);
7228 calcsize(n->nclist.pattern);
7229 calcsize(n->nclist.next);
7230 break;
7231 case NDEFUN:
7232 case NARG:
7233 sizenodelist(n->narg.backquote);
7234 funcstringsize += strlen(n->narg.text) + 1;
7235 calcsize(n->narg.next);
7236 break;
7237 case NTO:
7238 case NCLOBBER:
7239 case NFROM:
7240 case NFROMTO:
7241 case NAPPEND:
7242 calcsize(n->nfile.fname);
7243 calcsize(n->nfile.next);
7244 break;
7245 case NTOFD:
7246 case NFROMFD:
7247 calcsize(n->ndup.vname);
7248 calcsize(n->ndup.next);
7249 break;
7250 case NHERE:
7251 case NXHERE:
7252 calcsize(n->nhere.doc);
7253 calcsize(n->nhere.next);
7254 break;
7255 case NNOT:
7256 calcsize(n->nnot.com);
7257 break;
7258 };
7259}
7260
7261static char *
7262nodeckstrdup(char *s)
7263{
7264 char *rtn = funcstring;
7265
7266 strcpy(funcstring, s);
7267 funcstring += strlen(s) + 1;
7268 return rtn;
7269}
7270
7271static union node *copynode(union node *);
7272
7273static struct nodelist *
7274copynodelist(struct nodelist *lp)
7275{
7276 struct nodelist *start;
7277 struct nodelist **lpp;
7278
7279 lpp = &start;
7280 while (lp) {
7281 *lpp = funcblock;
7282 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7283 (*lpp)->n = copynode(lp->n);
7284 lp = lp->next;
7285 lpp = &(*lpp)->next;
7286 }
7287 *lpp = NULL;
7288 return start;
7289}
7290
7291static union node *
7292copynode(union node *n)
7293{
7294 union node *new;
7295
7296 if (n == NULL)
7297 return NULL;
7298 new = funcblock;
7299 funcblock = (char *) funcblock + nodesize[n->type];
7300
7301 switch (n->type) {
7302 case NCMD:
7303 new->ncmd.redirect = copynode(n->ncmd.redirect);
7304 new->ncmd.args = copynode(n->ncmd.args);
7305 new->ncmd.assign = copynode(n->ncmd.assign);
7306 break;
7307 case NPIPE:
7308 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7309 new->npipe.backgnd = n->npipe.backgnd;
7310 break;
7311 case NREDIR:
7312 case NBACKGND:
7313 case NSUBSHELL:
7314 new->nredir.redirect = copynode(n->nredir.redirect);
7315 new->nredir.n = copynode(n->nredir.n);
7316 break;
7317 case NAND:
7318 case NOR:
7319 case NSEMI:
7320 case NWHILE:
7321 case NUNTIL:
7322 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7323 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7324 break;
7325 case NIF:
7326 new->nif.elsepart = copynode(n->nif.elsepart);
7327 new->nif.ifpart = copynode(n->nif.ifpart);
7328 new->nif.test = copynode(n->nif.test);
7329 break;
7330 case NFOR:
7331 new->nfor.var = nodeckstrdup(n->nfor.var);
7332 new->nfor.body = copynode(n->nfor.body);
7333 new->nfor.args = copynode(n->nfor.args);
7334 break;
7335 case NCASE:
7336 new->ncase.cases = copynode(n->ncase.cases);
7337 new->ncase.expr = copynode(n->ncase.expr);
7338 break;
7339 case NCLIST:
7340 new->nclist.body = copynode(n->nclist.body);
7341 new->nclist.pattern = copynode(n->nclist.pattern);
7342 new->nclist.next = copynode(n->nclist.next);
7343 break;
7344 case NDEFUN:
7345 case NARG:
7346 new->narg.backquote = copynodelist(n->narg.backquote);
7347 new->narg.text = nodeckstrdup(n->narg.text);
7348 new->narg.next = copynode(n->narg.next);
7349 break;
7350 case NTO:
7351 case NCLOBBER:
7352 case NFROM:
7353 case NFROMTO:
7354 case NAPPEND:
7355 new->nfile.fname = copynode(n->nfile.fname);
7356 new->nfile.fd = n->nfile.fd;
7357 new->nfile.next = copynode(n->nfile.next);
7358 break;
7359 case NTOFD:
7360 case NFROMFD:
7361 new->ndup.vname = copynode(n->ndup.vname);
7362 new->ndup.dupfd = n->ndup.dupfd;
7363 new->ndup.fd = n->ndup.fd;
7364 new->ndup.next = copynode(n->ndup.next);
7365 break;
7366 case NHERE:
7367 case NXHERE:
7368 new->nhere.doc = copynode(n->nhere.doc);
7369 new->nhere.fd = n->nhere.fd;
7370 new->nhere.next = copynode(n->nhere.next);
7371 break;
7372 case NNOT:
7373 new->nnot.com = copynode(n->nnot.com);
7374 break;
7375 };
7376 new->type = n->type;
7377 return new;
7378}
7379
7380/*
7381 * Make a copy of a parse tree.
7382 */
7383static struct funcnode *
7384copyfunc(union node *n)
7385{
7386 struct funcnode *f;
7387 size_t blocksize;
7388
7389 funcblocksize = offsetof(struct funcnode, n);
7390 funcstringsize = 0;
7391 calcsize(n);
7392 blocksize = funcblocksize;
7393 f = ckmalloc(blocksize + funcstringsize);
7394 funcblock = (char *) f + offsetof(struct funcnode, n);
7395 funcstring = (char *) f + blocksize;
7396 copynode(n);
7397 f->count = 0;
7398 return f;
7399}
7400
7401/*
7402 * Define a shell function.
7403 */
7404static void
7405defun(char *name, union node *func)
7406{
7407 struct cmdentry entry;
7408
7409 INT_OFF;
7410 entry.cmdtype = CMDFUNCTION;
7411 entry.u.func = copyfunc(func);
7412 addcmdentry(name, &entry);
7413 INT_ON;
7414}
7415
7416static int evalskip; /* set if we are skipping commands */
7417/* reasons for skipping commands (see comment on breakcmd routine) */
7418#define SKIPBREAK (1 << 0)
7419#define SKIPCONT (1 << 1)
7420#define SKIPFUNC (1 << 2)
7421#define SKIPFILE (1 << 3)
7422#define SKIPEVAL (1 << 4)
7423static int skipcount; /* number of levels to skip */
7424static int funcnest; /* depth of function calls */
7425
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007426/* forward decl way out to parsing code - dotrap needs it */
7427static int evalstring(char *s, int mask);
7428
7429/*
7430 * Called to execute a trap. Perhaps we should avoid entering new trap
7431 * handlers while we are executing a trap handler.
7432 */
7433static int
7434dotrap(void)
7435{
7436 char *p;
7437 char *q;
7438 int i;
7439 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007440 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007441
7442 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007443 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007444 xbarrier();
7445
7446 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7447 if (!*q)
7448 continue;
7449 *q = '\0';
7450
7451 p = trap[i + 1];
7452 if (!p)
7453 continue;
7454 skip = evalstring(p, SKIPEVAL);
7455 exitstatus = savestatus;
7456 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007457 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007458 }
7459
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007460 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007461}
7462
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007463/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007464static void evalloop(union node *, int);
7465static void evalfor(union node *, int);
7466static void evalcase(union node *, int);
7467static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007468static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007469static void evalpipe(union node *, int);
7470static void evalcommand(union node *, int);
7471static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007472static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007473
Eric Andersen62483552001-07-10 06:09:16 +00007474/*
Eric Andersenc470f442003-07-28 09:56:35 +00007475 * Evaluate a parse tree. The value is left in the global variable
7476 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007477 */
Eric Andersenc470f442003-07-28 09:56:35 +00007478static void
7479evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007480{
Eric Andersenc470f442003-07-28 09:56:35 +00007481 int checkexit = 0;
7482 void (*evalfn)(union node *, int);
7483 unsigned isor;
7484 int status;
7485 if (n == NULL) {
7486 TRACE(("evaltree(NULL) called\n"));
7487 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007488 }
Eric Andersenc470f442003-07-28 09:56:35 +00007489 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007490 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007491 switch (n->type) {
7492 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007493#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007494 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007495 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007496 break;
7497#endif
7498 case NNOT:
7499 evaltree(n->nnot.com, EV_TESTED);
7500 status = !exitstatus;
7501 goto setstatus;
7502 case NREDIR:
7503 expredir(n->nredir.redirect);
7504 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7505 if (!status) {
7506 evaltree(n->nredir.n, flags & EV_TESTED);
7507 status = exitstatus;
7508 }
7509 popredir(0);
7510 goto setstatus;
7511 case NCMD:
7512 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007513 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007514 if (eflag && !(flags & EV_TESTED))
7515 checkexit = ~0;
7516 goto calleval;
7517 case NFOR:
7518 evalfn = evalfor;
7519 goto calleval;
7520 case NWHILE:
7521 case NUNTIL:
7522 evalfn = evalloop;
7523 goto calleval;
7524 case NSUBSHELL:
7525 case NBACKGND:
7526 evalfn = evalsubshell;
7527 goto calleval;
7528 case NPIPE:
7529 evalfn = evalpipe;
7530 goto checkexit;
7531 case NCASE:
7532 evalfn = evalcase;
7533 goto calleval;
7534 case NAND:
7535 case NOR:
7536 case NSEMI:
7537#if NAND + 1 != NOR
7538#error NAND + 1 != NOR
7539#endif
7540#if NOR + 1 != NSEMI
7541#error NOR + 1 != NSEMI
7542#endif
7543 isor = n->type - NAND;
7544 evaltree(
7545 n->nbinary.ch1,
7546 (flags | ((isor >> 1) - 1)) & EV_TESTED
7547 );
7548 if (!exitstatus == isor)
7549 break;
7550 if (!evalskip) {
7551 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007552 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007553 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007554 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007555 evalfn(n, flags);
7556 break;
7557 }
7558 break;
7559 case NIF:
7560 evaltree(n->nif.test, EV_TESTED);
7561 if (evalskip)
7562 break;
7563 if (exitstatus == 0) {
7564 n = n->nif.ifpart;
7565 goto evaln;
7566 } else if (n->nif.elsepart) {
7567 n = n->nif.elsepart;
7568 goto evaln;
7569 }
7570 goto success;
7571 case NDEFUN:
7572 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007573 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007574 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007575 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007576 exitstatus = status;
7577 break;
7578 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007579 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007580 if ((checkexit & exitstatus))
7581 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007582 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007583 goto exexit;
7584
7585 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007586 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007587 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007588 }
Eric Andersen62483552001-07-10 06:09:16 +00007589}
7590
Eric Andersenc470f442003-07-28 09:56:35 +00007591#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7592static
7593#endif
7594void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7595
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007596static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007597
7598static void
7599evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007600{
7601 int status;
7602
7603 loopnest++;
7604 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007605 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007606 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007607 int i;
7608
Eric Andersencb57d552001-06-28 07:25:16 +00007609 evaltree(n->nbinary.ch1, EV_TESTED);
7610 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007611 skipping:
7612 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007613 evalskip = 0;
7614 continue;
7615 }
7616 if (evalskip == SKIPBREAK && --skipcount <= 0)
7617 evalskip = 0;
7618 break;
7619 }
Eric Andersenc470f442003-07-28 09:56:35 +00007620 i = exitstatus;
7621 if (n->type != NWHILE)
7622 i = !i;
7623 if (i != 0)
7624 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007625 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007626 status = exitstatus;
7627 if (evalskip)
7628 goto skipping;
7629 }
7630 loopnest--;
7631 exitstatus = status;
7632}
7633
Eric Andersenc470f442003-07-28 09:56:35 +00007634static void
7635evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007636{
7637 struct arglist arglist;
7638 union node *argp;
7639 struct strlist *sp;
7640 struct stackmark smark;
7641
7642 setstackmark(&smark);
7643 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007644 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007645 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007646 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007647 if (evalskip)
7648 goto out;
7649 }
7650 *arglist.lastp = NULL;
7651
7652 exitstatus = 0;
7653 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007654 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007655 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007656 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007657 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007658 if (evalskip) {
7659 if (evalskip == SKIPCONT && --skipcount <= 0) {
7660 evalskip = 0;
7661 continue;
7662 }
7663 if (evalskip == SKIPBREAK && --skipcount <= 0)
7664 evalskip = 0;
7665 break;
7666 }
7667 }
7668 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007669 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007670 popstackmark(&smark);
7671}
7672
Eric Andersenc470f442003-07-28 09:56:35 +00007673static void
7674evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007675{
7676 union node *cp;
7677 union node *patp;
7678 struct arglist arglist;
7679 struct stackmark smark;
7680
7681 setstackmark(&smark);
7682 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007683 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007684 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007685 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7686 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007687 if (casematch(patp, arglist.list->text)) {
7688 if (evalskip == 0) {
7689 evaltree(cp->nclist.body, flags);
7690 }
7691 goto out;
7692 }
7693 }
7694 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007695 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007696 popstackmark(&smark);
7697}
7698
Eric Andersenc470f442003-07-28 09:56:35 +00007699/*
7700 * Kick off a subshell to evaluate a tree.
7701 */
Eric Andersenc470f442003-07-28 09:56:35 +00007702static void
7703evalsubshell(union node *n, int flags)
7704{
7705 struct job *jp;
7706 int backgnd = (n->type == NBACKGND);
7707 int status;
7708
7709 expredir(n->nredir.redirect);
7710 if (!backgnd && flags & EV_EXIT && !trap[0])
7711 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007712 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007713 jp = makejob(n, 1);
7714 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007715 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007716 flags |= EV_EXIT;
7717 if (backgnd)
7718 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007719 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007720 redirect(n->nredir.redirect, 0);
7721 evaltreenr(n->nredir.n, flags);
7722 /* never returns */
7723 }
7724 status = 0;
7725 if (! backgnd)
7726 status = waitforjob(jp);
7727 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007728 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007729}
7730
Eric Andersenc470f442003-07-28 09:56:35 +00007731/*
7732 * Compute the names of the files in a redirection list.
7733 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007734static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007735static void
7736expredir(union node *n)
7737{
7738 union node *redir;
7739
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007740 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007741 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007742
7743 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007744 fn.lastp = &fn.list;
7745 switch (redir->type) {
7746 case NFROMTO:
7747 case NFROM:
7748 case NTO:
7749 case NCLOBBER:
7750 case NAPPEND:
7751 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7752 redir->nfile.expfname = fn.list->text;
7753 break;
7754 case NFROMFD:
7755 case NTOFD:
7756 if (redir->ndup.vname) {
7757 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007758 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007759 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007760 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007761 }
7762 break;
7763 }
7764 }
7765}
7766
Eric Andersencb57d552001-06-28 07:25:16 +00007767/*
Eric Andersencb57d552001-06-28 07:25:16 +00007768 * Evaluate a pipeline. All the processes in the pipeline are children
7769 * of the process creating the pipeline. (This differs from some versions
7770 * of the shell, which make the last process in a pipeline the parent
7771 * of all the rest.)
7772 */
Eric Andersenc470f442003-07-28 09:56:35 +00007773static void
7774evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007775{
7776 struct job *jp;
7777 struct nodelist *lp;
7778 int pipelen;
7779 int prevfd;
7780 int pip[2];
7781
Eric Andersenc470f442003-07-28 09:56:35 +00007782 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007783 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007784 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007785 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007786 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007787 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007788 jp = makejob(n, pipelen);
7789 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007790 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007791 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007792 pip[1] = -1;
7793 if (lp->next) {
7794 if (pipe(pip) < 0) {
7795 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007796 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007797 }
7798 }
7799 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007800 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007801 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007802 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007803 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007804 if (prevfd > 0) {
7805 dup2(prevfd, 0);
7806 close(prevfd);
7807 }
7808 if (pip[1] > 1) {
7809 dup2(pip[1], 1);
7810 close(pip[1]);
7811 }
Eric Andersenc470f442003-07-28 09:56:35 +00007812 evaltreenr(lp->n, flags);
7813 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007814 }
7815 if (prevfd >= 0)
7816 close(prevfd);
7817 prevfd = pip[0];
7818 close(pip[1]);
7819 }
Eric Andersencb57d552001-06-28 07:25:16 +00007820 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007821 exitstatus = waitforjob(jp);
7822 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007823 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007824 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007825}
7826
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007827/*
7828 * Controls whether the shell is interactive or not.
7829 */
7830static void
7831setinteractive(int on)
7832{
7833 static int is_interactive;
7834
7835 if (++on == is_interactive)
7836 return;
7837 is_interactive = on;
7838 setsignal(SIGINT);
7839 setsignal(SIGQUIT);
7840 setsignal(SIGTERM);
7841#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7842 if (is_interactive > 1) {
7843 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007844 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007845
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007846 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007847 out1fmt(
7848 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007849 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007850 "Enter 'help' for a list of built-in commands."
7851 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007852 bb_banner);
7853 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007854 }
7855 }
7856#endif
7857}
7858
7859#if ENABLE_FEATURE_EDITING_VI
7860#define setvimode(on) do { \
7861 if (on) line_input_state->flags |= VI_MODE; \
7862 else line_input_state->flags &= ~VI_MODE; \
7863} while (0)
7864#else
7865#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7866#endif
7867
7868static void
7869optschanged(void)
7870{
7871#if DEBUG
7872 opentrace();
7873#endif
7874 setinteractive(iflag);
7875 setjobctl(mflag);
7876 setvimode(viflag);
7877}
7878
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007879static struct localvar *localvars;
7880
7881/*
7882 * Called after a function returns.
7883 * Interrupts must be off.
7884 */
7885static void
7886poplocalvars(void)
7887{
7888 struct localvar *lvp;
7889 struct var *vp;
7890
7891 while ((lvp = localvars) != NULL) {
7892 localvars = lvp->next;
7893 vp = lvp->vp;
7894 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7895 if (vp == NULL) { /* $- saved */
7896 memcpy(optlist, lvp->text, sizeof(optlist));
7897 free((char*)lvp->text);
7898 optschanged();
7899 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7900 unsetvar(vp->text);
7901 } else {
7902 if (vp->func)
7903 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7904 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7905 free((char*)vp->text);
7906 vp->flags = lvp->flags;
7907 vp->text = lvp->text;
7908 }
7909 free(lvp);
7910 }
7911}
7912
7913static int
7914evalfun(struct funcnode *func, int argc, char **argv, int flags)
7915{
7916 volatile struct shparam saveparam;
7917 struct localvar *volatile savelocalvars;
7918 struct jmploc *volatile savehandler;
7919 struct jmploc jmploc;
7920 int e;
7921
7922 saveparam = shellparam;
7923 savelocalvars = localvars;
7924 e = setjmp(jmploc.loc);
7925 if (e) {
7926 goto funcdone;
7927 }
7928 INT_OFF;
7929 savehandler = exception_handler;
7930 exception_handler = &jmploc;
7931 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007932 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007933 func->count++;
7934 funcnest++;
7935 INT_ON;
7936 shellparam.nparam = argc - 1;
7937 shellparam.p = argv + 1;
7938#if ENABLE_ASH_GETOPTS
7939 shellparam.optind = 1;
7940 shellparam.optoff = -1;
7941#endif
7942 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007943 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007944 INT_OFF;
7945 funcnest--;
7946 freefunc(func);
7947 poplocalvars();
7948 localvars = savelocalvars;
7949 freeparam(&shellparam);
7950 shellparam = saveparam;
7951 exception_handler = savehandler;
7952 INT_ON;
7953 evalskip &= ~SKIPFUNC;
7954 return e;
7955}
7956
Denis Vlasenko131ae172007-02-18 13:00:19 +00007957#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007958static char **
7959parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007960{
7961 char *cp, c;
7962
7963 for (;;) {
7964 cp = *++argv;
7965 if (!cp)
7966 return 0;
7967 if (*cp++ != '-')
7968 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007969 c = *cp++;
7970 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007971 break;
7972 if (c == '-' && !*cp) {
7973 argv++;
7974 break;
7975 }
7976 do {
7977 switch (c) {
7978 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007979 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007980 break;
7981 default:
7982 /* run 'typecmd' for other options */
7983 return 0;
7984 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007985 c = *cp++;
7986 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 }
7988 return argv;
7989}
7990#endif
7991
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007992/*
7993 * Make a variable a local variable. When a variable is made local, it's
7994 * value and flags are saved in a localvar structure. The saved values
7995 * will be restored when the shell function returns. We handle the name
7996 * "-" as a special case.
7997 */
7998static void
7999mklocal(char *name)
8000{
8001 struct localvar *lvp;
8002 struct var **vpp;
8003 struct var *vp;
8004
8005 INT_OFF;
8006 lvp = ckmalloc(sizeof(struct localvar));
8007 if (LONE_DASH(name)) {
8008 char *p;
8009 p = ckmalloc(sizeof(optlist));
8010 lvp->text = memcpy(p, optlist, sizeof(optlist));
8011 vp = NULL;
8012 } else {
8013 char *eq;
8014
8015 vpp = hashvar(name);
8016 vp = *findvar(vpp, name);
8017 eq = strchr(name, '=');
8018 if (vp == NULL) {
8019 if (eq)
8020 setvareq(name, VSTRFIXED);
8021 else
8022 setvar(name, NULL, VSTRFIXED);
8023 vp = *vpp; /* the new variable */
8024 lvp->flags = VUNSET;
8025 } else {
8026 lvp->text = vp->text;
8027 lvp->flags = vp->flags;
8028 vp->flags |= VSTRFIXED|VTEXTFIXED;
8029 if (eq)
8030 setvareq(name, 0);
8031 }
8032 }
8033 lvp->vp = vp;
8034 lvp->next = localvars;
8035 localvars = lvp;
8036 INT_ON;
8037}
8038
8039/*
8040 * The "local" command.
8041 */
8042static int
8043localcmd(int argc, char **argv)
8044{
8045 char *name;
8046
8047 argv = argptr;
8048 while ((name = *argv++) != NULL) {
8049 mklocal(name);
8050 }
8051 return 0;
8052}
8053
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008054static int
8055falsecmd(int argc, char **argv)
8056{
8057 return 1;
8058}
8059
8060static int
8061truecmd(int argc, char **argv)
8062{
8063 return 0;
8064}
8065
8066static int
8067execcmd(int argc, char **argv)
8068{
8069 if (argc > 1) {
8070 iflag = 0; /* exit on error */
8071 mflag = 0;
8072 optschanged();
8073 shellexec(argv + 1, pathval(), 0);
8074 }
8075 return 0;
8076}
8077
8078/*
8079 * The return command.
8080 */
8081static int
8082returncmd(int argc, char **argv)
8083{
8084 /*
8085 * If called outside a function, do what ksh does;
8086 * skip the rest of the file.
8087 */
8088 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8089 return argv[1] ? number(argv[1]) : exitstatus;
8090}
8091
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008092/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008093static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008094static int dotcmd(int, char **);
8095static int evalcmd(int, char **);
8096#if ENABLE_ASH_BUILTIN_ECHO
8097static int echocmd(int, char **);
8098#endif
8099#if ENABLE_ASH_BUILTIN_TEST
8100static int testcmd(int, char **);
8101#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008102static int exitcmd(int, char **);
8103static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008104#if ENABLE_ASH_GETOPTS
8105static int getoptscmd(int, char **);
8106#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008107#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8108static int helpcmd(int argc, char **argv);
8109#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008110#if ENABLE_ASH_MATH_SUPPORT
8111static int letcmd(int, char **);
8112#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008113static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008114static int setcmd(int, char **);
8115static int shiftcmd(int, char **);
8116static int timescmd(int, char **);
8117static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008118static int umaskcmd(int, char **);
8119static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008120static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008121
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008122#define BUILTIN_NOSPEC "0"
8123#define BUILTIN_SPECIAL "1"
8124#define BUILTIN_REGULAR "2"
8125#define BUILTIN_SPEC_REG "3"
8126#define BUILTIN_ASSIGN "4"
8127#define BUILTIN_SPEC_ASSG "5"
8128#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008129#define BUILTIN_SPEC_REG_ASSG "7"
8130
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008131/* make sure to keep these in proper order since it is searched via bsearch() */
8132static const struct builtincmd builtintab[] = {
8133 { BUILTIN_SPEC_REG ".", dotcmd },
8134 { BUILTIN_SPEC_REG ":", truecmd },
8135#if ENABLE_ASH_BUILTIN_TEST
8136 { BUILTIN_REGULAR "[", testcmd },
8137 { BUILTIN_REGULAR "[[", testcmd },
8138#endif
8139#if ENABLE_ASH_ALIAS
8140 { BUILTIN_REG_ASSG "alias", aliascmd },
8141#endif
8142#if JOBS
8143 { BUILTIN_REGULAR "bg", fg_bgcmd },
8144#endif
8145 { BUILTIN_SPEC_REG "break", breakcmd },
8146 { BUILTIN_REGULAR "cd", cdcmd },
8147 { BUILTIN_NOSPEC "chdir", cdcmd },
8148#if ENABLE_ASH_CMDCMD
8149 { BUILTIN_REGULAR "command", commandcmd },
8150#endif
8151 { BUILTIN_SPEC_REG "continue", breakcmd },
8152#if ENABLE_ASH_BUILTIN_ECHO
8153 { BUILTIN_REGULAR "echo", echocmd },
8154#endif
8155 { BUILTIN_SPEC_REG "eval", evalcmd },
8156 { BUILTIN_SPEC_REG "exec", execcmd },
8157 { BUILTIN_SPEC_REG "exit", exitcmd },
8158 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8159 { BUILTIN_REGULAR "false", falsecmd },
8160#if JOBS
8161 { BUILTIN_REGULAR "fg", fg_bgcmd },
8162#endif
8163#if ENABLE_ASH_GETOPTS
8164 { BUILTIN_REGULAR "getopts", getoptscmd },
8165#endif
8166 { BUILTIN_NOSPEC "hash", hashcmd },
8167#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8168 { BUILTIN_NOSPEC "help", helpcmd },
8169#endif
8170#if JOBS
8171 { BUILTIN_REGULAR "jobs", jobscmd },
8172 { BUILTIN_REGULAR "kill", killcmd },
8173#endif
8174#if ENABLE_ASH_MATH_SUPPORT
8175 { BUILTIN_NOSPEC "let", letcmd },
8176#endif
8177 { BUILTIN_ASSIGN "local", localcmd },
8178 { BUILTIN_NOSPEC "pwd", pwdcmd },
8179 { BUILTIN_REGULAR "read", readcmd },
8180 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8181 { BUILTIN_SPEC_REG "return", returncmd },
8182 { BUILTIN_SPEC_REG "set", setcmd },
8183 { BUILTIN_SPEC_REG "shift", shiftcmd },
8184 { BUILTIN_SPEC_REG "source", dotcmd },
8185#if ENABLE_ASH_BUILTIN_TEST
8186 { BUILTIN_REGULAR "test", testcmd },
8187#endif
8188 { BUILTIN_SPEC_REG "times", timescmd },
8189 { BUILTIN_SPEC_REG "trap", trapcmd },
8190 { BUILTIN_REGULAR "true", truecmd },
8191 { BUILTIN_NOSPEC "type", typecmd },
8192 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8193 { BUILTIN_REGULAR "umask", umaskcmd },
8194#if ENABLE_ASH_ALIAS
8195 { BUILTIN_REGULAR "unalias", unaliascmd },
8196#endif
8197 { BUILTIN_SPEC_REG "unset", unsetcmd },
8198 { BUILTIN_REGULAR "wait", waitcmd },
8199};
8200
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008201
8202#define COMMANDCMD (builtintab + 5 + \
8203 2 * ENABLE_ASH_BUILTIN_TEST + \
8204 ENABLE_ASH_ALIAS + \
8205 ENABLE_ASH_JOB_CONTROL)
8206#define EXECCMD (builtintab + 7 + \
8207 2 * ENABLE_ASH_BUILTIN_TEST + \
8208 ENABLE_ASH_ALIAS + \
8209 ENABLE_ASH_JOB_CONTROL + \
8210 ENABLE_ASH_CMDCMD + \
8211 ENABLE_ASH_BUILTIN_ECHO)
8212
8213/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008214 * Search the table of builtin commands.
8215 */
8216static struct builtincmd *
8217find_builtin(const char *name)
8218{
8219 struct builtincmd *bp;
8220
8221 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008222 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008223 pstrcmp
8224 );
8225 return bp;
8226}
8227
8228/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008229 * Execute a simple command.
8230 */
8231static int back_exitstatus; /* exit status of backquoted command */
8232static int
8233isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008234{
8235 const char *q = endofname(p);
8236 if (p == q)
8237 return 0;
8238 return *q == '=';
8239}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008240static int
8241bltincmd(int argc, char **argv)
8242{
8243 /* Preserve exitstatus of a previous possible redirection
8244 * as POSIX mandates */
8245 return back_exitstatus;
8246}
Eric Andersenc470f442003-07-28 09:56:35 +00008247static void
8248evalcommand(union node *cmd, int flags)
8249{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008250 static const struct builtincmd bltin = {
8251 "\0\0", bltincmd
8252 };
Eric Andersenc470f442003-07-28 09:56:35 +00008253 struct stackmark smark;
8254 union node *argp;
8255 struct arglist arglist;
8256 struct arglist varlist;
8257 char **argv;
8258 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008259 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008260 struct cmdentry cmdentry;
8261 struct job *jp;
8262 char *lastarg;
8263 const char *path;
8264 int spclbltin;
8265 int cmd_is_exec;
8266 int status;
8267 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008268 struct builtincmd *bcmd;
8269 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008270
8271 /* First expand the arguments. */
8272 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8273 setstackmark(&smark);
8274 back_exitstatus = 0;
8275
8276 cmdentry.cmdtype = CMDBUILTIN;
8277 cmdentry.u.cmd = &bltin;
8278 varlist.lastp = &varlist.list;
8279 *varlist.lastp = NULL;
8280 arglist.lastp = &arglist.list;
8281 *arglist.lastp = NULL;
8282
8283 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008284 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008285 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8286 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8287 }
8288
Eric Andersenc470f442003-07-28 09:56:35 +00008289 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8290 struct strlist **spp;
8291
8292 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008293 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008294 expandarg(argp, &arglist, EXP_VARTILDE);
8295 else
8296 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8297
Eric Andersenc470f442003-07-28 09:56:35 +00008298 for (sp = *spp; sp; sp = sp->next)
8299 argc++;
8300 }
8301
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008302 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008303 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008304 TRACE(("evalcommand arg: %s\n", sp->text));
8305 *nargv++ = sp->text;
8306 }
8307 *nargv = NULL;
8308
8309 lastarg = NULL;
8310 if (iflag && funcnest == 0 && argc > 0)
8311 lastarg = nargv[-1];
8312
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008313 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008314 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008315 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008316
8317 path = vpath.text;
8318 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8319 struct strlist **spp;
8320 char *p;
8321
8322 spp = varlist.lastp;
8323 expandarg(argp, &varlist, EXP_VARTILDE);
8324
8325 /*
8326 * Modify the command lookup path, if a PATH= assignment
8327 * is present
8328 */
8329 p = (*spp)->text;
8330 if (varequal(p, path))
8331 path = p;
8332 }
8333
8334 /* Print the command if xflag is set. */
8335 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008336 int n;
8337 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008338
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008339 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008340 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008341
8342 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008343 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008344 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008345 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008346 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008347 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008348 p--;
8349 }
8350 }
8351 sp = arglist.list;
8352 }
Rob Landley53437472006-07-16 08:14:35 +00008353 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008354 }
8355
8356 cmd_is_exec = 0;
8357 spclbltin = -1;
8358
8359 /* Now locate the command. */
8360 if (argc) {
8361 const char *oldpath;
8362 int cmd_flag = DO_ERR;
8363
8364 path += 5;
8365 oldpath = path;
8366 for (;;) {
8367 find_command(argv[0], &cmdentry, cmd_flag, path);
8368 if (cmdentry.cmdtype == CMDUNKNOWN) {
8369 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008370 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008371 goto bail;
8372 }
8373
8374 /* implement bltin and command here */
8375 if (cmdentry.cmdtype != CMDBUILTIN)
8376 break;
8377 if (spclbltin < 0)
8378 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8379 if (cmdentry.u.cmd == EXECCMD)
8380 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008381#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008382 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008383 path = oldpath;
8384 nargv = parse_command_args(argv, &path);
8385 if (!nargv)
8386 break;
8387 argc -= nargv - argv;
8388 argv = nargv;
8389 cmd_flag |= DO_NOFUNC;
8390 } else
8391#endif
8392 break;
8393 }
8394 }
8395
8396 if (status) {
8397 /* We have a redirection error. */
8398 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008399 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008400 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008401 exitstatus = status;
8402 goto out;
8403 }
8404
8405 /* Execute the command. */
8406 switch (cmdentry.cmdtype) {
8407 default:
8408 /* Fork off a child process if necessary. */
8409 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008410 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008411 jp = makejob(cmd, 1);
8412 if (forkshell(jp, cmd, FORK_FG) != 0) {
8413 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008414 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008415 break;
8416 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008417 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008418 }
8419 listsetvar(varlist.list, VEXPORT|VSTACK);
8420 shellexec(argv, path, cmdentry.u.index);
8421 /* NOTREACHED */
8422
8423 case CMDBUILTIN:
8424 cmdenviron = varlist.list;
8425 if (cmdenviron) {
8426 struct strlist *list = cmdenviron;
8427 int i = VNOSET;
8428 if (spclbltin > 0 || argc == 0) {
8429 i = 0;
8430 if (cmd_is_exec && argc > 1)
8431 i = VEXPORT;
8432 }
8433 listsetvar(list, i);
8434 }
8435 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8436 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008437 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008438 if (i == EXEXIT)
8439 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008440 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008441 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008442 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008443 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008444 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008445 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008446 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008447 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008448 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008449 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008450 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008451 }
8452 break;
8453
8454 case CMDFUNCTION:
8455 listsetvar(varlist.list, 0);
8456 if (evalfun(cmdentry.u.func, argc, argv, flags))
8457 goto raise;
8458 break;
8459 }
8460
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008461 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008462 popredir(cmd_is_exec);
8463 if (lastarg)
8464 /* dsl: I think this is intended to be used to support
8465 * '_' in 'vi' command mode during line editing...
8466 * However I implemented that within libedit itself.
8467 */
8468 setvar("_", lastarg, 0);
8469 popstackmark(&smark);
8470}
8471
8472static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008473evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8474{
Eric Andersenc470f442003-07-28 09:56:35 +00008475 char *volatile savecmdname;
8476 struct jmploc *volatile savehandler;
8477 struct jmploc jmploc;
8478 int i;
8479
8480 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008481 i = setjmp(jmploc.loc);
8482 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008483 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008484 savehandler = exception_handler;
8485 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008486 commandname = argv[0];
8487 argptr = argv + 1;
8488 optptr = NULL; /* initialize nextopt */
8489 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008490 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008491 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008492 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008493 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008494 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008495// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008496 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008497
8498 return i;
8499}
8500
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008501static int
8502goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008503{
8504 return !*endofname(p);
8505}
8506
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008507
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008508/*
8509 * Search for a command. This is called before we fork so that the
8510 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008511 * the child. The check for "goodname" is an overly conservative
8512 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008513 */
Eric Andersenc470f442003-07-28 09:56:35 +00008514static void
8515prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008516{
8517 struct cmdentry entry;
8518
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008519 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8520 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008521}
8522
Eric Andersencb57d552001-06-28 07:25:16 +00008523
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008524/* ============ Builtin commands
8525 *
8526 * Builtin commands whose functions are closely tied to evaluation
8527 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008528 */
8529
8530/*
Eric Andersencb57d552001-06-28 07:25:16 +00008531 * Handle break and continue commands. Break, continue, and return are
8532 * all handled by setting the evalskip flag. The evaluation routines
8533 * above all check this flag, and if it is set they start skipping
8534 * commands rather than executing them. The variable skipcount is
8535 * the number of loops to break/continue, or the number of function
8536 * levels to return. (The latter is always 1.) It should probably
8537 * be an error to break out of more loops than exist, but it isn't
8538 * in the standard shell so we don't make it one here.
8539 */
Eric Andersenc470f442003-07-28 09:56:35 +00008540static int
8541breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008542{
8543 int n = argc > 1 ? number(argv[1]) : 1;
8544
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008545 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008546 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008547 if (n > loopnest)
8548 n = loopnest;
8549 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008550 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008551 skipcount = n;
8552 }
8553 return 0;
8554}
8555
Eric Andersenc470f442003-07-28 09:56:35 +00008556
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008557/* ============ input.c
8558 *
Eric Andersen90898442003-08-06 11:20:52 +00008559 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008560 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008561
Eric Andersenc470f442003-07-28 09:56:35 +00008562#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008563
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008564enum {
8565 INPUT_PUSH_FILE = 1,
8566 INPUT_NOFILE_OK = 2,
8567};
Eric Andersencb57d552001-06-28 07:25:16 +00008568
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008569static int plinno = 1; /* input line number */
8570/* number of characters left in input buffer */
8571static int parsenleft; /* copy of parsefile->nleft */
8572static int parselleft; /* copy of parsefile->lleft */
8573/* next character in input buffer */
8574static char *parsenextc; /* copy of parsefile->nextc */
8575
8576static int checkkwd;
8577/* values of checkkwd variable */
8578#define CHKALIAS 0x1
8579#define CHKKWD 0x2
8580#define CHKNL 0x4
8581
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008582static void
8583popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008584{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008585 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008586
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008587 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008588#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589 if (sp->ap) {
8590 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8591 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008592 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008593 if (sp->string != sp->ap->val) {
8594 free(sp->string);
8595 }
8596 sp->ap->flag &= ~ALIASINUSE;
8597 if (sp->ap->flag & ALIASDEAD) {
8598 unalias(sp->ap->name);
8599 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008600 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008601#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008602 parsenextc = sp->prevstring;
8603 parsenleft = sp->prevnleft;
8604/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8605 parsefile->strpush = sp->prev;
8606 if (sp != &(parsefile->basestrpush))
8607 free(sp);
8608 INT_ON;
8609}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008610
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008611static int
8612preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008613{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008614 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008615 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008616 parsenextc = buf;
8617
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008618 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008619#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008620 if (!iflag || parsefile->fd)
8621 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8622 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008623#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008624 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008625#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008626 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8627 if (nr == 0) {
8628 /* Ctrl+C pressed */
8629 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008630 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008631 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008632 raise(SIGINT);
8633 return 1;
8634 }
Eric Andersenc470f442003-07-28 09:56:35 +00008635 goto retry;
8636 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008637 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008638 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008639 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008640 }
Eric Andersencb57d552001-06-28 07:25:16 +00008641 }
8642#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008643 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008644#endif
8645
8646 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008647 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008648 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008649 if (flags >= 0 && (flags & O_NONBLOCK)) {
8650 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008651 if (fcntl(0, F_SETFL, flags) >= 0) {
8652 out2str("sh: turning off NDELAY mode\n");
8653 goto retry;
8654 }
8655 }
8656 }
8657 }
8658 return nr;
8659}
8660
8661/*
8662 * Refill the input buffer and return the next input character:
8663 *
8664 * 1) If a string was pushed back on the input, pop it;
8665 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8666 * from a string so we can't refill the buffer, return EOF.
8667 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8668 * 4) Process input up to the next newline, deleting nul characters.
8669 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008670static int
Eric Andersenc470f442003-07-28 09:56:35 +00008671preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008672{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008673 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008674 int more;
8675 char savec;
8676
8677 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008678#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008679 if (parsenleft == -1 && parsefile->strpush->ap &&
8680 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008681 return PEOA;
8682 }
Eric Andersen2870d962001-07-02 17:27:21 +00008683#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008684 popstring();
8685 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008686 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008687 }
8688 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8689 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008690 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008691
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008692 more = parselleft;
8693 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008694 again:
8695 more = preadfd();
8696 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008697 parselleft = parsenleft = EOF_NLEFT;
8698 return PEOF;
8699 }
8700 }
8701
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008702 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008703
8704 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008705 for (;;) {
8706 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008707
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008708 more--;
8709 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008710
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008711 if (!c)
8712 memmove(q, q + 1, more);
8713 else {
8714 q++;
8715 if (c == '\n') {
8716 parsenleft = q - parsenextc - 1;
8717 break;
8718 }
Eric Andersencb57d552001-06-28 07:25:16 +00008719 }
8720
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008721 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008722 parsenleft = q - parsenextc - 1;
8723 if (parsenleft < 0)
8724 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008725 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008726 }
8727 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008728 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008729
8730 savec = *q;
8731 *q = '\0';
8732
8733 if (vflag) {
8734 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008735 }
8736
8737 *q = savec;
8738
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008739 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008740}
8741
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008742#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008743static int
8744pgetc(void)
8745{
8746 return pgetc_as_macro();
8747}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008748
8749#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8750#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008751#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008752#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008753#endif
8754
8755/*
8756 * Same as pgetc(), but ignores PEOA.
8757 */
8758#if ENABLE_ASH_ALIAS
8759static int
8760pgetc2(void)
8761{
8762 int c;
8763
8764 do {
8765 c = pgetc_macro();
8766 } while (c == PEOA);
8767 return c;
8768}
8769#else
8770static int
8771pgetc2(void)
8772{
8773 return pgetc_macro();
8774}
8775#endif
8776
8777/*
8778 * Read a line from the script.
8779 */
8780static char *
8781pfgets(char *line, int len)
8782{
8783 char *p = line;
8784 int nleft = len;
8785 int c;
8786
8787 while (--nleft > 0) {
8788 c = pgetc2();
8789 if (c == PEOF) {
8790 if (p == line)
8791 return NULL;
8792 break;
8793 }
8794 *p++ = c;
8795 if (c == '\n')
8796 break;
8797 }
8798 *p = '\0';
8799 return line;
8800}
8801
Eric Andersenc470f442003-07-28 09:56:35 +00008802/*
8803 * Undo the last call to pgetc. Only one character may be pushed back.
8804 * PEOF may be pushed back.
8805 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008806static void
Eric Andersenc470f442003-07-28 09:56:35 +00008807pungetc(void)
8808{
8809 parsenleft++;
8810 parsenextc--;
8811}
Eric Andersencb57d552001-06-28 07:25:16 +00008812
8813/*
8814 * Push a string back onto the input at this current parsefile level.
8815 * We handle aliases this way.
8816 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008817static void
Eric Andersenc470f442003-07-28 09:56:35 +00008818pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008819{
Eric Andersencb57d552001-06-28 07:25:16 +00008820 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008821 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008822
Eric Andersenc470f442003-07-28 09:56:35 +00008823 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008824 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008825/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8826 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008827 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008828 sp->prev = parsefile->strpush;
8829 parsefile->strpush = sp;
8830 } else
8831 sp = parsefile->strpush = &(parsefile->basestrpush);
8832 sp->prevstring = parsenextc;
8833 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008834#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008835 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008836 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008837 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008838 sp->string = s;
8839 }
Eric Andersen2870d962001-07-02 17:27:21 +00008840#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008841 parsenextc = s;
8842 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008843 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008844}
8845
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008846/*
8847 * To handle the "." command, a stack of input files is used. Pushfile
8848 * adds a new entry to the stack and popfile restores the previous level.
8849 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008850static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008851pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008852{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008853 struct parsefile *pf;
8854
8855 parsefile->nleft = parsenleft;
8856 parsefile->lleft = parselleft;
8857 parsefile->nextc = parsenextc;
8858 parsefile->linno = plinno;
8859 pf = ckmalloc(sizeof(*pf));
8860 pf->prev = parsefile;
8861 pf->fd = -1;
8862 pf->strpush = NULL;
8863 pf->basestrpush.prev = NULL;
8864 parsefile = pf;
8865}
8866
8867static void
8868popfile(void)
8869{
8870 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008871
Denis Vlasenkob012b102007-02-19 22:43:01 +00008872 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008873 if (pf->fd >= 0)
8874 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008875 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008876 while (pf->strpush)
8877 popstring();
8878 parsefile = pf->prev;
8879 free(pf);
8880 parsenleft = parsefile->nleft;
8881 parselleft = parsefile->lleft;
8882 parsenextc = parsefile->nextc;
8883 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008884 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008885}
8886
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008887/*
8888 * Return to top level.
8889 */
8890static void
8891popallfiles(void)
8892{
8893 while (parsefile != &basepf)
8894 popfile();
8895}
8896
8897/*
8898 * Close the file(s) that the shell is reading commands from. Called
8899 * after a fork is done.
8900 */
8901static void
8902closescript(void)
8903{
8904 popallfiles();
8905 if (parsefile->fd > 0) {
8906 close(parsefile->fd);
8907 parsefile->fd = 0;
8908 }
8909}
8910
8911/*
8912 * Like setinputfile, but takes an open file descriptor. Call this with
8913 * interrupts off.
8914 */
8915static void
8916setinputfd(int fd, int push)
8917{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008918 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008919 if (push) {
8920 pushfile();
8921 parsefile->buf = 0;
8922 }
8923 parsefile->fd = fd;
8924 if (parsefile->buf == NULL)
8925 parsefile->buf = ckmalloc(IBUFSIZ);
8926 parselleft = parsenleft = 0;
8927 plinno = 1;
8928}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008929
Eric Andersenc470f442003-07-28 09:56:35 +00008930/*
8931 * Set the input to take input from a file. If push is set, push the
8932 * old input onto the stack first.
8933 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008934static int
8935setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008936{
8937 int fd;
8938 int fd2;
8939
Denis Vlasenkob012b102007-02-19 22:43:01 +00008940 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008941 fd = open(fname, O_RDONLY);
8942 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008943 if (flags & INPUT_NOFILE_OK)
8944 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008945 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008946 }
Eric Andersenc470f442003-07-28 09:56:35 +00008947 if (fd < 10) {
8948 fd2 = copyfd(fd, 10);
8949 close(fd);
8950 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008951 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008952 fd = fd2;
8953 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008954 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008955 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008956 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008957 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008958}
8959
Eric Andersencb57d552001-06-28 07:25:16 +00008960/*
8961 * Like setinputfile, but takes input from a string.
8962 */
Eric Andersenc470f442003-07-28 09:56:35 +00008963static void
8964setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008965{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008966 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008967 pushfile();
8968 parsenextc = string;
8969 parsenleft = strlen(string);
8970 parsefile->buf = NULL;
8971 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008972 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008973}
8974
8975
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008976/* ============ mail.c
8977 *
8978 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008979 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008980
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008981#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008982
Eric Andersencb57d552001-06-28 07:25:16 +00008983#define MAXMBOXES 10
8984
Eric Andersenc470f442003-07-28 09:56:35 +00008985/* times of mailboxes */
8986static time_t mailtime[MAXMBOXES];
8987/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008988static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008989
Eric Andersencb57d552001-06-28 07:25:16 +00008990/*
Eric Andersenc470f442003-07-28 09:56:35 +00008991 * Print appropriate message(s) if mail has arrived.
8992 * If mail_var_path_changed is set,
8993 * then the value of MAIL has mail_var_path_changed,
8994 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008995 */
Eric Andersenc470f442003-07-28 09:56:35 +00008996static void
8997chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008998{
Eric Andersencb57d552001-06-28 07:25:16 +00008999 const char *mpath;
9000 char *p;
9001 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009002 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009003 struct stackmark smark;
9004 struct stat statb;
9005
Eric Andersencb57d552001-06-28 07:25:16 +00009006 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009007 mpath = mpathset() ? mpathval() : mailval();
9008 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009009 p = padvance(&mpath, nullstr);
9010 if (p == NULL)
9011 break;
9012 if (*p == '\0')
9013 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009014 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009015#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009016 if (q[-1] != '/')
9017 abort();
9018#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009019 q[-1] = '\0'; /* delete trailing '/' */
9020 if (stat(p, &statb) < 0) {
9021 *mtp = 0;
9022 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009023 }
Eric Andersenc470f442003-07-28 09:56:35 +00009024 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9025 fprintf(
9026 stderr, snlfmt,
9027 pathopt ? pathopt : "you have mail"
9028 );
9029 }
9030 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009031 }
Eric Andersenc470f442003-07-28 09:56:35 +00009032 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009033 popstackmark(&smark);
9034}
Eric Andersencb57d552001-06-28 07:25:16 +00009035
Eric Andersenc470f442003-07-28 09:56:35 +00009036static void
9037changemail(const char *val)
9038{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009039 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009040}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009041
Denis Vlasenko131ae172007-02-18 13:00:19 +00009042#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009043
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009044
9045/* ============ ??? */
9046
Eric Andersencb57d552001-06-28 07:25:16 +00009047/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009048 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009049 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009050static void
9051setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009052{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009053 char **newparam;
9054 char **ap;
9055 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009056
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009057 for (nparam = 0; argv[nparam]; nparam++);
9058 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9059 while (*argv) {
9060 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009061 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009062 *ap = NULL;
9063 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009064 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009065 shellparam.nparam = nparam;
9066 shellparam.p = newparam;
9067#if ENABLE_ASH_GETOPTS
9068 shellparam.optind = 1;
9069 shellparam.optoff = -1;
9070#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009071}
9072
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009073/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009074 * Process shell options. The global variable argptr contains a pointer
9075 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009076 */
9077static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009078minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009079{
9080 int i;
9081
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009082 if (name) {
9083 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009084 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009085 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009086 return;
9087 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009088 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009089 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009090 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009091 out1str("Current option settings\n");
9092 for (i = 0; i < NOPTS; i++)
9093 out1fmt("%-16s%s\n", optnames(i),
9094 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009095}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009096static void
9097setoption(int flag, int val)
9098{
9099 int i;
9100
9101 for (i = 0; i < NOPTS; i++) {
9102 if (optletters(i) == flag) {
9103 optlist[i] = val;
9104 return;
9105 }
9106 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009107 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009108 /* NOTREACHED */
9109}
Eric Andersenc470f442003-07-28 09:56:35 +00009110static void
9111options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009112{
9113 char *p;
9114 int val;
9115 int c;
9116
9117 if (cmdline)
9118 minusc = NULL;
9119 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009120 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009121 if (c != '-' && c != '+')
9122 break;
9123 argptr++;
9124 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009125 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009126 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009127 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009128 if (!cmdline) {
9129 /* "-" means turn off -x and -v */
9130 if (p[0] == '\0')
9131 xflag = vflag = 0;
9132 /* "--" means reset params */
9133 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009134 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009135 }
Eric Andersenc470f442003-07-28 09:56:35 +00009136 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009137 }
Eric Andersencb57d552001-06-28 07:25:16 +00009138 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009139 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009140 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009141 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009142 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009143 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009144 } else if (c == 'o') {
9145 minus_o(*argptr, val);
9146 if (*argptr)
9147 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009148 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9149 isloginsh = 1;
9150 /* bash does not accept +-login, we also won't */
9151 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009152 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009153 isloginsh = 1;
9154 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009155 } else {
9156 setoption(c, val);
9157 }
9158 }
9159 }
9160}
9161
Eric Andersencb57d552001-06-28 07:25:16 +00009162/*
Eric Andersencb57d552001-06-28 07:25:16 +00009163 * The shift builtin command.
9164 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009165static int
Eric Andersenc470f442003-07-28 09:56:35 +00009166shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009167{
9168 int n;
9169 char **ap1, **ap2;
9170
9171 n = 1;
9172 if (argc > 1)
9173 n = number(argv[1]);
9174 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009175 ash_msg_and_raise_error("can't shift that many");
9176 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009177 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009178 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009179 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009180 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009181 }
9182 ap2 = shellparam.p;
9183 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009184#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009185 shellparam.optind = 1;
9186 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009187#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009188 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009189 return 0;
9190}
9191
Eric Andersencb57d552001-06-28 07:25:16 +00009192/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009193 * POSIX requires that 'set' (but not export or readonly) output the
9194 * variables in lexicographic order - by the locale's collating order (sigh).
9195 * Maybe we could keep them in an ordered balanced binary tree
9196 * instead of hashed lists.
9197 * For now just roll 'em through qsort for printing...
9198 */
9199static int
9200showvars(const char *sep_prefix, int on, int off)
9201{
9202 const char *sep;
9203 char **ep, **epend;
9204
9205 ep = listvars(on, off, &epend);
9206 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9207
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009208 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009209
9210 for (; ep < epend; ep++) {
9211 const char *p;
9212 const char *q;
9213
9214 p = strchrnul(*ep, '=');
9215 q = nullstr;
9216 if (*p)
9217 q = single_quote(++p);
9218 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9219 }
9220 return 0;
9221}
9222
9223/*
Eric Andersencb57d552001-06-28 07:25:16 +00009224 * The set command builtin.
9225 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009226static int
Eric Andersenc470f442003-07-28 09:56:35 +00009227setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009228{
9229 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009230 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009231 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009232 options(0);
9233 optschanged();
9234 if (*argptr != NULL) {
9235 setparam(argptr);
9236 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009237 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009238 return 0;
9239}
9240
Denis Vlasenko131ae172007-02-18 13:00:19 +00009241#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009242/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009243static void
9244change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009245{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009246 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009247 /* "get", generate */
9248 char buf[16];
9249
9250 rseed = rseed * 1103515245 + 12345;
9251 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9252 /* set without recursion */
9253 setvar(vrandom.text, buf, VNOFUNC);
9254 vrandom.flags &= ~VNOFUNC;
9255 } else {
9256 /* set/reset */
9257 rseed = strtoul(value, (char **)NULL, 10);
9258 }
Eric Andersenef02f822004-03-11 13:34:24 +00009259}
Eric Andersen16767e22004-03-16 05:14:10 +00009260#endif
9261
Denis Vlasenko131ae172007-02-18 13:00:19 +00009262#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009263static int
Eric Andersenc470f442003-07-28 09:56:35 +00009264getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009265{
9266 char *p, *q;
9267 char c = '?';
9268 int done = 0;
9269 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009270 char s[12];
9271 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009272
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009273 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009274 return 1;
9275 optnext = optfirst + *param_optind - 1;
9276
9277 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009278 p = NULL;
9279 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009280 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009281 if (p == NULL || *p == '\0') {
9282 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009283 p = *optnext;
9284 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009285 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009286 p = NULL;
9287 done = 1;
9288 goto out;
9289 }
9290 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009291 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009292 goto atend;
9293 }
9294
9295 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009296 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009297 if (*q == '\0') {
9298 if (optstr[0] == ':') {
9299 s[0] = c;
9300 s[1] = '\0';
9301 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009302 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009303 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009304 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009305 }
9306 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009307 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009308 }
9309 if (*++q == ':')
9310 q++;
9311 }
9312
9313 if (*++q == ':') {
9314 if (*p == '\0' && (p = *optnext) == NULL) {
9315 if (optstr[0] == ':') {
9316 s[0] = c;
9317 s[1] = '\0';
9318 err |= setvarsafe("OPTARG", s, 0);
9319 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009320 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009321 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009322 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009323 c = '?';
9324 }
Eric Andersenc470f442003-07-28 09:56:35 +00009325 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009326 }
9327
9328 if (p == *optnext)
9329 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009330 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009331 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009332 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009333 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009334 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009335 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009336 *param_optind = optnext - optfirst + 1;
9337 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009338 err |= setvarsafe("OPTIND", s, VNOFUNC);
9339 s[0] = c;
9340 s[1] = '\0';
9341 err |= setvarsafe(optvar, s, 0);
9342 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009343 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009344 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009345 flush_stdout_stderr();
9346 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009347 }
9348 return done;
9349}
Eric Andersenc470f442003-07-28 09:56:35 +00009350
9351/*
9352 * The getopts builtin. Shellparam.optnext points to the next argument
9353 * to be processed. Shellparam.optptr points to the next character to
9354 * be processed in the current argument. If shellparam.optnext is NULL,
9355 * then it's the first time getopts has been called.
9356 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009357static int
Eric Andersenc470f442003-07-28 09:56:35 +00009358getoptscmd(int argc, char **argv)
9359{
9360 char **optbase;
9361
9362 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009363 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009364 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009365 optbase = shellparam.p;
9366 if (shellparam.optind > shellparam.nparam + 1) {
9367 shellparam.optind = 1;
9368 shellparam.optoff = -1;
9369 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009370 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009371 optbase = &argv[3];
9372 if (shellparam.optind > argc - 2) {
9373 shellparam.optind = 1;
9374 shellparam.optoff = -1;
9375 }
9376 }
9377
9378 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009379 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009380}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009381#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009382
Eric Andersencb57d552001-06-28 07:25:16 +00009383
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009384/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009385
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009386/*
9387 * NEOF is returned by parsecmd when it encounters an end of file. It
9388 * must be distinct from NULL, so we use the address of a variable that
9389 * happens to be handy.
9390 */
9391static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009392#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009393static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009394static int lasttoken; /* last token read */
9395static char *wordtext; /* text of last word returned by readtoken */
9396static struct nodelist *backquotelist;
9397static union node *redirnode;
9398static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009399static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009400
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009401static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9402static void
9403raise_error_syntax(const char *msg)
9404{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009405 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009406 /* NOTREACHED */
9407}
9408
9409/*
9410 * Called when an unexpected token is read during the parse. The argument
9411 * is the token that is expected, or -1 if more than one type of token can
9412 * occur at this point.
9413 */
9414static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9415static void
9416raise_error_unexpected_syntax(int token)
9417{
9418 char msg[64];
9419 int l;
9420
9421 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9422 if (token >= 0)
9423 sprintf(msg + l, " (expecting %s)", tokname(token));
9424 raise_error_syntax(msg);
9425 /* NOTREACHED */
9426}
Eric Andersencb57d552001-06-28 07:25:16 +00009427
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009428#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009429
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009430struct heredoc {
9431 struct heredoc *next; /* next here document in list */
9432 union node *here; /* redirection node */
9433 char *eofmark; /* string indicating end of input */
9434 int striptabs; /* if set, strip leading tabs */
9435};
Eric Andersencb57d552001-06-28 07:25:16 +00009436
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009437static struct heredoc *heredoclist; /* list of here documents to read */
9438
9439/* parsing is heavily cross-recursive, need these forward decls */
9440static union node *andor(void);
9441static union node *pipeline(void);
9442static union node *parse_command(void);
9443static void parseheredoc(void);
9444static char peektoken(void);
9445static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009446
Eric Andersenc470f442003-07-28 09:56:35 +00009447static union node *
9448list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009449{
9450 union node *n1, *n2, *n3;
9451 int tok;
9452
Eric Andersenc470f442003-07-28 09:56:35 +00009453 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9454 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009455 return NULL;
9456 n1 = NULL;
9457 for (;;) {
9458 n2 = andor();
9459 tok = readtoken();
9460 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009461 if (n2->type == NPIPE) {
9462 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009463 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009464 if (n2->type != NREDIR) {
9465 n3 = stalloc(sizeof(struct nredir));
9466 n3->nredir.n = n2;
9467 n3->nredir.redirect = NULL;
9468 n2 = n3;
9469 }
9470 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009471 }
9472 }
9473 if (n1 == NULL) {
9474 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009475 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009476 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009477 n3->type = NSEMI;
9478 n3->nbinary.ch1 = n1;
9479 n3->nbinary.ch2 = n2;
9480 n1 = n3;
9481 }
9482 switch (tok) {
9483 case TBACKGND:
9484 case TSEMI:
9485 tok = readtoken();
9486 /* fall through */
9487 case TNL:
9488 if (tok == TNL) {
9489 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009490 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009491 return n1;
9492 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009493 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009494 }
Eric Andersenc470f442003-07-28 09:56:35 +00009495 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009496 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009497 return n1;
9498 break;
9499 case TEOF:
9500 if (heredoclist)
9501 parseheredoc();
9502 else
Eric Andersenc470f442003-07-28 09:56:35 +00009503 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009504 return n1;
9505 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009506 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009507 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009508 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009509 return n1;
9510 }
9511 }
9512}
9513
Eric Andersenc470f442003-07-28 09:56:35 +00009514static union node *
9515andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009516{
Eric Andersencb57d552001-06-28 07:25:16 +00009517 union node *n1, *n2, *n3;
9518 int t;
9519
Eric Andersencb57d552001-06-28 07:25:16 +00009520 n1 = pipeline();
9521 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009522 t = readtoken();
9523 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009524 t = NAND;
9525 } else if (t == TOR) {
9526 t = NOR;
9527 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009528 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009529 return n1;
9530 }
Eric Andersenc470f442003-07-28 09:56:35 +00009531 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009532 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009533 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009534 n3->type = t;
9535 n3->nbinary.ch1 = n1;
9536 n3->nbinary.ch2 = n2;
9537 n1 = n3;
9538 }
9539}
9540
Eric Andersenc470f442003-07-28 09:56:35 +00009541static union node *
9542pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009543{
Eric Andersencb57d552001-06-28 07:25:16 +00009544 union node *n1, *n2, *pipenode;
9545 struct nodelist *lp, *prev;
9546 int negate;
9547
9548 negate = 0;
9549 TRACE(("pipeline: entered\n"));
9550 if (readtoken() == TNOT) {
9551 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009552 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009553 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009554 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009555 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009556 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009557 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009558 pipenode->type = NPIPE;
9559 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009560 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009561 pipenode->npipe.cmdlist = lp;
9562 lp->n = n1;
9563 do {
9564 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009565 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009566 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009567 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009568 prev->next = lp;
9569 } while (readtoken() == TPIPE);
9570 lp->next = NULL;
9571 n1 = pipenode;
9572 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009573 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009574 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009575 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009576 n2->type = NNOT;
9577 n2->nnot.com = n1;
9578 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009579 }
9580 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009581}
9582
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009583static union node *
9584makename(void)
9585{
9586 union node *n;
9587
9588 n = stalloc(sizeof(struct narg));
9589 n->type = NARG;
9590 n->narg.next = NULL;
9591 n->narg.text = wordtext;
9592 n->narg.backquote = backquotelist;
9593 return n;
9594}
9595
9596static void
9597fixredir(union node *n, const char *text, int err)
9598{
9599 TRACE(("Fix redir %s %d\n", text, err));
9600 if (!err)
9601 n->ndup.vname = NULL;
9602
9603 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009604 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009605 else if (LONE_DASH(text))
9606 n->ndup.dupfd = -1;
9607 else {
9608 if (err)
9609 raise_error_syntax("Bad fd number");
9610 n->ndup.vname = makename();
9611 }
9612}
9613
9614/*
9615 * Returns true if the text contains nothing to expand (no dollar signs
9616 * or backquotes).
9617 */
9618static int
9619noexpand(char *text)
9620{
9621 char *p;
9622 char c;
9623
9624 p = text;
9625 while ((c = *p++) != '\0') {
9626 if (c == CTLQUOTEMARK)
9627 continue;
9628 if (c == CTLESC)
9629 p++;
9630 else if (SIT(c, BASESYNTAX) == CCTL)
9631 return 0;
9632 }
9633 return 1;
9634}
9635
9636static void
9637parsefname(void)
9638{
9639 union node *n = redirnode;
9640
9641 if (readtoken() != TWORD)
9642 raise_error_unexpected_syntax(-1);
9643 if (n->type == NHERE) {
9644 struct heredoc *here = heredoc;
9645 struct heredoc *p;
9646 int i;
9647
9648 if (quoteflag == 0)
9649 n->type = NXHERE;
9650 TRACE(("Here document %d\n", n->type));
9651 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9652 raise_error_syntax("Illegal eof marker for << redirection");
9653 rmescapes(wordtext);
9654 here->eofmark = wordtext;
9655 here->next = NULL;
9656 if (heredoclist == NULL)
9657 heredoclist = here;
9658 else {
9659 for (p = heredoclist; p->next; p = p->next);
9660 p->next = here;
9661 }
9662 } else if (n->type == NTOFD || n->type == NFROMFD) {
9663 fixredir(n, wordtext, 0);
9664 } else {
9665 n->nfile.fname = makename();
9666 }
9667}
Eric Andersencb57d552001-06-28 07:25:16 +00009668
Eric Andersenc470f442003-07-28 09:56:35 +00009669static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009670simplecmd(void)
9671{
9672 union node *args, **app;
9673 union node *n = NULL;
9674 union node *vars, **vpp;
9675 union node **rpp, *redir;
9676 int savecheckkwd;
9677
9678 args = NULL;
9679 app = &args;
9680 vars = NULL;
9681 vpp = &vars;
9682 redir = NULL;
9683 rpp = &redir;
9684
9685 savecheckkwd = CHKALIAS;
9686 for (;;) {
9687 checkkwd = savecheckkwd;
9688 switch (readtoken()) {
9689 case TWORD:
9690 n = stalloc(sizeof(struct narg));
9691 n->type = NARG;
9692 n->narg.text = wordtext;
9693 n->narg.backquote = backquotelist;
9694 if (savecheckkwd && isassignment(wordtext)) {
9695 *vpp = n;
9696 vpp = &n->narg.next;
9697 } else {
9698 *app = n;
9699 app = &n->narg.next;
9700 savecheckkwd = 0;
9701 }
9702 break;
9703 case TREDIR:
9704 *rpp = n = redirnode;
9705 rpp = &n->nfile.next;
9706 parsefname(); /* read name of redirection file */
9707 break;
9708 case TLP:
9709 if (args && app == &args->narg.next
9710 && !vars && !redir
9711 ) {
9712 struct builtincmd *bcmd;
9713 const char *name;
9714
9715 /* We have a function */
9716 if (readtoken() != TRP)
9717 raise_error_unexpected_syntax(TRP);
9718 name = n->narg.text;
9719 if (!goodname(name)
9720 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9721 ) {
9722 raise_error_syntax("Bad function name");
9723 }
9724 n->type = NDEFUN;
9725 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9726 n->narg.next = parse_command();
9727 return n;
9728 }
9729 /* fall through */
9730 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009731 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009732 goto out;
9733 }
9734 }
9735 out:
9736 *app = NULL;
9737 *vpp = NULL;
9738 *rpp = NULL;
9739 n = stalloc(sizeof(struct ncmd));
9740 n->type = NCMD;
9741 n->ncmd.args = args;
9742 n->ncmd.assign = vars;
9743 n->ncmd.redirect = redir;
9744 return n;
9745}
9746
9747static union node *
9748parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009749{
Eric Andersencb57d552001-06-28 07:25:16 +00009750 union node *n1, *n2;
9751 union node *ap, **app;
9752 union node *cp, **cpp;
9753 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009754 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009755 int t;
9756
9757 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009758 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009759
Eric Andersencb57d552001-06-28 07:25:16 +00009760 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009761 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009762 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009763 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009764 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009765 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009766 n1->type = NIF;
9767 n1->nif.test = list(0);
9768 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009769 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009770 n1->nif.ifpart = list(0);
9771 n2 = n1;
9772 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009773 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009774 n2 = n2->nif.elsepart;
9775 n2->type = NIF;
9776 n2->nif.test = list(0);
9777 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009778 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009779 n2->nif.ifpart = list(0);
9780 }
9781 if (lasttoken == TELSE)
9782 n2->nif.elsepart = list(0);
9783 else {
9784 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009785 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009786 }
Eric Andersenc470f442003-07-28 09:56:35 +00009787 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009788 break;
9789 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009790 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009791 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009792 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009793 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009794 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009795 got = readtoken();
9796 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009797 TRACE(("expecting DO got %s %s\n", tokname(got),
9798 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009799 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009800 }
9801 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009802 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009803 break;
9804 }
9805 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009806 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009807 raise_error_syntax("Bad for loop variable");
9808 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009809 n1->type = NFOR;
9810 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009811 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009812 if (readtoken() == TIN) {
9813 app = &ap;
9814 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009815 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009816 n2->type = NARG;
9817 n2->narg.text = wordtext;
9818 n2->narg.backquote = backquotelist;
9819 *app = n2;
9820 app = &n2->narg.next;
9821 }
9822 *app = NULL;
9823 n1->nfor.args = ap;
9824 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009825 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009826 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009827 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009828 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009829 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009830 n2->narg.backquote = NULL;
9831 n2->narg.next = NULL;
9832 n1->nfor.args = n2;
9833 /*
9834 * Newline or semicolon here is optional (but note
9835 * that the original Bourne shell only allowed NL).
9836 */
9837 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009838 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009839 }
Eric Andersenc470f442003-07-28 09:56:35 +00009840 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009841 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009842 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009843 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009844 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009845 break;
9846 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009847 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009848 n1->type = NCASE;
9849 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009850 raise_error_unexpected_syntax(TWORD);
9851 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009852 n2->type = NARG;
9853 n2->narg.text = wordtext;
9854 n2->narg.backquote = backquotelist;
9855 n2->narg.next = NULL;
9856 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009857 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009858 } while (readtoken() == TNL);
9859 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009860 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009861 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009862 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009863 checkkwd = CHKNL | CHKKWD;
9864 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009865 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009866 if (lasttoken == TLP)
9867 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009868 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009869 cp->type = NCLIST;
9870 app = &cp->nclist.pattern;
9871 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009872 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009873 ap->type = NARG;
9874 ap->narg.text = wordtext;
9875 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009876 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009877 break;
9878 app = &ap->narg.next;
9879 readtoken();
9880 }
9881 ap->narg.next = NULL;
9882 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009883 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009884 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009885
Eric Andersenc470f442003-07-28 09:56:35 +00009886 cpp = &cp->nclist.next;
9887
9888 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009889 t = readtoken();
9890 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009891 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009892 raise_error_unexpected_syntax(TENDCASE);
9893 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009894 }
Eric Andersenc470f442003-07-28 09:56:35 +00009895 }
Eric Andersencb57d552001-06-28 07:25:16 +00009896 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009897 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009898 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009899 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009900 n1->type = NSUBSHELL;
9901 n1->nredir.n = list(0);
9902 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009903 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 break;
9905 case TBEGIN:
9906 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009907 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009908 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009910 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009911 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009912 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009913 }
9914
Eric Andersenc470f442003-07-28 09:56:35 +00009915 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009916 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009917
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009918 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009919 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009920 checkkwd = CHKKWD | CHKALIAS;
9921 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009922 while (readtoken() == TREDIR) {
9923 *rpp = n2 = redirnode;
9924 rpp = &n2->nfile.next;
9925 parsefname();
9926 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009927 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009928 *rpp = NULL;
9929 if (redir) {
9930 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009931 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009932 n2->type = NREDIR;
9933 n2->nredir.n = n1;
9934 n1 = n2;
9935 }
9936 n1->nredir.redirect = redir;
9937 }
Eric Andersencb57d552001-06-28 07:25:16 +00009938 return n1;
9939}
9940
Eric Andersencb57d552001-06-28 07:25:16 +00009941/*
9942 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9943 * is not NULL, read a here document. In the latter case, eofmark is the
9944 * word which marks the end of the document and striptabs is true if
9945 * leading tabs should be stripped from the document. The argument firstc
9946 * is the first character of the input token or document.
9947 *
9948 * Because C does not have internal subroutines, I have simulated them
9949 * using goto's to implement the subroutine linkage. The following macros
9950 * will run code that appears at the end of readtoken1.
9951 */
9952
Eric Andersen2870d962001-07-02 17:27:21 +00009953#define CHECKEND() {goto checkend; checkend_return:;}
9954#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9955#define PARSESUB() {goto parsesub; parsesub_return:;}
9956#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9957#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9958#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009959
9960static int
Eric Andersenc470f442003-07-28 09:56:35 +00009961readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009962{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009963 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009964 int c = firstc;
9965 char *out;
9966 int len;
9967 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009968 struct nodelist *bqlist;
9969 smallint quotef;
9970 smallint dblquote;
9971 smallint oldstyle;
9972 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009973#if ENABLE_ASH_EXPAND_PRMT
9974 smallint pssyntax; /* we are expanding a prompt string */
9975#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009976 int varnest; /* levels of variables expansion */
9977 int arinest; /* levels of arithmetic expansion */
9978 int parenlevel; /* levels of parens in arithmetic */
9979 int dqvarnest; /* levels of variables expansion within double quotes */
9980
Eric Andersencb57d552001-06-28 07:25:16 +00009981#if __GNUC__
9982 /* Avoid longjmp clobbering */
9983 (void) &out;
9984 (void) &quotef;
9985 (void) &dblquote;
9986 (void) &varnest;
9987 (void) &arinest;
9988 (void) &parenlevel;
9989 (void) &dqvarnest;
9990 (void) &oldstyle;
9991 (void) &prevsyntax;
9992 (void) &syntax;
9993#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009994 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009995 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009996 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009997 oldstyle = 0;
9998 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +00009999#if ENABLE_ASH_EXPAND_PRMT
10000 pssyntax = (syntax == PSSYNTAX);
10001 if (pssyntax)
10002 syntax = DQSYNTAX;
10003#endif
10004 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010005 varnest = 0;
10006 arinest = 0;
10007 parenlevel = 0;
10008 dqvarnest = 0;
10009
10010 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010011 loop: { /* for each line, until end of word */
10012 CHECKEND(); /* set c to PEOF if at end of here document */
10013 for (;;) { /* until end of line or end of word */
10014 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010015 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010016 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010017 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010018 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010019 USTPUTC(c, out);
10020 plinno++;
10021 if (doprompt)
10022 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010023 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010024 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010025 case CWORD:
10026 USTPUTC(c, out);
10027 break;
10028 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010029 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010030 USTPUTC(CTLESC, out);
10031 USTPUTC(c, out);
10032 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010033 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010034 c = pgetc2();
10035 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010036 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010037 USTPUTC('\\', out);
10038 pungetc();
10039 } else if (c == '\n') {
10040 if (doprompt)
10041 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010042 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010043#if ENABLE_ASH_EXPAND_PRMT
10044 if (c == '$' && pssyntax) {
10045 USTPUTC(CTLESC, out);
10046 USTPUTC('\\', out);
10047 }
10048#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010049 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010050 c != '\\' && c != '`' &&
10051 c != '$' && (
10052 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010053 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010054 ) {
10055 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010056 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010057 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010058 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010059 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010060 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010061 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010062 }
10063 break;
10064 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010065 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010066 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010067 if (eofmark == NULL) {
10068 USTPUTC(CTLQUOTEMARK, out);
10069 }
Eric Andersencb57d552001-06-28 07:25:16 +000010070 break;
10071 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010072 syntax = DQSYNTAX;
10073 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010074 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010075 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010076 if (eofmark != NULL && arinest == 0
10077 && varnest == 0
10078 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010079 USTPUTC(c, out);
10080 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010081 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010082 syntax = BASESYNTAX;
10083 dblquote = 0;
10084 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010085 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010086 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010087 }
10088 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010089 case CVAR: /* '$' */
10090 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010091 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010092 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010093 if (varnest > 0) {
10094 varnest--;
10095 if (dqvarnest > 0) {
10096 dqvarnest--;
10097 }
10098 USTPUTC(CTLENDVAR, out);
10099 } else {
10100 USTPUTC(c, out);
10101 }
10102 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010103#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010104 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010105 parenlevel++;
10106 USTPUTC(c, out);
10107 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010108 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010109 if (parenlevel > 0) {
10110 USTPUTC(c, out);
10111 --parenlevel;
10112 } else {
10113 if (pgetc() == ')') {
10114 if (--arinest == 0) {
10115 USTPUTC(CTLENDARI, out);
10116 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010117 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010118 } else
10119 USTPUTC(')', out);
10120 } else {
10121 /*
10122 * unbalanced parens
10123 * (don't 2nd guess - no error)
10124 */
10125 pungetc();
10126 USTPUTC(')', out);
10127 }
10128 }
10129 break;
10130#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010131 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010132 PARSEBACKQOLD();
10133 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010134 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010135 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010136 case CIGN:
10137 break;
10138 default:
10139 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010140 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010141#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010142 if (c != PEOA)
10143#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010144 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010145
Eric Andersencb57d552001-06-28 07:25:16 +000010146 }
10147 c = pgetc_macro();
10148 }
10149 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010150 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010151#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010152 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010153 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010154#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010155 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010156 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010157 if (varnest != 0) {
10158 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010159 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010160 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010161 }
10162 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010163 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010164 out = stackblock();
10165 if (eofmark == NULL) {
10166 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010167 && quotef == 0
10168 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010169 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010170 PARSEREDIR();
10171 return lasttoken = TREDIR;
10172 } else {
10173 pungetc();
10174 }
10175 }
10176 quoteflag = quotef;
10177 backquotelist = bqlist;
10178 grabstackblock(len);
10179 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010180 lasttoken = TWORD;
10181 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010182/* end of readtoken routine */
10183
Eric Andersencb57d552001-06-28 07:25:16 +000010184/*
10185 * Check to see whether we are at the end of the here document. When this
10186 * is called, c is set to the first character of the next input line. If
10187 * we are at the end of the here document, this routine sets the c to PEOF.
10188 */
Eric Andersenc470f442003-07-28 09:56:35 +000010189checkend: {
10190 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010191#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010192 if (c == PEOA) {
10193 c = pgetc2();
10194 }
10195#endif
10196 if (striptabs) {
10197 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010198 c = pgetc2();
10199 }
Eric Andersenc470f442003-07-28 09:56:35 +000010200 }
10201 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010202 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010203 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010204
Eric Andersenc470f442003-07-28 09:56:35 +000010205 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010206 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010207 if (*p == '\n' && *q == '\0') {
10208 c = PEOF;
10209 plinno++;
10210 needprompt = doprompt;
10211 } else {
10212 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010213 }
10214 }
10215 }
10216 }
Eric Andersenc470f442003-07-28 09:56:35 +000010217 goto checkend_return;
10218}
Eric Andersencb57d552001-06-28 07:25:16 +000010219
Eric Andersencb57d552001-06-28 07:25:16 +000010220/*
10221 * Parse a redirection operator. The variable "out" points to a string
10222 * specifying the fd to be redirected. The variable "c" contains the
10223 * first character of the redirection operator.
10224 */
Eric Andersenc470f442003-07-28 09:56:35 +000010225parseredir: {
10226 char fd = *out;
10227 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010228
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010229 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010230 if (c == '>') {
10231 np->nfile.fd = 1;
10232 c = pgetc();
10233 if (c == '>')
10234 np->type = NAPPEND;
10235 else if (c == '|')
10236 np->type = NCLOBBER;
10237 else if (c == '&')
10238 np->type = NTOFD;
10239 else {
10240 np->type = NTO;
10241 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010242 }
Eric Andersenc470f442003-07-28 09:56:35 +000010243 } else { /* c == '<' */
10244 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010245 c = pgetc();
10246 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010247 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010248 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010249 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010250 np->nfile.fd = 0;
10251 }
10252 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010253 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010254 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010255 c = pgetc();
10256 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010257 heredoc->striptabs = 1;
10258 } else {
10259 heredoc->striptabs = 0;
10260 pungetc();
10261 }
10262 break;
10263
10264 case '&':
10265 np->type = NFROMFD;
10266 break;
10267
10268 case '>':
10269 np->type = NFROMTO;
10270 break;
10271
10272 default:
10273 np->type = NFROM;
10274 pungetc();
10275 break;
10276 }
Eric Andersencb57d552001-06-28 07:25:16 +000010277 }
Eric Andersenc470f442003-07-28 09:56:35 +000010278 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010279 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010280 redirnode = np;
10281 goto parseredir_return;
10282}
Eric Andersencb57d552001-06-28 07:25:16 +000010283
Eric Andersencb57d552001-06-28 07:25:16 +000010284/*
10285 * Parse a substitution. At this point, we have read the dollar sign
10286 * and nothing else.
10287 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010288
10289/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10290 * (assuming ascii char codes, as the original implementation did) */
10291#define is_special(c) \
10292 ((((unsigned int)c) - 33 < 32) \
10293 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010294parsesub: {
10295 int subtype;
10296 int typeloc;
10297 int flags;
10298 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010299 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010300
Eric Andersenc470f442003-07-28 09:56:35 +000010301 c = pgetc();
10302 if (
10303 c <= PEOA_OR_PEOF ||
10304 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10305 ) {
10306 USTPUTC('$', out);
10307 pungetc();
10308 } else if (c == '(') { /* $(command) or $((arith)) */
10309 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010310#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010311 PARSEARITH();
10312#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010313 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010314#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010315 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010316 pungetc();
10317 PARSEBACKQNEW();
10318 }
10319 } else {
10320 USTPUTC(CTLVAR, out);
10321 typeloc = out - (char *)stackblock();
10322 USTPUTC(VSNORMAL, out);
10323 subtype = VSNORMAL;
10324 if (c == '{') {
10325 c = pgetc();
10326 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010327 c = pgetc();
10328 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010329 c = '#';
10330 else
10331 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010332 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010333 subtype = 0;
10334 }
10335 if (c > PEOA_OR_PEOF && is_name(c)) {
10336 do {
10337 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010338 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010339 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010340 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010341 do {
10342 STPUTC(c, out);
10343 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010344 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010345 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010346 USTPUTC(c, out);
10347 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010348 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010349 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010350
Eric Andersenc470f442003-07-28 09:56:35 +000010351 STPUTC('=', out);
10352 flags = 0;
10353 if (subtype == 0) {
10354 switch (c) {
10355 case ':':
10356 flags = VSNUL;
10357 c = pgetc();
10358 /*FALLTHROUGH*/
10359 default:
10360 p = strchr(types, c);
10361 if (p == NULL)
10362 goto badsub;
10363 subtype = p - types + VSNORMAL;
10364 break;
10365 case '%':
10366 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010367 {
10368 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010369 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010370 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010371 c = pgetc();
10372 if (c == cc)
10373 subtype++;
10374 else
10375 pungetc();
10376 break;
10377 }
10378 }
Eric Andersenc470f442003-07-28 09:56:35 +000010379 } else {
10380 pungetc();
10381 }
10382 if (dblquote || arinest)
10383 flags |= VSQUOTE;
10384 *((char *)stackblock() + typeloc) = subtype | flags;
10385 if (subtype != VSNORMAL) {
10386 varnest++;
10387 if (dblquote || arinest) {
10388 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010389 }
10390 }
10391 }
Eric Andersenc470f442003-07-28 09:56:35 +000010392 goto parsesub_return;
10393}
Eric Andersencb57d552001-06-28 07:25:16 +000010394
Eric Andersencb57d552001-06-28 07:25:16 +000010395/*
10396 * Called to parse command substitutions. Newstyle is set if the command
10397 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10398 * list of commands (passed by reference), and savelen is the number of
10399 * characters on the top of the stack which must be preserved.
10400 */
Eric Andersenc470f442003-07-28 09:56:35 +000010401parsebackq: {
10402 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010403 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010404 union node *n;
10405 char *volatile str;
10406 struct jmploc jmploc;
10407 struct jmploc *volatile savehandler;
10408 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010409 smallint saveprompt = 0;
10410
Eric Andersencb57d552001-06-28 07:25:16 +000010411#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010412 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010413#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010414 savepbq = parsebackquote;
10415 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010416 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010417 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010418 exception_handler = savehandler;
10419 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010420 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010421 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010422 str = NULL;
10423 savelen = out - (char *)stackblock();
10424 if (savelen > 0) {
10425 str = ckmalloc(savelen);
10426 memcpy(str, stackblock(), savelen);
10427 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010428 savehandler = exception_handler;
10429 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010430 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010431 if (oldstyle) {
10432 /* We must read until the closing backquote, giving special
10433 treatment to some slashes, and then push the string and
10434 reread it as input, interpreting it normally. */
10435 char *pout;
10436 int pc;
10437 size_t psavelen;
10438 char *pstr;
10439
10440
10441 STARTSTACKSTR(pout);
10442 for (;;) {
10443 if (needprompt) {
10444 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010445 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010446 pc = pgetc();
10447 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010448 case '`':
10449 goto done;
10450
10451 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010452 pc = pgetc();
10453 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010454 plinno++;
10455 if (doprompt)
10456 setprompt(2);
10457 /*
10458 * If eating a newline, avoid putting
10459 * the newline into the new character
10460 * stream (via the STPUTC after the
10461 * switch).
10462 */
10463 continue;
10464 }
10465 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010466 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010467 STPUTC('\\', pout);
10468 if (pc > PEOA_OR_PEOF) {
10469 break;
10470 }
10471 /* fall through */
10472
10473 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010474#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010475 case PEOA:
10476#endif
10477 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010478 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010479
10480 case '\n':
10481 plinno++;
10482 needprompt = doprompt;
10483 break;
10484
10485 default:
10486 break;
10487 }
10488 STPUTC(pc, pout);
10489 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010490 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010491 STPUTC('\0', pout);
10492 psavelen = pout - (char *)stackblock();
10493 if (psavelen > 0) {
10494 pstr = grabstackstr(pout);
10495 setinputstring(pstr);
10496 }
10497 }
10498 nlpp = &bqlist;
10499 while (*nlpp)
10500 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010501 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010502 (*nlpp)->next = NULL;
10503 parsebackquote = oldstyle;
10504
10505 if (oldstyle) {
10506 saveprompt = doprompt;
10507 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010508 }
10509
Eric Andersenc470f442003-07-28 09:56:35 +000010510 n = list(2);
10511
10512 if (oldstyle)
10513 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010514 else if (readtoken() != TRP)
10515 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010516
10517 (*nlpp)->n = n;
10518 if (oldstyle) {
10519 /*
10520 * Start reading from old file again, ignoring any pushed back
10521 * tokens left from the backquote parsing
10522 */
10523 popfile();
10524 tokpushback = 0;
10525 }
10526 while (stackblocksize() <= savelen)
10527 growstackblock();
10528 STARTSTACKSTR(out);
10529 if (str) {
10530 memcpy(out, str, savelen);
10531 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010532 INT_OFF;
10533 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010534 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010535 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010536 }
10537 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010538 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010539 if (arinest || dblquote)
10540 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10541 else
10542 USTPUTC(CTLBACKQ, out);
10543 if (oldstyle)
10544 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010545 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010546}
10547
Denis Vlasenko131ae172007-02-18 13:00:19 +000010548#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010549/*
10550 * Parse an arithmetic expansion (indicate start of one and set state)
10551 */
Eric Andersenc470f442003-07-28 09:56:35 +000010552parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010553 if (++arinest == 1) {
10554 prevsyntax = syntax;
10555 syntax = ARISYNTAX;
10556 USTPUTC(CTLARI, out);
10557 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010558 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010559 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010560 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010561 } else {
10562 /*
10563 * we collapse embedded arithmetic expansion to
10564 * parenthesis, which should be equivalent
10565 */
10566 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010567 }
Eric Andersenc470f442003-07-28 09:56:35 +000010568 goto parsearith_return;
10569}
10570#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010571
Eric Andersenc470f442003-07-28 09:56:35 +000010572} /* end of readtoken */
10573
Eric Andersencb57d552001-06-28 07:25:16 +000010574/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010575 * Read the next input token.
10576 * If the token is a word, we set backquotelist to the list of cmds in
10577 * backquotes. We set quoteflag to true if any part of the word was
10578 * quoted.
10579 * If the token is TREDIR, then we set redirnode to a structure containing
10580 * the redirection.
10581 * In all cases, the variable startlinno is set to the number of the line
10582 * on which the token starts.
10583 *
10584 * [Change comment: here documents and internal procedures]
10585 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10586 * word parsing code into a separate routine. In this case, readtoken
10587 * doesn't need to have any internal procedures, but parseword does.
10588 * We could also make parseoperator in essence the main routine, and
10589 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010590 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010591#define NEW_xxreadtoken
10592#ifdef NEW_xxreadtoken
10593/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010594static const char xxreadtoken_chars[7] ALIGN1 = {
10595 '\n', '(', ')', '&', '|', ';', 0
10596};
Eric Andersencb57d552001-06-28 07:25:16 +000010597
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010598static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010599 TNL, TLP, TRP, /* only single occurrence allowed */
10600 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10601 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010602 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010603};
10604
10605#define xxreadtoken_doubles \
10606 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10607#define xxreadtoken_singles \
10608 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10609
10610static int
10611xxreadtoken(void)
10612{
10613 int c;
10614
10615 if (tokpushback) {
10616 tokpushback = 0;
10617 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010618 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010619 if (needprompt) {
10620 setprompt(2);
10621 }
10622 startlinno = plinno;
10623 for (;;) { /* until token or start of word found */
10624 c = pgetc_macro();
10625
10626 if ((c != ' ') && (c != '\t')
10627#if ENABLE_ASH_ALIAS
10628 && (c != PEOA)
10629#endif
10630 ) {
10631 if (c == '#') {
10632 while ((c = pgetc()) != '\n' && c != PEOF);
10633 pungetc();
10634 } else if (c == '\\') {
10635 if (pgetc() != '\n') {
10636 pungetc();
10637 goto READTOKEN1;
10638 }
10639 startlinno = ++plinno;
10640 if (doprompt)
10641 setprompt(2);
10642 } else {
10643 const char *p
10644 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10645
10646 if (c != PEOF) {
10647 if (c == '\n') {
10648 plinno++;
10649 needprompt = doprompt;
10650 }
10651
10652 p = strchr(xxreadtoken_chars, c);
10653 if (p == NULL) {
10654 READTOKEN1:
10655 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10656 }
10657
10658 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10659 if (pgetc() == *p) { /* double occurrence? */
10660 p += xxreadtoken_doubles + 1;
10661 } else {
10662 pungetc();
10663 }
10664 }
10665 }
10666 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10667 }
10668 }
10669 } /* for */
10670}
10671#else
10672#define RETURN(token) return lasttoken = token
10673static int
10674xxreadtoken(void)
10675{
10676 int c;
10677
10678 if (tokpushback) {
10679 tokpushback = 0;
10680 return lasttoken;
10681 }
10682 if (needprompt) {
10683 setprompt(2);
10684 }
10685 startlinno = plinno;
10686 for (;;) { /* until token or start of word found */
10687 c = pgetc_macro();
10688 switch (c) {
10689 case ' ': case '\t':
10690#if ENABLE_ASH_ALIAS
10691 case PEOA:
10692#endif
10693 continue;
10694 case '#':
10695 while ((c = pgetc()) != '\n' && c != PEOF);
10696 pungetc();
10697 continue;
10698 case '\\':
10699 if (pgetc() == '\n') {
10700 startlinno = ++plinno;
10701 if (doprompt)
10702 setprompt(2);
10703 continue;
10704 }
10705 pungetc();
10706 goto breakloop;
10707 case '\n':
10708 plinno++;
10709 needprompt = doprompt;
10710 RETURN(TNL);
10711 case PEOF:
10712 RETURN(TEOF);
10713 case '&':
10714 if (pgetc() == '&')
10715 RETURN(TAND);
10716 pungetc();
10717 RETURN(TBACKGND);
10718 case '|':
10719 if (pgetc() == '|')
10720 RETURN(TOR);
10721 pungetc();
10722 RETURN(TPIPE);
10723 case ';':
10724 if (pgetc() == ';')
10725 RETURN(TENDCASE);
10726 pungetc();
10727 RETURN(TSEMI);
10728 case '(':
10729 RETURN(TLP);
10730 case ')':
10731 RETURN(TRP);
10732 default:
10733 goto breakloop;
10734 }
10735 }
10736 breakloop:
10737 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10738#undef RETURN
10739}
10740#endif /* NEW_xxreadtoken */
10741
10742static int
10743readtoken(void)
10744{
10745 int t;
10746#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010747 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010748#endif
10749
10750#if ENABLE_ASH_ALIAS
10751 top:
10752#endif
10753
10754 t = xxreadtoken();
10755
10756 /*
10757 * eat newlines
10758 */
10759 if (checkkwd & CHKNL) {
10760 while (t == TNL) {
10761 parseheredoc();
10762 t = xxreadtoken();
10763 }
10764 }
10765
10766 if (t != TWORD || quoteflag) {
10767 goto out;
10768 }
10769
10770 /*
10771 * check for keywords
10772 */
10773 if (checkkwd & CHKKWD) {
10774 const char *const *pp;
10775
10776 pp = findkwd(wordtext);
10777 if (pp) {
10778 lasttoken = t = pp - tokname_array;
10779 TRACE(("keyword %s recognized\n", tokname(t)));
10780 goto out;
10781 }
10782 }
10783
10784 if (checkkwd & CHKALIAS) {
10785#if ENABLE_ASH_ALIAS
10786 struct alias *ap;
10787 ap = lookupalias(wordtext, 1);
10788 if (ap != NULL) {
10789 if (*ap->val) {
10790 pushstring(ap->val, ap);
10791 }
10792 goto top;
10793 }
10794#endif
10795 }
10796 out:
10797 checkkwd = 0;
10798#if DEBUG
10799 if (!alreadyseen)
10800 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10801 else
10802 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10803#endif
10804 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010805}
10806
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010807static char
10808peektoken(void)
10809{
10810 int t;
10811
10812 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010813 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 return tokname_array[t][0];
10815}
Eric Andersencb57d552001-06-28 07:25:16 +000010816
10817/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010818 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10819 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010820 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010821static union node *
10822parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010823{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010824 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010825
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 tokpushback = 0;
10827 doprompt = interact;
10828 if (doprompt)
10829 setprompt(doprompt);
10830 needprompt = 0;
10831 t = readtoken();
10832 if (t == TEOF)
10833 return NEOF;
10834 if (t == TNL)
10835 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010836 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010837 return list(1);
10838}
10839
10840/*
10841 * Input any here documents.
10842 */
10843static void
10844parseheredoc(void)
10845{
10846 struct heredoc *here;
10847 union node *n;
10848
10849 here = heredoclist;
10850 heredoclist = 0;
10851
10852 while (here) {
10853 if (needprompt) {
10854 setprompt(2);
10855 }
10856 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10857 here->eofmark, here->striptabs);
10858 n = stalloc(sizeof(struct narg));
10859 n->narg.type = NARG;
10860 n->narg.next = NULL;
10861 n->narg.text = wordtext;
10862 n->narg.backquote = backquotelist;
10863 here->here->nhere.doc = n;
10864 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010865 }
Eric Andersencb57d552001-06-28 07:25:16 +000010866}
10867
10868
10869/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010870 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010871 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010872#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010873static const char *
10874expandstr(const char *ps)
10875{
10876 union node n;
10877
10878 /* XXX Fix (char *) cast. */
10879 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010880 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010881 popfile();
10882
10883 n.narg.type = NARG;
10884 n.narg.next = NULL;
10885 n.narg.text = wordtext;
10886 n.narg.backquote = backquotelist;
10887
10888 expandarg(&n, NULL, 0);
10889 return stackblock();
10890}
10891#endif
10892
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010893/*
10894 * Execute a command or commands contained in a string.
10895 */
10896static int
10897evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010898{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010899 union node *n;
10900 struct stackmark smark;
10901 int skip;
10902
10903 setinputstring(s);
10904 setstackmark(&smark);
10905
10906 skip = 0;
10907 while ((n = parsecmd(0)) != NEOF) {
10908 evaltree(n, 0);
10909 popstackmark(&smark);
10910 skip = evalskip;
10911 if (skip)
10912 break;
10913 }
10914 popfile();
10915
10916 skip &= mask;
10917 evalskip = skip;
10918 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010919}
10920
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010921/*
10922 * The eval command.
10923 */
10924static int
10925evalcmd(int argc, char **argv)
10926{
10927 char *p;
10928 char *concat;
10929 char **ap;
10930
10931 if (argc > 1) {
10932 p = argv[1];
10933 if (argc > 2) {
10934 STARTSTACKSTR(concat);
10935 ap = argv + 2;
10936 for (;;) {
10937 concat = stack_putstr(p, concat);
10938 p = *ap++;
10939 if (p == NULL)
10940 break;
10941 STPUTC(' ', concat);
10942 }
10943 STPUTC('\0', concat);
10944 p = grabstackstr(concat);
10945 }
10946 evalstring(p, ~SKIPEVAL);
10947
10948 }
10949 return exitstatus;
10950}
10951
10952/*
10953 * Read and execute commands. "Top" is nonzero for the top level command
10954 * loop; it turns on prompting if the shell is interactive.
10955 */
10956static int
10957cmdloop(int top)
10958{
10959 union node *n;
10960 struct stackmark smark;
10961 int inter;
10962 int numeof = 0;
10963
10964 TRACE(("cmdloop(%d) called\n", top));
10965 for (;;) {
10966 int skip;
10967
10968 setstackmark(&smark);
10969#if JOBS
10970 if (jobctl)
10971 showjobs(stderr, SHOW_CHANGED);
10972#endif
10973 inter = 0;
10974 if (iflag && top) {
10975 inter++;
10976#if ENABLE_ASH_MAIL
10977 chkmail();
10978#endif
10979 }
10980 n = parsecmd(inter);
10981 /* showtree(n); DEBUG */
10982 if (n == NEOF) {
10983 if (!top || numeof >= 50)
10984 break;
10985 if (!stoppedjobs()) {
10986 if (!Iflag)
10987 break;
10988 out2str("\nUse \"exit\" to leave shell.\n");
10989 }
10990 numeof++;
10991 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010992 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10993 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010994 numeof = 0;
10995 evaltree(n, 0);
10996 }
10997 popstackmark(&smark);
10998 skip = evalskip;
10999
11000 if (skip) {
11001 evalskip = 0;
11002 return skip & SKIPEVAL;
11003 }
11004 }
11005 return 0;
11006}
11007
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011008/*
11009 * Take commands from a file. To be compatible we should do a path
11010 * search for the file, which is necessary to find sub-commands.
11011 */
11012static char *
11013find_dot_file(char *name)
11014{
11015 char *fullname;
11016 const char *path = pathval();
11017 struct stat statb;
11018
11019 /* don't try this for absolute or relative paths */
11020 if (strchr(name, '/'))
11021 return name;
11022
11023 while ((fullname = padvance(&path, name)) != NULL) {
11024 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11025 /*
11026 * Don't bother freeing here, since it will
11027 * be freed by the caller.
11028 */
11029 return fullname;
11030 }
11031 stunalloc(fullname);
11032 }
11033
11034 /* not found in the PATH */
11035 ash_msg_and_raise_error("%s: not found", name);
11036 /* NOTREACHED */
11037}
11038
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011039static int
11040dotcmd(int argc, char **argv)
11041{
11042 struct strlist *sp;
11043 volatile struct shparam saveparam;
11044 int status = 0;
11045
11046 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011047 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011048
11049 if (argc >= 2) { /* That's what SVR2 does */
11050 char *fullname;
11051
11052 fullname = find_dot_file(argv[1]);
11053
11054 if (argc > 2) {
11055 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011056 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011057 shellparam.nparam = argc - 2;
11058 shellparam.p = argv + 2;
11059 };
11060
11061 setinputfile(fullname, INPUT_PUSH_FILE);
11062 commandname = fullname;
11063 cmdloop(0);
11064 popfile();
11065
11066 if (argc > 2) {
11067 freeparam(&shellparam);
11068 shellparam = saveparam;
11069 };
11070 status = exitstatus;
11071 }
11072 return status;
11073}
11074
11075static int
11076exitcmd(int argc, char **argv)
11077{
11078 if (stoppedjobs())
11079 return 0;
11080 if (argc > 1)
11081 exitstatus = number(argv[1]);
11082 raise_exception(EXEXIT);
11083 /* NOTREACHED */
11084}
11085
11086#if ENABLE_ASH_BUILTIN_ECHO
11087static int
11088echocmd(int argc, char **argv)
11089{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011090 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011091}
11092#endif
11093
11094#if ENABLE_ASH_BUILTIN_TEST
11095static int
11096testcmd(int argc, char **argv)
11097{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011098 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011099}
11100#endif
11101
11102/*
11103 * Read a file containing shell functions.
11104 */
11105static void
11106readcmdfile(char *name)
11107{
11108 setinputfile(name, INPUT_PUSH_FILE);
11109 cmdloop(0);
11110 popfile();
11111}
11112
11113
Denis Vlasenkocc571512007-02-23 21:10:35 +000011114/* ============ find_command inplementation */
11115
11116/*
11117 * Resolve a command name. If you change this routine, you may have to
11118 * change the shellexec routine as well.
11119 */
11120static void
11121find_command(char *name, struct cmdentry *entry, int act, const char *path)
11122{
11123 struct tblentry *cmdp;
11124 int idx;
11125 int prev;
11126 char *fullname;
11127 struct stat statb;
11128 int e;
11129 int updatetbl;
11130 struct builtincmd *bcmd;
11131
11132 /* If name contains a slash, don't use PATH or hash table */
11133 if (strchr(name, '/') != NULL) {
11134 entry->u.index = -1;
11135 if (act & DO_ABS) {
11136 while (stat(name, &statb) < 0) {
11137#ifdef SYSV
11138 if (errno == EINTR)
11139 continue;
11140#endif
11141 entry->cmdtype = CMDUNKNOWN;
11142 return;
11143 }
11144 }
11145 entry->cmdtype = CMDNORMAL;
11146 return;
11147 }
11148
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011149/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011150
11151 updatetbl = (path == pathval());
11152 if (!updatetbl) {
11153 act |= DO_ALTPATH;
11154 if (strstr(path, "%builtin") != NULL)
11155 act |= DO_ALTBLTIN;
11156 }
11157
11158 /* If name is in the table, check answer will be ok */
11159 cmdp = cmdlookup(name, 0);
11160 if (cmdp != NULL) {
11161 int bit;
11162
11163 switch (cmdp->cmdtype) {
11164 default:
11165#if DEBUG
11166 abort();
11167#endif
11168 case CMDNORMAL:
11169 bit = DO_ALTPATH;
11170 break;
11171 case CMDFUNCTION:
11172 bit = DO_NOFUNC;
11173 break;
11174 case CMDBUILTIN:
11175 bit = DO_ALTBLTIN;
11176 break;
11177 }
11178 if (act & bit) {
11179 updatetbl = 0;
11180 cmdp = NULL;
11181 } else if (cmdp->rehash == 0)
11182 /* if not invalidated by cd, we're done */
11183 goto success;
11184 }
11185
11186 /* If %builtin not in path, check for builtin next */
11187 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011188 if (bcmd) {
11189 if (IS_BUILTIN_REGULAR(bcmd))
11190 goto builtin_success;
11191 if (act & DO_ALTPATH) {
11192 if (!(act & DO_ALTBLTIN))
11193 goto builtin_success;
11194 } else if (builtinloc <= 0) {
11195 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011196 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011197 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011198
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011199#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011200 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011201 entry->cmdtype = CMDNORMAL;
11202 entry->u.index = -1;
11203 return;
11204 }
11205#endif
11206
Denis Vlasenkocc571512007-02-23 21:10:35 +000011207 /* We have to search path. */
11208 prev = -1; /* where to start */
11209 if (cmdp && cmdp->rehash) { /* doing a rehash */
11210 if (cmdp->cmdtype == CMDBUILTIN)
11211 prev = builtinloc;
11212 else
11213 prev = cmdp->param.index;
11214 }
11215
11216 e = ENOENT;
11217 idx = -1;
11218 loop:
11219 while ((fullname = padvance(&path, name)) != NULL) {
11220 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011221 /* NB: code below will still use fullname
11222 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011223 idx++;
11224 if (pathopt) {
11225 if (prefix(pathopt, "builtin")) {
11226 if (bcmd)
11227 goto builtin_success;
11228 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011229 } else if (!(act & DO_NOFUNC)
11230 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011231 /* handled below */
11232 } else {
11233 /* ignore unimplemented options */
11234 continue;
11235 }
11236 }
11237 /* if rehash, don't redo absolute path names */
11238 if (fullname[0] == '/' && idx <= prev) {
11239 if (idx < prev)
11240 continue;
11241 TRACE(("searchexec \"%s\": no change\n", name));
11242 goto success;
11243 }
11244 while (stat(fullname, &statb) < 0) {
11245#ifdef SYSV
11246 if (errno == EINTR)
11247 continue;
11248#endif
11249 if (errno != ENOENT && errno != ENOTDIR)
11250 e = errno;
11251 goto loop;
11252 }
11253 e = EACCES; /* if we fail, this will be the error */
11254 if (!S_ISREG(statb.st_mode))
11255 continue;
11256 if (pathopt) { /* this is a %func directory */
11257 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011258 /* NB: stalloc will return space pointed by fullname
11259 * (because we don't have any intervening allocations
11260 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011261 readcmdfile(fullname);
11262 cmdp = cmdlookup(name, 0);
11263 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11264 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11265 stunalloc(fullname);
11266 goto success;
11267 }
11268 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11269 if (!updatetbl) {
11270 entry->cmdtype = CMDNORMAL;
11271 entry->u.index = idx;
11272 return;
11273 }
11274 INT_OFF;
11275 cmdp = cmdlookup(name, 1);
11276 cmdp->cmdtype = CMDNORMAL;
11277 cmdp->param.index = idx;
11278 INT_ON;
11279 goto success;
11280 }
11281
11282 /* We failed. If there was an entry for this command, delete it */
11283 if (cmdp && updatetbl)
11284 delete_cmd_entry();
11285 if (act & DO_ERR)
11286 ash_msg("%s: %s", name, errmsg(e, "not found"));
11287 entry->cmdtype = CMDUNKNOWN;
11288 return;
11289
11290 builtin_success:
11291 if (!updatetbl) {
11292 entry->cmdtype = CMDBUILTIN;
11293 entry->u.cmd = bcmd;
11294 return;
11295 }
11296 INT_OFF;
11297 cmdp = cmdlookup(name, 1);
11298 cmdp->cmdtype = CMDBUILTIN;
11299 cmdp->param.cmd = bcmd;
11300 INT_ON;
11301 success:
11302 cmdp->rehash = 0;
11303 entry->cmdtype = cmdp->cmdtype;
11304 entry->u = cmdp->param;
11305}
11306
11307
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011308/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011309
Eric Andersencb57d552001-06-28 07:25:16 +000011310/*
Eric Andersencb57d552001-06-28 07:25:16 +000011311 * The trap builtin.
11312 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011313static int
Eric Andersenc470f442003-07-28 09:56:35 +000011314trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011315{
11316 char *action;
11317 char **ap;
11318 int signo;
11319
Eric Andersenc470f442003-07-28 09:56:35 +000011320 nextopt(nullstr);
11321 ap = argptr;
11322 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011323 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011324 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011325 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011326
Rob Landleyc9c1a412006-07-12 19:17:55 +000011327 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011328 out1fmt("trap -- %s %s\n",
11329 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011330 }
11331 }
11332 return 0;
11333 }
Eric Andersenc470f442003-07-28 09:56:35 +000011334 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011335 action = NULL;
11336 else
11337 action = *ap++;
11338 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011339 signo = get_signum(*ap);
11340 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011341 ash_msg_and_raise_error("%s: bad trap", *ap);
11342 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011343 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011344 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011345 action = NULL;
11346 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011347 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011348 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011349 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011350 trap[signo] = action;
11351 if (signo != 0)
11352 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011353 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011354 ap++;
11355 }
11356 return 0;
11357}
11358
Eric Andersenc470f442003-07-28 09:56:35 +000011359
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011360/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011361
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011362#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011363/*
11364 * Lists available builtins
11365 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011366static int
11367helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011368{
11369 int col, i;
11370
11371 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011372 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011373 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011374 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011375 if (col > 60) {
11376 out1fmt("\n");
11377 col = 0;
11378 }
11379 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011380#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011381 {
11382 const char *a = applet_names;
11383 while (*a) {
11384 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11385 if (col > 60) {
11386 out1fmt("\n");
11387 col = 0;
11388 }
11389 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011390 }
11391 }
11392#endif
11393 out1fmt("\n\n");
11394 return EXIT_SUCCESS;
11395}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011396#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011397
Eric Andersencb57d552001-06-28 07:25:16 +000011398/*
Eric Andersencb57d552001-06-28 07:25:16 +000011399 * The export and readonly commands.
11400 */
Eric Andersenc470f442003-07-28 09:56:35 +000011401static int
11402exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011403{
11404 struct var *vp;
11405 char *name;
11406 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011407 char **aptr;
11408 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011409
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011410 if (nextopt("p") != 'p') {
11411 aptr = argptr;
11412 name = *aptr;
11413 if (name) {
11414 do {
11415 p = strchr(name, '=');
11416 if (p != NULL) {
11417 p++;
11418 } else {
11419 vp = *findvar(hashvar(name), name);
11420 if (vp) {
11421 vp->flags |= flag;
11422 continue;
11423 }
Eric Andersencb57d552001-06-28 07:25:16 +000011424 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011425 setvar(name, p, flag);
11426 } while ((name = *++aptr) != NULL);
11427 return 0;
11428 }
Eric Andersencb57d552001-06-28 07:25:16 +000011429 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011430 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011431 return 0;
11432}
11433
Eric Andersencb57d552001-06-28 07:25:16 +000011434/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011435 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011436 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011437static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011438unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011439{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011440 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011441
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011442 cmdp = cmdlookup(name, 0);
11443 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11444 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011445}
11446
Eric Andersencb57d552001-06-28 07:25:16 +000011447/*
Eric Andersencb57d552001-06-28 07:25:16 +000011448 * The unset builtin command. We unset the function before we unset the
11449 * variable to allow a function to be unset when there is a readonly variable
11450 * with the same name.
11451 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011452static int
Eric Andersenc470f442003-07-28 09:56:35 +000011453unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011454{
11455 char **ap;
11456 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011457 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011458 int ret = 0;
11459
11460 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011461 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011462 }
Eric Andersencb57d552001-06-28 07:25:16 +000011463
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011464 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011465 if (flag != 'f') {
11466 i = unsetvar(*ap);
11467 ret |= i;
11468 if (!(i & 2))
11469 continue;
11470 }
11471 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011472 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011473 }
Eric Andersenc470f442003-07-28 09:56:35 +000011474 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011475}
11476
11477
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011478/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011479
Eric Andersenc470f442003-07-28 09:56:35 +000011480#include <sys/times.h>
11481
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011482static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011483 ' ', offsetof(struct tms, tms_utime),
11484 '\n', offsetof(struct tms, tms_stime),
11485 ' ', offsetof(struct tms, tms_cutime),
11486 '\n', offsetof(struct tms, tms_cstime),
11487 0
11488};
Eric Andersencb57d552001-06-28 07:25:16 +000011489
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011490static int
11491timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011492{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011493 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011494 const unsigned char *p;
11495 struct tms buf;
11496
11497 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011498 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011499
11500 p = timescmd_str;
11501 do {
11502 t = *(clock_t *)(((char *) &buf) + p[1]);
11503 s = t / clk_tck;
11504 out1fmt("%ldm%ld.%.3lds%c",
11505 s/60, s%60,
11506 ((t - s * clk_tck) * 1000) / clk_tck,
11507 p[0]);
11508 } while (*(p += 2));
11509
Eric Andersencb57d552001-06-28 07:25:16 +000011510 return 0;
11511}
11512
Denis Vlasenko131ae172007-02-18 13:00:19 +000011513#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011514static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011515dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011516{
Eric Andersened9ecf72004-06-22 08:29:45 +000011517 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011518 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011519
Denis Vlasenkob012b102007-02-19 22:43:01 +000011520 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011521 result = arith(s, &errcode);
11522 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011523 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011524 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011525 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011526 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011527 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011529 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011530 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011531 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011532
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011533 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011534}
Eric Andersenc470f442003-07-28 09:56:35 +000011535
Eric Andersenc470f442003-07-28 09:56:35 +000011536/*
Eric Andersen90898442003-08-06 11:20:52 +000011537 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11538 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11539 *
11540 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011541 */
11542static int
Eric Andersen90898442003-08-06 11:20:52 +000011543letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011544{
Eric Andersenc470f442003-07-28 09:56:35 +000011545 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011546 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011547
Eric Andersen90898442003-08-06 11:20:52 +000011548 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011549 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011550 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011551 for (ap = argv + 1; *ap; ap++) {
11552 i = dash_arith(*ap);
11553 }
Eric Andersenc470f442003-07-28 09:56:35 +000011554
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011555 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011556}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011557#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011558
Eric Andersenc470f442003-07-28 09:56:35 +000011559
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011560/* ============ miscbltin.c
11561 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011562 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011563 */
11564
11565#undef rflag
11566
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011567#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011568typedef enum __rlimit_resource rlim_t;
11569#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011570
Eric Andersenc470f442003-07-28 09:56:35 +000011571/*
11572 * The read builtin. The -e option causes backslashes to escape the
11573 * following character.
11574 *
11575 * This uses unbuffered input, which may be avoidable in some cases.
11576 */
Eric Andersenc470f442003-07-28 09:56:35 +000011577static int
11578readcmd(int argc, char **argv)
11579{
11580 char **ap;
11581 int backslash;
11582 char c;
11583 int rflag;
11584 char *prompt;
11585 const char *ifs;
11586 char *p;
11587 int startword;
11588 int status;
11589 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011590#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011591 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011592 int nchars = 0;
11593 int silent = 0;
11594 struct termios tty, old_tty;
11595#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011596#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011597 fd_set set;
11598 struct timeval ts;
11599
11600 ts.tv_sec = ts.tv_usec = 0;
11601#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011602
11603 rflag = 0;
11604 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011605#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011606 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011607#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011608 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011609#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011610 while ((i = nextopt("p:rt:")) != '\0')
11611#else
11612 while ((i = nextopt("p:r")) != '\0')
11613#endif
11614 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011615 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011616 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011617 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011618 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011619#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011620 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011621 nchars = bb_strtou(optionarg, NULL, 10);
11622 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011623 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011624 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011625 break;
11626 case 's':
11627 silent = 1;
11628 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011629#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011630#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011631 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011632 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011633 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011634 /* EINVAL means number is ok, but not terminated by NUL */
11635 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011636 char *p2;
11637 if (*++p) {
11638 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011639 ts.tv_usec = bb_strtou(p, &p2, 10);
11640 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011641 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011642 scale = p2 - p;
11643 /* normalize to usec */
11644 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011645 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011646 while (scale++ < 6)
11647 ts.tv_usec *= 10;
11648 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011649 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011650 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011651 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011652 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011653 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011654 }
Paul Fox02eb9342005-09-07 16:56:02 +000011655 break;
11656#endif
11657 case 'r':
11658 rflag = 1;
11659 break;
11660 default:
11661 break;
11662 }
Eric Andersenc470f442003-07-28 09:56:35 +000011663 }
11664 if (prompt && isatty(0)) {
11665 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011666 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011667 ap = argptr;
11668 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011669 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011670 ifs = bltinlookup("IFS");
11671 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011672 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011673#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011674 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011675 if (tcgetattr(0, &tty) != 0) {
11676 /* Not a tty */
11677 n_flag = 0;
11678 silent = 0;
11679 } else {
11680 old_tty = tty;
11681 if (n_flag) {
11682 tty.c_lflag &= ~ICANON;
11683 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11684 }
11685 if (silent) {
11686 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11687 }
11688 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011689 }
Paul Fox02eb9342005-09-07 16:56:02 +000011690 }
11691#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011692#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011693 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011694 FD_ZERO(&set);
11695 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011696
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011697 /* poll-based wait produces bigger code, using select */
11698 i = select(1, &set, NULL, NULL, &ts);
11699 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011700#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011701 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011702 tcsetattr(0, TCSANOW, &old_tty);
11703#endif
11704 return 1;
11705 }
11706 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011707#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011708 status = 0;
11709 startword = 1;
11710 backslash = 0;
11711 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011712 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011713 if (read(0, &c, 1) != 1) {
11714 status = 1;
11715 break;
11716 }
11717 if (c == '\0')
11718 continue;
11719 if (backslash) {
11720 backslash = 0;
11721 if (c != '\n')
11722 goto put;
11723 continue;
11724 }
11725 if (!rflag && c == '\\') {
11726 backslash++;
11727 continue;
11728 }
11729 if (c == '\n')
11730 break;
11731 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11732 continue;
11733 }
11734 startword = 0;
11735 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11736 STACKSTRNUL(p);
11737 setvar(*ap, stackblock(), 0);
11738 ap++;
11739 startword = 1;
11740 STARTSTACKSTR(p);
11741 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011742 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011743 STPUTC(c, p);
11744 }
11745 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011746/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011747#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011748 while (!n_flag || --nchars);
11749#else
11750 while (1);
11751#endif
11752
11753#if ENABLE_ASH_READ_NCHARS
11754 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011755 tcsetattr(0, TCSANOW, &old_tty);
11756#endif
11757
Eric Andersenc470f442003-07-28 09:56:35 +000011758 STACKSTRNUL(p);
11759 /* Remove trailing blanks */
11760 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11761 *p = '\0';
11762 setvar(*ap, stackblock(), 0);
11763 while (*++ap != NULL)
11764 setvar(*ap, nullstr, 0);
11765 return status;
11766}
11767
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011768static int
11769umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011770{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011771 static const char permuser[3] ALIGN1 = "ugo";
11772 static const char permmode[3] ALIGN1 = "rwx";
11773 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011774 S_IRUSR, S_IWUSR, S_IXUSR,
11775 S_IRGRP, S_IWGRP, S_IXGRP,
11776 S_IROTH, S_IWOTH, S_IXOTH
11777 };
11778
11779 char *ap;
11780 mode_t mask;
11781 int i;
11782 int symbolic_mode = 0;
11783
11784 while (nextopt("S") != '\0') {
11785 symbolic_mode = 1;
11786 }
11787
Denis Vlasenkob012b102007-02-19 22:43:01 +000011788 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011789 mask = umask(0);
11790 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011791 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011792
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011793 ap = *argptr;
11794 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011795 if (symbolic_mode) {
11796 char buf[18];
11797 char *p = buf;
11798
11799 for (i = 0; i < 3; i++) {
11800 int j;
11801
11802 *p++ = permuser[i];
11803 *p++ = '=';
11804 for (j = 0; j < 3; j++) {
11805 if ((mask & permmask[3 * i + j]) == 0) {
11806 *p++ = permmode[j];
11807 }
11808 }
11809 *p++ = ',';
11810 }
11811 *--p = 0;
11812 puts(buf);
11813 } else {
11814 out1fmt("%.4o\n", mask);
11815 }
11816 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011817 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011818 mask = 0;
11819 do {
11820 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011821 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011822 mask = (mask << 3) + (*ap - '0');
11823 } while (*++ap != '\0');
11824 umask(mask);
11825 } else {
11826 mask = ~mask & 0777;
11827 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011828 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011829 }
11830 umask(~mask & 0777);
11831 }
11832 }
11833 return 0;
11834}
11835
11836/*
11837 * ulimit builtin
11838 *
11839 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11840 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11841 * ash by J.T. Conklin.
11842 *
11843 * Public domain.
11844 */
11845
11846struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011847 uint8_t cmd; /* RLIMIT_xxx fit into it */
11848 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011849 char option;
11850};
11851
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011852static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011853#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011854 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011855#endif
11856#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011857 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011858#endif
11859#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011860 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011861#endif
11862#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011863 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011864#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011865#ifdef RLIMIT_CORE
11866 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011867#endif
11868#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011869 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011870#endif
11871#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011872 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011873#endif
11874#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011875 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011876#endif
11877#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011878 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011879#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011880#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011881 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011882#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011883#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011884 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011885#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011886};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011887static const char limits_name[] =
11888#ifdef RLIMIT_CPU
11889 "time(seconds)" "\0"
11890#endif
11891#ifdef RLIMIT_FSIZE
11892 "file(blocks)" "\0"
11893#endif
11894#ifdef RLIMIT_DATA
11895 "data(kb)" "\0"
11896#endif
11897#ifdef RLIMIT_STACK
11898 "stack(kb)" "\0"
11899#endif
11900#ifdef RLIMIT_CORE
11901 "coredump(blocks)" "\0"
11902#endif
11903#ifdef RLIMIT_RSS
11904 "memory(kb)" "\0"
11905#endif
11906#ifdef RLIMIT_MEMLOCK
11907 "locked memory(kb)" "\0"
11908#endif
11909#ifdef RLIMIT_NPROC
11910 "process" "\0"
11911#endif
11912#ifdef RLIMIT_NOFILE
11913 "nofiles" "\0"
11914#endif
11915#ifdef RLIMIT_AS
11916 "vmemory(kb)" "\0"
11917#endif
11918#ifdef RLIMIT_LOCKS
11919 "locks" "\0"
11920#endif
11921;
Eric Andersenc470f442003-07-28 09:56:35 +000011922
Glenn L McGrath76620622004-01-13 10:19:37 +000011923enum limtype { SOFT = 0x1, HARD = 0x2 };
11924
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011925static void
11926printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011927 const struct limits *l)
11928{
11929 rlim_t val;
11930
11931 val = limit->rlim_max;
11932 if (how & SOFT)
11933 val = limit->rlim_cur;
11934
11935 if (val == RLIM_INFINITY)
11936 out1fmt("unlimited\n");
11937 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011938 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011939 out1fmt("%lld\n", (long long) val);
11940 }
11941}
11942
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011943static int
Eric Andersenc470f442003-07-28 09:56:35 +000011944ulimitcmd(int argc, char **argv)
11945{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011946 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011947 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011948 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011949 const struct limits *l;
11950 int set, all = 0;
11951 int optc, what;
11952 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011953
11954 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011955 while ((optc = nextopt("HSa"
11956#ifdef RLIMIT_CPU
11957 "t"
11958#endif
11959#ifdef RLIMIT_FSIZE
11960 "f"
11961#endif
11962#ifdef RLIMIT_DATA
11963 "d"
11964#endif
11965#ifdef RLIMIT_STACK
11966 "s"
11967#endif
11968#ifdef RLIMIT_CORE
11969 "c"
11970#endif
11971#ifdef RLIMIT_RSS
11972 "m"
11973#endif
11974#ifdef RLIMIT_MEMLOCK
11975 "l"
11976#endif
11977#ifdef RLIMIT_NPROC
11978 "p"
11979#endif
11980#ifdef RLIMIT_NOFILE
11981 "n"
11982#endif
11983#ifdef RLIMIT_AS
11984 "v"
11985#endif
11986#ifdef RLIMIT_LOCKS
11987 "w"
11988#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011989 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011990 switch (optc) {
11991 case 'H':
11992 how = HARD;
11993 break;
11994 case 'S':
11995 how = SOFT;
11996 break;
11997 case 'a':
11998 all = 1;
11999 break;
12000 default:
12001 what = optc;
12002 }
12003
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012004 for (l = limits_tbl; l->option != what; l++)
12005 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012006
12007 set = *argptr ? 1 : 0;
12008 if (set) {
12009 char *p = *argptr;
12010
12011 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012012 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012013 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012014 val = RLIM_INFINITY;
12015 else {
12016 val = (rlim_t) 0;
12017
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012018 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012019 val = (val * 10) + (long)(c - '0');
12020 if (val < (rlim_t) 0)
12021 break;
12022 }
12023 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012024 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012025 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012026 }
12027 }
12028 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012029 const char *lname = limits_name;
12030 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012031 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012032 out1fmt("%-20s ", lname);
12033 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012034 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012035 }
12036 return 0;
12037 }
12038
12039 getrlimit(l->cmd, &limit);
12040 if (set) {
12041 if (how & HARD)
12042 limit.rlim_max = val;
12043 if (how & SOFT)
12044 limit.rlim_cur = val;
12045 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012046 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012047 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012048 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012049 }
12050 return 0;
12051}
12052
Eric Andersen90898442003-08-06 11:20:52 +000012053
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012054/* ============ Math support */
12055
Denis Vlasenko131ae172007-02-18 13:00:19 +000012056#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012057
12058/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12059
12060 Permission is hereby granted, free of charge, to any person obtaining
12061 a copy of this software and associated documentation files (the
12062 "Software"), to deal in the Software without restriction, including
12063 without limitation the rights to use, copy, modify, merge, publish,
12064 distribute, sublicense, and/or sell copies of the Software, and to
12065 permit persons to whom the Software is furnished to do so, subject to
12066 the following conditions:
12067
12068 The above copyright notice and this permission notice shall be
12069 included in all copies or substantial portions of the Software.
12070
12071 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12072 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12073 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12074 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12075 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12076 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12077 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12078*/
12079
12080/* This is my infix parser/evaluator. It is optimized for size, intended
12081 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012082 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012083 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012084 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012085 * be that which POSIX specifies for shells. */
12086
12087/* The code uses a simple two-stack algorithm. See
12088 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012089 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012090 * this is based (this code differs in that it applies operators immediately
12091 * to the stack instead of adding them to a queue to end up with an
12092 * expression). */
12093
12094/* To use the routine, call it with an expression string and error return
12095 * pointer */
12096
12097/*
12098 * Aug 24, 2001 Manuel Novoa III
12099 *
12100 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12101 *
12102 * 1) In arith_apply():
12103 * a) Cached values of *numptr and &(numptr[-1]).
12104 * b) Removed redundant test for zero denominator.
12105 *
12106 * 2) In arith():
12107 * a) Eliminated redundant code for processing operator tokens by moving
12108 * to a table-based implementation. Also folded handling of parens
12109 * into the table.
12110 * b) Combined all 3 loops which called arith_apply to reduce generated
12111 * code size at the cost of speed.
12112 *
12113 * 3) The following expressions were treated as valid by the original code:
12114 * 1() , 0! , 1 ( *3 ) .
12115 * These bugs have been fixed by internally enclosing the expression in
12116 * parens and then checking that all binary ops and right parens are
12117 * preceded by a valid expression (NUM_TOKEN).
12118 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012119 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012120 * ctype's isspace() if it is used by another busybox applet or if additional
12121 * whitespace chars should be considered. Look below the "#include"s for a
12122 * precompiler test.
12123 */
12124
12125/*
12126 * Aug 26, 2001 Manuel Novoa III
12127 *
12128 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12129 *
12130 * Merge in Aaron's comments previously posted to the busybox list,
12131 * modified slightly to take account of my changes to the code.
12132 *
12133 */
12134
12135/*
12136 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12137 *
12138 * - allow access to variable,
12139 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12140 * - realize assign syntax (VAR=expr, +=, *= etc)
12141 * - realize exponentiation (** operator)
12142 * - realize comma separated - expr, expr
12143 * - realise ++expr --expr expr++ expr--
12144 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012145 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012146 * - was restored loses XOR operator
12147 * - remove one goto label, added three ;-)
12148 * - protect $((num num)) as true zero expr (Manuel`s error)
12149 * - always use special isspace(), see comment from bash ;-)
12150 */
12151
Eric Andersen90898442003-08-06 11:20:52 +000012152#define arith_isspace(arithval) \
12153 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12154
Eric Andersen90898442003-08-06 11:20:52 +000012155typedef unsigned char operator;
12156
12157/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012158 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012159 * precedence. The ID portion is so that multiple operators can have the
12160 * same precedence, ensuring that the leftmost one is evaluated first.
12161 * Consider * and /. */
12162
12163#define tok_decl(prec,id) (((id)<<5)|(prec))
12164#define PREC(op) ((op) & 0x1F)
12165
12166#define TOK_LPAREN tok_decl(0,0)
12167
12168#define TOK_COMMA tok_decl(1,0)
12169
12170#define TOK_ASSIGN tok_decl(2,0)
12171#define TOK_AND_ASSIGN tok_decl(2,1)
12172#define TOK_OR_ASSIGN tok_decl(2,2)
12173#define TOK_XOR_ASSIGN tok_decl(2,3)
12174#define TOK_PLUS_ASSIGN tok_decl(2,4)
12175#define TOK_MINUS_ASSIGN tok_decl(2,5)
12176#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12177#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12178
12179#define TOK_MUL_ASSIGN tok_decl(3,0)
12180#define TOK_DIV_ASSIGN tok_decl(3,1)
12181#define TOK_REM_ASSIGN tok_decl(3,2)
12182
12183/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012184#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012185
12186/* conditional is right associativity too */
12187#define TOK_CONDITIONAL tok_decl(4,0)
12188#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12189
12190#define TOK_OR tok_decl(5,0)
12191
12192#define TOK_AND tok_decl(6,0)
12193
12194#define TOK_BOR tok_decl(7,0)
12195
12196#define TOK_BXOR tok_decl(8,0)
12197
12198#define TOK_BAND tok_decl(9,0)
12199
12200#define TOK_EQ tok_decl(10,0)
12201#define TOK_NE tok_decl(10,1)
12202
12203#define TOK_LT tok_decl(11,0)
12204#define TOK_GT tok_decl(11,1)
12205#define TOK_GE tok_decl(11,2)
12206#define TOK_LE tok_decl(11,3)
12207
12208#define TOK_LSHIFT tok_decl(12,0)
12209#define TOK_RSHIFT tok_decl(12,1)
12210
12211#define TOK_ADD tok_decl(13,0)
12212#define TOK_SUB tok_decl(13,1)
12213
12214#define TOK_MUL tok_decl(14,0)
12215#define TOK_DIV tok_decl(14,1)
12216#define TOK_REM tok_decl(14,2)
12217
12218/* exponent is right associativity */
12219#define TOK_EXPONENT tok_decl(15,1)
12220
12221/* For now unary operators. */
12222#define UNARYPREC 16
12223#define TOK_BNOT tok_decl(UNARYPREC,0)
12224#define TOK_NOT tok_decl(UNARYPREC,1)
12225
12226#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12227#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12228
12229#define PREC_PRE (UNARYPREC+2)
12230
12231#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12232#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12233
12234#define PREC_POST (UNARYPREC+3)
12235
12236#define TOK_POST_INC tok_decl(PREC_POST, 0)
12237#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12238
12239#define SPEC_PREC (UNARYPREC+4)
12240
12241#define TOK_NUM tok_decl(SPEC_PREC, 0)
12242#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12243
12244#define NUMPTR (*numstackptr)
12245
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012246static int
12247tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012248{
12249 operator prec = PREC(op);
12250
12251 convert_prec_is_assing(prec);
12252 return (prec == PREC(TOK_ASSIGN) ||
12253 prec == PREC_PRE || prec == PREC_POST);
12254}
12255
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012256static int
12257is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012258{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012259 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12260 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012261}
12262
Eric Andersen90898442003-08-06 11:20:52 +000012263typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012264 arith_t val;
12265 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012266 char contidional_second_val_initialized;
12267 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012268 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012269} v_n_t;
12270
Eric Andersen90898442003-08-06 11:20:52 +000012271typedef struct CHK_VAR_RECURSIVE_LOOPED {
12272 const char *var;
12273 struct CHK_VAR_RECURSIVE_LOOPED *next;
12274} chk_var_recursive_looped_t;
12275
12276static chk_var_recursive_looped_t *prev_chk_var_recursive;
12277
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012278static int
12279arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012280{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012281 if (t->var) {
12282 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012283
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012284 if (p) {
12285 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012286
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012287 /* recursive try as expression */
12288 chk_var_recursive_looped_t *cur;
12289 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012290
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012291 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12292 if (strcmp(cur->var, t->var) == 0) {
12293 /* expression recursion loop detected */
12294 return -5;
12295 }
12296 }
12297 /* save current lookuped var name */
12298 cur = prev_chk_var_recursive;
12299 cur_save.var = t->var;
12300 cur_save.next = cur;
12301 prev_chk_var_recursive = &cur_save;
12302
12303 t->val = arith (p, &errcode);
12304 /* restore previous ptr after recursiving */
12305 prev_chk_var_recursive = cur;
12306 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012307 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012308 /* allow undefined var as 0 */
12309 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012310 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012311 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012312}
12313
12314/* "applying" a token means performing it on the top elements on the integer
12315 * stack. For a unary operator it will only change the top element, but a
12316 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012317static int
12318arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012319{
Eric Andersen90898442003-08-06 11:20:52 +000012320 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012321 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012322 int ret_arith_lookup_val;
12323
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012324 /* There is no operator that can work without arguments */
12325 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012326 numptr_m1 = NUMPTR - 1;
12327
12328 /* check operand is var with noninteger value */
12329 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012330 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012331 return ret_arith_lookup_val;
12332
12333 rez = numptr_m1->val;
12334 if (op == TOK_UMINUS)
12335 rez *= -1;
12336 else if (op == TOK_NOT)
12337 rez = !rez;
12338 else if (op == TOK_BNOT)
12339 rez = ~rez;
12340 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12341 rez++;
12342 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12343 rez--;
12344 else if (op != TOK_UPLUS) {
12345 /* Binary operators */
12346
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012347 /* check and binary operators need two arguments */
12348 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012349
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012350 /* ... and they pop one */
12351 --NUMPTR;
12352 numptr_val = rez;
12353 if (op == TOK_CONDITIONAL) {
12354 if (! numptr_m1->contidional_second_val_initialized) {
12355 /* protect $((expr1 ? expr2)) without ": expr" */
12356 goto err;
12357 }
12358 rez = numptr_m1->contidional_second_val;
12359 } else if (numptr_m1->contidional_second_val_initialized) {
12360 /* protect $((expr1 : expr2)) without "expr ? " */
12361 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012362 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012363 numptr_m1 = NUMPTR - 1;
12364 if (op != TOK_ASSIGN) {
12365 /* check operand is var with noninteger value for not '=' */
12366 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12367 if (ret_arith_lookup_val)
12368 return ret_arith_lookup_val;
12369 }
12370 if (op == TOK_CONDITIONAL) {
12371 numptr_m1->contidional_second_val = rez;
12372 }
12373 rez = numptr_m1->val;
12374 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012375 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012377 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012378 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012379 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012381 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012382 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012383 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012384 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012385 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012387 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012388 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012389 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012390 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012391 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012392 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012393 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012394 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012395 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012397 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012399 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012400 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012401 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012403 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012405 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012407 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012408 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012409 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012410 /* protect $((expr : expr)) without "expr ? " */
12411 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012412 }
12413 numptr_m1->contidional_second_val_initialized = op;
12414 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012415 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012416 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012417 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012418 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012419 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012420 return -3; /* exponent less than 0 */
12421 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012422 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012423
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012424 if (numptr_val)
12425 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012426 c *= rez;
12427 rez = c;
12428 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012429 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012430 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012431 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012432 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012433 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012434 rez %= numptr_val;
12435 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012436 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012437 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012438
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012440 /* Hmm, 1=2 ? */
12441 goto err;
12442 }
12443 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012444#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012445 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012446#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012447 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012448#endif
Eric Andersen90898442003-08-06 11:20:52 +000012449 setvar(numptr_m1->var, buf, 0);
12450 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012452 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012454 rez++;
12455 }
12456 numptr_m1->val = rez;
12457 /* protect geting var value, is number now */
12458 numptr_m1->var = NULL;
12459 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012460 err:
12461 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012462}
12463
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012464/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012465static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012466 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12467 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12468 '<','<', 0, TOK_LSHIFT,
12469 '>','>', 0, TOK_RSHIFT,
12470 '|','|', 0, TOK_OR,
12471 '&','&', 0, TOK_AND,
12472 '!','=', 0, TOK_NE,
12473 '<','=', 0, TOK_LE,
12474 '>','=', 0, TOK_GE,
12475 '=','=', 0, TOK_EQ,
12476 '|','=', 0, TOK_OR_ASSIGN,
12477 '&','=', 0, TOK_AND_ASSIGN,
12478 '*','=', 0, TOK_MUL_ASSIGN,
12479 '/','=', 0, TOK_DIV_ASSIGN,
12480 '%','=', 0, TOK_REM_ASSIGN,
12481 '+','=', 0, TOK_PLUS_ASSIGN,
12482 '-','=', 0, TOK_MINUS_ASSIGN,
12483 '-','-', 0, TOK_POST_DEC,
12484 '^','=', 0, TOK_XOR_ASSIGN,
12485 '+','+', 0, TOK_POST_INC,
12486 '*','*', 0, TOK_EXPONENT,
12487 '!', 0, TOK_NOT,
12488 '<', 0, TOK_LT,
12489 '>', 0, TOK_GT,
12490 '=', 0, TOK_ASSIGN,
12491 '|', 0, TOK_BOR,
12492 '&', 0, TOK_BAND,
12493 '*', 0, TOK_MUL,
12494 '/', 0, TOK_DIV,
12495 '%', 0, TOK_REM,
12496 '+', 0, TOK_ADD,
12497 '-', 0, TOK_SUB,
12498 '^', 0, TOK_BXOR,
12499 /* uniq */
12500 '~', 0, TOK_BNOT,
12501 ',', 0, TOK_COMMA,
12502 '?', 0, TOK_CONDITIONAL,
12503 ':', 0, TOK_CONDITIONAL_SEP,
12504 ')', 0, TOK_RPAREN,
12505 '(', 0, TOK_LPAREN,
12506 0
12507};
12508/* ptr to ")" */
12509#define endexpression &op_tokens[sizeof(op_tokens)-7]
12510
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012511static arith_t
12512arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012513{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012514 char arithval; /* Current character under analysis */
12515 operator lasttok, op;
12516 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012517
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012518 const char *p = endexpression;
12519 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012520
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012521 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012522
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 /* Stack of integers */
12524 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12525 * in any given correct or incorrect expression is left as an exercise to
12526 * the reader. */
12527 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12528 *numstackptr = numstack;
12529 /* Stack of operator tokens */
12530 operator *stack = alloca((datasizes) * sizeof(operator)),
12531 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012532
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012533 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12534 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012535
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012536 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012537 arithval = *expr;
12538 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012539 if (p == endexpression) {
12540 /* Null expression. */
12541 return 0;
12542 }
12543
12544 /* This is only reached after all tokens have been extracted from the
12545 * input stream. If there are still tokens on the operator stack, they
12546 * are to be applied in order. At the end, there should be a final
12547 * result on the integer stack */
12548
12549 if (expr != endexpression + 1) {
12550 /* If we haven't done so already, */
12551 /* append a closing right paren */
12552 expr = endexpression;
12553 /* and let the loop process it. */
12554 continue;
12555 }
12556 /* At this point, we're done with the expression. */
12557 if (numstackptr != numstack+1) {
12558 /* ... but if there isn't, it's bad */
12559 err:
12560 return (*perrcode = -1);
12561 }
12562 if (numstack->var) {
12563 /* expression is $((var)) only, lookup now */
12564 errcode = arith_lookup_val(numstack);
12565 }
12566 ret:
12567 *perrcode = errcode;
12568 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012569 }
12570
Eric Andersen90898442003-08-06 11:20:52 +000012571 /* Continue processing the expression. */
12572 if (arith_isspace(arithval)) {
12573 /* Skip whitespace */
12574 goto prologue;
12575 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012576 p = endofname(expr);
12577 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012578 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012579
12580 numstackptr->var = alloca(var_name_size);
12581 safe_strncpy(numstackptr->var, expr, var_name_size);
12582 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012583 num:
Eric Andersen90898442003-08-06 11:20:52 +000012584 numstackptr->contidional_second_val_initialized = 0;
12585 numstackptr++;
12586 lasttok = TOK_NUM;
12587 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012588 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012589 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012590 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012591#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012592 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012593#else
12594 numstackptr->val = strtol(expr, (char **) &expr, 0);
12595#endif
Eric Andersen90898442003-08-06 11:20:52 +000012596 goto num;
12597 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012598 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012599 const char *o;
12600
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012601 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012602 /* strange operator not found */
12603 goto err;
12604 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012605 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012606 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012607 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012608 /* found */
12609 expr = o - 1;
12610 break;
12611 }
12612 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012613 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012614 p++;
12615 /* skip zero delim */
12616 p++;
12617 }
12618 op = p[1];
12619
12620 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012621 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12622 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012623
12624 /* Plus and minus are binary (not unary) _only_ if the last
12625 * token was as number, or a right paren (which pretends to be
12626 * a number, since it evaluates to one). Think about it.
12627 * It makes sense. */
12628 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012629 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012630 case TOK_ADD:
12631 op = TOK_UPLUS;
12632 break;
12633 case TOK_SUB:
12634 op = TOK_UMINUS;
12635 break;
12636 case TOK_POST_INC:
12637 op = TOK_PRE_INC;
12638 break;
12639 case TOK_POST_DEC:
12640 op = TOK_PRE_DEC;
12641 break;
Eric Andersen90898442003-08-06 11:20:52 +000012642 }
12643 }
12644 /* We don't want a unary operator to cause recursive descent on the
12645 * stack, because there can be many in a row and it could cause an
12646 * operator to be evaluated before its argument is pushed onto the
12647 * integer stack. */
12648 /* But for binary operators, "apply" everything on the operator
12649 * stack until we find an operator with a lesser priority than the
12650 * one we have just extracted. */
12651 /* Left paren is given the lowest priority so it will never be
12652 * "applied" in this way.
12653 * if associativity is right and priority eq, applied also skip
12654 */
12655 prec = PREC(op);
12656 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12657 /* not left paren or unary */
12658 if (lasttok != TOK_NUM) {
12659 /* binary op must be preceded by a num */
12660 goto err;
12661 }
12662 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012663 if (op == TOK_RPAREN) {
12664 /* The algorithm employed here is simple: while we don't
12665 * hit an open paren nor the bottom of the stack, pop
12666 * tokens and apply them */
12667 if (stackptr[-1] == TOK_LPAREN) {
12668 --stackptr;
12669 /* Any operator directly after a */
12670 lasttok = TOK_NUM;
12671 /* close paren should consider itself binary */
12672 goto prologue;
12673 }
12674 } else {
12675 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012676
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012677 convert_prec_is_assing(prec);
12678 convert_prec_is_assing(prev_prec);
12679 if (prev_prec < prec)
12680 break;
12681 /* check right assoc */
12682 if (prev_prec == prec && is_right_associativity(prec))
12683 break;
12684 }
12685 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12686 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012687 }
12688 if (op == TOK_RPAREN) {
12689 goto err;
12690 }
12691 }
12692
12693 /* Push this operator to the stack and remember it. */
12694 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012695 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012696 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012697 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012698}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012699#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012700
12701
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012702/* ============ main() and helpers */
12703
12704/*
12705 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012706 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012707static void exitshell(void) ATTRIBUTE_NORETURN;
12708static void
12709exitshell(void)
12710{
12711 struct jmploc loc;
12712 char *p;
12713 int status;
12714
12715 status = exitstatus;
12716 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12717 if (setjmp(loc.loc)) {
12718 if (exception == EXEXIT)
12719/* dash bug: it just does _exit(exitstatus) here
12720 * but we have to do setjobctl(0) first!
12721 * (bug is still not fixed in dash-0.5.3 - if you run dash
12722 * under Midnight Commander, on exit from dash MC is backgrounded) */
12723 status = exitstatus;
12724 goto out;
12725 }
12726 exception_handler = &loc;
12727 p = trap[0];
12728 if (p) {
12729 trap[0] = NULL;
12730 evalstring(p, 0);
12731 }
12732 flush_stdout_stderr();
12733 out:
12734 setjobctl(0);
12735 _exit(status);
12736 /* NOTREACHED */
12737}
12738
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012739static void
12740init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012741{
12742 /* from input.c: */
12743 basepf.nextc = basepf.buf = basebuf;
12744
12745 /* from trap.c: */
12746 signal(SIGCHLD, SIG_DFL);
12747
12748 /* from var.c: */
12749 {
12750 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012751 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012752 const char *p;
12753 struct stat st1, st2;
12754
12755 initvar();
12756 for (envp = environ; envp && *envp; envp++) {
12757 if (strchr(*envp, '=')) {
12758 setvareq(*envp, VEXPORT|VTEXTFIXED);
12759 }
12760 }
12761
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012762 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012763 setvar("PPID", ppid, 0);
12764
12765 p = lookupvar("PWD");
12766 if (p)
12767 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12768 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12769 p = '\0';
12770 setpwd(p, 0);
12771 }
12772}
12773
12774/*
12775 * Process the shell command line arguments.
12776 */
12777static void
12778procargs(int argc, char **argv)
12779{
12780 int i;
12781 const char *xminusc;
12782 char **xargv;
12783
12784 xargv = argv;
12785 arg0 = xargv[0];
12786 if (argc > 0)
12787 xargv++;
12788 for (i = 0; i < NOPTS; i++)
12789 optlist[i] = 2;
12790 argptr = xargv;
12791 options(1);
12792 xargv = argptr;
12793 xminusc = minusc;
12794 if (*xargv == NULL) {
12795 if (xminusc)
12796 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12797 sflag = 1;
12798 }
12799 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12800 iflag = 1;
12801 if (mflag == 2)
12802 mflag = iflag;
12803 for (i = 0; i < NOPTS; i++)
12804 if (optlist[i] == 2)
12805 optlist[i] = 0;
12806#if DEBUG == 2
12807 debug = 1;
12808#endif
12809 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12810 if (xminusc) {
12811 minusc = *xargv++;
12812 if (*xargv)
12813 goto setarg0;
12814 } else if (!sflag) {
12815 setinputfile(*xargv, 0);
12816 setarg0:
12817 arg0 = *xargv++;
12818 commandname = arg0;
12819 }
12820
12821 shellparam.p = xargv;
12822#if ENABLE_ASH_GETOPTS
12823 shellparam.optind = 1;
12824 shellparam.optoff = -1;
12825#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012826 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012827 while (*xargv) {
12828 shellparam.nparam++;
12829 xargv++;
12830 }
12831 optschanged();
12832}
12833
12834/*
12835 * Read /etc/profile or .profile.
12836 */
12837static void
12838read_profile(const char *name)
12839{
12840 int skip;
12841
12842 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12843 return;
12844 skip = cmdloop(0);
12845 popfile();
12846 if (skip)
12847 exitshell();
12848}
12849
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012850/*
12851 * This routine is called when an error or an interrupt occurs in an
12852 * interactive shell and control is returned to the main command loop.
12853 */
12854static void
12855reset(void)
12856{
12857 /* from eval.c: */
12858 evalskip = 0;
12859 loopnest = 0;
12860 /* from input.c: */
12861 parselleft = parsenleft = 0; /* clear input buffer */
12862 popallfiles();
12863 /* from parser.c: */
12864 tokpushback = 0;
12865 checkkwd = 0;
12866 /* from redir.c: */
12867 clearredir(0);
12868}
12869
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012870#if PROFILE
12871static short profile_buf[16384];
12872extern int etext();
12873#endif
12874
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012875/*
12876 * Main routine. We initialize things, parse the arguments, execute
12877 * profiles if we're a login shell, and then call cmdloop to execute
12878 * commands. The setjmp call sets up the location to jump to when an
12879 * exception occurs. When an exception occurs the variable "state"
12880 * is used to figure out how far we had gotten.
12881 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012882int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012883int ash_main(int argc, char **argv)
12884{
12885 char *shinit;
12886 volatile int state;
12887 struct jmploc jmploc;
12888 struct stackmark smark;
12889
Denis Vlasenko01631112007-12-16 17:20:38 +000012890 /* Initialize global data */
12891 INIT_G_misc();
12892 INIT_G_memstack();
12893 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012894#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012895 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012896#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012897 INIT_G_cmdtable();
12898
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012899#if PROFILE
12900 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12901#endif
12902
12903#if ENABLE_FEATURE_EDITING
12904 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12905#endif
12906 state = 0;
12907 if (setjmp(jmploc.loc)) {
12908 int e;
12909 int s;
12910
12911 reset();
12912
12913 e = exception;
12914 if (e == EXERROR)
12915 exitstatus = 2;
12916 s = state;
12917 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12918 exitshell();
12919
12920 if (e == EXINT) {
12921 outcslow('\n', stderr);
12922 }
12923 popstackmark(&smark);
12924 FORCE_INT_ON; /* enable interrupts */
12925 if (s == 1)
12926 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012927 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012928 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012929 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012930 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012931 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932 }
12933 exception_handler = &jmploc;
12934#if DEBUG
12935 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012936 trace_puts("Shell args: ");
12937 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938#endif
12939 rootpid = getpid();
12940
12941#if ENABLE_ASH_RANDOM_SUPPORT
12942 rseed = rootpid + time(NULL);
12943#endif
12944 init();
12945 setstackmark(&smark);
12946 procargs(argc, argv);
12947#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12948 if (iflag) {
12949 const char *hp = lookupvar("HISTFILE");
12950
12951 if (hp == NULL) {
12952 hp = lookupvar("HOME");
12953 if (hp != NULL) {
12954 char *defhp = concat_path_file(hp, ".ash_history");
12955 setvar("HISTFILE", defhp, 0);
12956 free(defhp);
12957 }
12958 }
12959 }
12960#endif
12961 if (argv[0] && argv[0][0] == '-')
12962 isloginsh = 1;
12963 if (isloginsh) {
12964 state = 1;
12965 read_profile("/etc/profile");
12966 state1:
12967 state = 2;
12968 read_profile(".profile");
12969 }
12970 state2:
12971 state = 3;
12972 if (
12973#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012974 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012975#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012976 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012977 ) {
12978 shinit = lookupvar("ENV");
12979 if (shinit != NULL && *shinit != '\0') {
12980 read_profile(shinit);
12981 }
12982 }
12983 state3:
12984 state = 4;
12985 if (minusc)
12986 evalstring(minusc, 0);
12987
12988 if (sflag || minusc == NULL) {
12989#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12990 if ( iflag ) {
12991 const char *hp = lookupvar("HISTFILE");
12992
12993 if (hp != NULL)
12994 line_input_state->hist_file = hp;
12995 }
12996#endif
12997 state4: /* XXX ??? - why isn't this before the "if" statement */
12998 cmdloop(1);
12999 }
13000#if PROFILE
13001 monitor(0);
13002#endif
13003#ifdef GPROF
13004 {
13005 extern void _mcleanup(void);
13006 _mcleanup();
13007 }
13008#endif
13009 exitshell();
13010 /* NOTREACHED */
13011}
13012
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013013#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013014const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013015int main(int argc, char **argv)
13016{
13017 return ash_main(argc, argv);
13018}
13019#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013020
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013021
Eric Andersendf82f612001-06-28 07:46:40 +000013022/*-
13023 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013024 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013025 *
13026 * This code is derived from software contributed to Berkeley by
13027 * Kenneth Almquist.
13028 *
13029 * Redistribution and use in source and binary forms, with or without
13030 * modification, are permitted provided that the following conditions
13031 * are met:
13032 * 1. Redistributions of source code must retain the above copyright
13033 * notice, this list of conditions and the following disclaimer.
13034 * 2. Redistributions in binary form must reproduce the above copyright
13035 * notice, this list of conditions and the following disclaimer in the
13036 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013037 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013038 * may be used to endorse or promote products derived from this software
13039 * without specific prior written permission.
13040 *
13041 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13042 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13043 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13044 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13045 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13046 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13047 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13048 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13049 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13050 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13051 * SUCH DAMAGE.
13052 */