blob: 2b6133d20acc9e5e31277df5a658bad10a637733 [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 Andersenc470f442003-07-28 09:56:35 +000045#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000046
47#define IFS_BROKEN
48
49#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000050
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000052#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#define _GNU_SOURCE
54#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000055#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000056
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000057#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000061#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000062#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko01631112007-12-16 17:20:38 +000070/* ============ Hash table sizes. Configurable. */
71
72#define VTABSIZE 39
73#define ATABSIZE 39
74#define CMDTABLESIZE 31 /* should be prime */
75
76
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000077/* ============ Misc helpers */
78
79#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
80
81/* C99 say: "char" declaration may be signed or unsigned default */
82#define signed_char2int(sc) ((int)((signed char)sc))
83
84
Denis Vlasenkob012b102007-02-19 22:43:01 +000085/* ============ Shell options */
86
87static const char *const optletters_optnames[] = {
88 "e" "errexit",
89 "f" "noglob",
90 "I" "ignoreeof",
91 "i" "interactive",
92 "m" "monitor",
93 "n" "noexec",
94 "s" "stdin",
95 "x" "xtrace",
96 "v" "verbose",
97 "C" "noclobber",
98 "a" "allexport",
99 "b" "notify",
100 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000101 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000102#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 ,"\0" "nolog"
104 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000105#endif
106};
107
108#define optletters(n) optletters_optnames[(n)][0]
109#define optnames(n) (&optletters_optnames[(n)][1])
110
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000111enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000112
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114
115#define eflag optlist[0]
116#define fflag optlist[1]
117#define Iflag optlist[2]
118#define iflag optlist[3]
119#define mflag optlist[4]
120#define nflag optlist[5]
121#define sflag optlist[6]
122#define xflag optlist[7]
123#define vflag optlist[8]
124#define Cflag optlist[9]
125#define aflag optlist[10]
126#define bflag optlist[11]
127#define uflag optlist[12]
128#define viflag optlist[13]
129#if DEBUG
130#define nolog optlist[14]
131#define debug optlist[15]
132#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000133
134
Denis Vlasenkob012b102007-02-19 22:43:01 +0000135/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000136
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000137static const char homestr[] ALIGN1 = "HOME";
138static const char snlfmt[] ALIGN1 = "%s\n";
139static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000140
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000141/*
Eric Andersenc470f442003-07-28 09:56:35 +0000142 * We enclose jmp_buf in a structure so that we can declare pointers to
143 * jump locations. The global variable handler contains the location to
144 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000145 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000146 * exception handlers, the user should save the value of handler on entry
147 * to an inner scope, set handler to point to a jmploc structure for the
148 * inner scope, and restore handler on exit from the scope.
149 */
Eric Andersenc470f442003-07-28 09:56:35 +0000150struct jmploc {
151 jmp_buf loc;
152};
Denis Vlasenko01631112007-12-16 17:20:38 +0000153
154struct globals_misc {
155 /* pid of main shell */
156 int rootpid;
157 /* shell level: 0 for the main shell, 1 for its children, and so on */
158 int shlvl;
159#define rootshell (!shlvl)
160 char *minusc; /* argument to -c option */
161
162 char *curdir; // = nullstr; /* current working directory */
163 char *physdir; // = nullstr; /* physical working directory */
164
165 char *arg0; /* value of $0 */
166
167 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000168
169// disabled by vda: cannot understand how it was supposed to work -
170// cannot fix bugs. That's why you have to explain your non-trivial designs!
171// /* do we generate EXSIG events */
172// int exsig; /* counter */
173 volatile int suppressint; /* counter */
174 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
175 /* last pending signal */
176 volatile /*sig_atomic_t*/ smallint pendingsig;
177 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000178 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000179#define EXINT 0 /* SIGINT received */
180#define EXERROR 1 /* a generic error */
181#define EXSHELLPROC 2 /* execute a shell procedure */
182#define EXEXEC 3 /* command execution failed */
183#define EXEXIT 4 /* exit the shell */
184#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000185
Denis Vlasenko01631112007-12-16 17:20:38 +0000186 /* trap handler commands */
187 char *trap[NSIG];
188 smallint isloginsh;
189 char nullstr[1]; /* zero length string */
190 /*
191 * Sigmode records the current value of the signal handlers for the various
192 * modes. A value of zero means that the current handler is not known.
193 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
194 */
195 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000196#define S_DFL 1 /* default signal handling (SIG_DFL) */
197#define S_CATCH 2 /* signal is caught */
198#define S_IGN 3 /* signal is ignored (SIG_IGN) */
199#define S_HARD_IGN 4 /* signal is ignored permenantly */
200#define S_RESET 5 /* temporary - to reset a hard ignored sig */
201
Denis Vlasenko01631112007-12-16 17:20:38 +0000202 /* indicates specified signal received */
203 char gotsig[NSIG - 1];
204};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000205extern struct globals_misc *const ash_ptr_to_globals_misc;
206#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko01631112007-12-16 17:20:38 +0000207#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 { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000225 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
226 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000227 curdir = nullstr; \
228 physdir = nullstr; \
229} while (0)
230
231
232/* ============ Interrupts / exceptions */
233
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000234/*
Eric Andersen2870d962001-07-02 17:27:21 +0000235 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000236 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000237 * much more efficient and portable. (But hacking the kernel is so much
238 * more fun than worrying about efficiency and portability. :-))
239 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000240#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000241 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000242 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000243 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000244 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000245
246/*
247 * Called to raise an exception. Since C doesn't include exceptions, we
248 * just do a longjmp to the exception handler. The type of exception is
249 * stored in the global variable "exception".
250 */
251static void raise_exception(int) ATTRIBUTE_NORETURN;
252static void
253raise_exception(int e)
254{
255#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000256 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000257 abort();
258#endif
259 INT_OFF;
260 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000261 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000262}
263
264/*
265 * Called from trap.c when a SIGINT is received. (If the user specifies
266 * that SIGINT is to be trapped or ignored using the trap builtin, then
267 * this routine is not called.) Suppressint is nonzero when interrupts
268 * are held using the INT_OFF macro. (The test for iflag is just
269 * defensive programming.)
270 */
271static void raise_interrupt(void) ATTRIBUTE_NORETURN;
272static void
273raise_interrupt(void)
274{
275 int i;
276
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 Vlasenko3f165fa2008-03-17 08:29:08 +0000280 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000281 /* pendingsig = 0; - now done in onsig() */
282
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 i = EXSIG;
284 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
285 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000286 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287 signal(SIGINT, SIG_DFL);
288 raise(SIGINT);
289 }
290 i = EXINT;
291 }
292 raise_exception(i);
293 /* NOTREACHED */
294}
295
296#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000297static void
298int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299{
300 if (--suppressint == 0 && intpending) {
301 raise_interrupt();
302 }
303}
304#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000305static void
306force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307{
308 suppressint = 0;
309 if (intpending)
310 raise_interrupt();
311}
312#define FORCE_INT_ON force_int_on()
313#else
314#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000315 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000316 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000317 if (--suppressint == 0 && intpending) \
318 raise_interrupt(); \
319 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000320#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000321 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000322 xbarrier(); \
323 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000324 if (intpending) \
325 raise_interrupt(); \
326 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000327#endif /* ASH_OPTIMIZE_FOR_SIZE */
328
329#define SAVE_INT(v) ((v) = suppressint)
330
331#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000332 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000333 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000334 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000335 if (suppressint == 0 && intpending) \
336 raise_interrupt(); \
337 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000338
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000339/*
340 * Ignore a signal. Only one usage site - in forkchild()
341 */
342static void
343ignoresig(int signo)
344{
345 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
346 signal(signo, SIG_IGN);
347 }
348 sigmode[signo - 1] = S_HARD_IGN;
349}
350
351/*
352 * Signal handler. Only one usage site - in setsignal()
353 */
354static void
355onsig(int signo)
356{
357 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000358 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000359
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000360 if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000361 if (!suppressint) {
362 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000363 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000364 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000365 intpending = 1;
366 }
367}
368
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000369
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000370/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000371
Eric Andersenc470f442003-07-28 09:56:35 +0000372static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000373outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000374{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375 INT_OFF;
376 fputs(p, file);
377 INT_ON;
378}
379
380static void
381flush_stdout_stderr(void)
382{
383 INT_OFF;
384 fflush(stdout);
385 fflush(stderr);
386 INT_ON;
387}
388
389static void
390flush_stderr(void)
391{
392 INT_OFF;
393 fflush(stderr);
394 INT_ON;
395}
396
397static void
398outcslow(int c, FILE *dest)
399{
400 INT_OFF;
401 putc(c, dest);
402 fflush(dest);
403 INT_ON;
404}
405
406static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
407static int
408out1fmt(const char *fmt, ...)
409{
410 va_list ap;
411 int r;
412
413 INT_OFF;
414 va_start(ap, fmt);
415 r = vprintf(fmt, ap);
416 va_end(ap);
417 INT_ON;
418 return r;
419}
420
421static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
422static int
423fmtstr(char *outbuf, size_t length, const char *fmt, ...)
424{
425 va_list ap;
426 int ret;
427
428 va_start(ap, fmt);
429 INT_OFF;
430 ret = vsnprintf(outbuf, length, fmt, ap);
431 va_end(ap);
432 INT_ON;
433 return ret;
434}
435
436static void
437out1str(const char *p)
438{
439 outstr(p, stdout);
440}
441
442static void
443out2str(const char *p)
444{
445 outstr(p, stderr);
446 flush_stderr();
447}
448
449
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000450/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000451
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000452/* control characters in argument strings */
453#define CTLESC '\201' /* escape next character */
454#define CTLVAR '\202' /* variable defn */
455#define CTLENDVAR '\203'
456#define CTLBACKQ '\204'
457#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
458/* CTLBACKQ | CTLQUOTE == '\205' */
459#define CTLARI '\206' /* arithmetic expression */
460#define CTLENDARI '\207'
461#define CTLQUOTEMARK '\210'
462
463/* variable substitution byte (follows CTLVAR) */
464#define VSTYPE 0x0f /* type of variable substitution */
465#define VSNUL 0x10 /* colon--treat the empty string as unset */
466#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
467
468/* values of VSTYPE field */
469#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
470#define VSMINUS 0x2 /* ${var-text} */
471#define VSPLUS 0x3 /* ${var+text} */
472#define VSQUESTION 0x4 /* ${var?message} */
473#define VSASSIGN 0x5 /* ${var=text} */
474#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
475#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
476#define VSTRIMLEFT 0x8 /* ${var#pattern} */
477#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
478#define VSLENGTH 0xa /* ${#var} */
479
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000480static const char dolatstr[] ALIGN1 = {
481 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
482};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000483
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000484#define NCMD 0
485#define NPIPE 1
486#define NREDIR 2
487#define NBACKGND 3
488#define NSUBSHELL 4
489#define NAND 5
490#define NOR 6
491#define NSEMI 7
492#define NIF 8
493#define NWHILE 9
494#define NUNTIL 10
495#define NFOR 11
496#define NCASE 12
497#define NCLIST 13
498#define NDEFUN 14
499#define NARG 15
500#define NTO 16
501#define NCLOBBER 17
502#define NFROM 18
503#define NFROMTO 19
504#define NAPPEND 20
505#define NTOFD 21
506#define NFROMFD 22
507#define NHERE 23
508#define NXHERE 24
509#define NNOT 25
510
511union node;
512
513struct ncmd {
514 int type;
515 union node *assign;
516 union node *args;
517 union node *redirect;
518};
519
520struct npipe {
521 int type;
522 int backgnd;
523 struct nodelist *cmdlist;
524};
525
526struct nredir {
527 int type;
528 union node *n;
529 union node *redirect;
530};
531
532struct nbinary {
533 int type;
534 union node *ch1;
535 union node *ch2;
536};
537
538struct nif {
539 int type;
540 union node *test;
541 union node *ifpart;
542 union node *elsepart;
543};
544
545struct nfor {
546 int type;
547 union node *args;
548 union node *body;
549 char *var;
550};
551
552struct ncase {
553 int type;
554 union node *expr;
555 union node *cases;
556};
557
558struct nclist {
559 int type;
560 union node *next;
561 union node *pattern;
562 union node *body;
563};
564
565struct narg {
566 int type;
567 union node *next;
568 char *text;
569 struct nodelist *backquote;
570};
571
572struct nfile {
573 int type;
574 union node *next;
575 int fd;
576 union node *fname;
577 char *expfname;
578};
579
580struct ndup {
581 int type;
582 union node *next;
583 int fd;
584 int dupfd;
585 union node *vname;
586};
587
588struct nhere {
589 int type;
590 union node *next;
591 int fd;
592 union node *doc;
593};
594
595struct nnot {
596 int type;
597 union node *com;
598};
599
600union node {
601 int type;
602 struct ncmd ncmd;
603 struct npipe npipe;
604 struct nredir nredir;
605 struct nbinary nbinary;
606 struct nif nif;
607 struct nfor nfor;
608 struct ncase ncase;
609 struct nclist nclist;
610 struct narg narg;
611 struct nfile nfile;
612 struct ndup ndup;
613 struct nhere nhere;
614 struct nnot nnot;
615};
616
617struct nodelist {
618 struct nodelist *next;
619 union node *n;
620};
621
622struct funcnode {
623 int count;
624 union node n;
625};
626
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000627/*
628 * Free a parse tree.
629 */
630static void
631freefunc(struct funcnode *f)
632{
633 if (f && --f->count < 0)
634 free(f);
635}
636
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000637
638/* ============ Debugging output */
639
640#if DEBUG
641
642static FILE *tracefile;
643
644static void
645trace_printf(const char *fmt, ...)
646{
647 va_list va;
648
649 if (debug != 1)
650 return;
651 va_start(va, fmt);
652 vfprintf(tracefile, fmt, va);
653 va_end(va);
654}
655
656static void
657trace_vprintf(const char *fmt, va_list va)
658{
659 if (debug != 1)
660 return;
661 vfprintf(tracefile, fmt, va);
662}
663
664static void
665trace_puts(const char *s)
666{
667 if (debug != 1)
668 return;
669 fputs(s, tracefile);
670}
671
672static void
673trace_puts_quoted(char *s)
674{
675 char *p;
676 char c;
677
678 if (debug != 1)
679 return;
680 putc('"', tracefile);
681 for (p = s; *p; p++) {
682 switch (*p) {
683 case '\n': c = 'n'; goto backslash;
684 case '\t': c = 't'; goto backslash;
685 case '\r': c = 'r'; goto backslash;
686 case '"': c = '"'; goto backslash;
687 case '\\': c = '\\'; goto backslash;
688 case CTLESC: c = 'e'; goto backslash;
689 case CTLVAR: c = 'v'; goto backslash;
690 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
691 case CTLBACKQ: c = 'q'; goto backslash;
692 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
693 backslash:
694 putc('\\', tracefile);
695 putc(c, tracefile);
696 break;
697 default:
698 if (*p >= ' ' && *p <= '~')
699 putc(*p, tracefile);
700 else {
701 putc('\\', tracefile);
702 putc(*p >> 6 & 03, tracefile);
703 putc(*p >> 3 & 07, tracefile);
704 putc(*p & 07, tracefile);
705 }
706 break;
707 }
708 }
709 putc('"', tracefile);
710}
711
712static void
713trace_puts_args(char **ap)
714{
715 if (debug != 1)
716 return;
717 if (!*ap)
718 return;
719 while (1) {
720 trace_puts_quoted(*ap);
721 if (!*++ap) {
722 putc('\n', tracefile);
723 break;
724 }
725 putc(' ', tracefile);
726 }
727}
728
729static void
730opentrace(void)
731{
732 char s[100];
733#ifdef O_APPEND
734 int flags;
735#endif
736
737 if (debug != 1) {
738 if (tracefile)
739 fflush(tracefile);
740 /* leave open because libedit might be using it */
741 return;
742 }
743 strcpy(s, "./trace");
744 if (tracefile) {
745 if (!freopen(s, "a", tracefile)) {
746 fprintf(stderr, "Can't re-open %s\n", s);
747 debug = 0;
748 return;
749 }
750 } else {
751 tracefile = fopen(s, "a");
752 if (tracefile == NULL) {
753 fprintf(stderr, "Can't open %s\n", s);
754 debug = 0;
755 return;
756 }
757 }
758#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000759 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000760 if (flags >= 0)
761 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
762#endif
763 setlinebuf(tracefile);
764 fputs("\nTracing started.\n", tracefile);
765}
766
767static void
768indent(int amount, char *pfx, FILE *fp)
769{
770 int i;
771
772 for (i = 0; i < amount; i++) {
773 if (pfx && i == amount - 1)
774 fputs(pfx, fp);
775 putc('\t', fp);
776 }
777}
778
779/* little circular references here... */
780static void shtree(union node *n, int ind, char *pfx, FILE *fp);
781
782static void
783sharg(union node *arg, FILE *fp)
784{
785 char *p;
786 struct nodelist *bqlist;
787 int subtype;
788
789 if (arg->type != NARG) {
790 out1fmt("<node type %d>\n", arg->type);
791 abort();
792 }
793 bqlist = arg->narg.backquote;
794 for (p = arg->narg.text; *p; p++) {
795 switch (*p) {
796 case CTLESC:
797 putc(*++p, fp);
798 break;
799 case CTLVAR:
800 putc('$', fp);
801 putc('{', fp);
802 subtype = *++p;
803 if (subtype == VSLENGTH)
804 putc('#', fp);
805
806 while (*p != '=')
807 putc(*p++, fp);
808
809 if (subtype & VSNUL)
810 putc(':', fp);
811
812 switch (subtype & VSTYPE) {
813 case VSNORMAL:
814 putc('}', fp);
815 break;
816 case VSMINUS:
817 putc('-', fp);
818 break;
819 case VSPLUS:
820 putc('+', fp);
821 break;
822 case VSQUESTION:
823 putc('?', fp);
824 break;
825 case VSASSIGN:
826 putc('=', fp);
827 break;
828 case VSTRIMLEFT:
829 putc('#', fp);
830 break;
831 case VSTRIMLEFTMAX:
832 putc('#', fp);
833 putc('#', fp);
834 break;
835 case VSTRIMRIGHT:
836 putc('%', fp);
837 break;
838 case VSTRIMRIGHTMAX:
839 putc('%', fp);
840 putc('%', fp);
841 break;
842 case VSLENGTH:
843 break;
844 default:
845 out1fmt("<subtype %d>", subtype);
846 }
847 break;
848 case CTLENDVAR:
849 putc('}', fp);
850 break;
851 case CTLBACKQ:
852 case CTLBACKQ|CTLQUOTE:
853 putc('$', fp);
854 putc('(', fp);
855 shtree(bqlist->n, -1, NULL, fp);
856 putc(')', fp);
857 break;
858 default:
859 putc(*p, fp);
860 break;
861 }
862 }
863}
864
865static void
866shcmd(union node *cmd, FILE *fp)
867{
868 union node *np;
869 int first;
870 const char *s;
871 int dftfd;
872
873 first = 1;
874 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000875 if (!first)
876 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000877 sharg(np, fp);
878 first = 0;
879 }
880 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000881 if (!first)
882 putc(' ', fp);
883 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000884 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000885 case NTO: s = ">>"+1; dftfd = 1; break;
886 case NCLOBBER: s = ">|"; dftfd = 1; break;
887 case NAPPEND: s = ">>"; dftfd = 1; break;
888 case NTOFD: s = ">&"; dftfd = 1; break;
889 case NFROM: s = "<"; break;
890 case NFROMFD: s = "<&"; break;
891 case NFROMTO: s = "<>"; break;
892 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000893 }
894 if (np->nfile.fd != dftfd)
895 fprintf(fp, "%d", np->nfile.fd);
896 fputs(s, fp);
897 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
898 fprintf(fp, "%d", np->ndup.dupfd);
899 } else {
900 sharg(np->nfile.fname, fp);
901 }
902 first = 0;
903 }
904}
905
906static void
907shtree(union node *n, int ind, char *pfx, FILE *fp)
908{
909 struct nodelist *lp;
910 const char *s;
911
912 if (n == NULL)
913 return;
914
915 indent(ind, pfx, fp);
916 switch (n->type) {
917 case NSEMI:
918 s = "; ";
919 goto binop;
920 case NAND:
921 s = " && ";
922 goto binop;
923 case NOR:
924 s = " || ";
925 binop:
926 shtree(n->nbinary.ch1, ind, NULL, fp);
927 /* if (ind < 0) */
928 fputs(s, fp);
929 shtree(n->nbinary.ch2, ind, NULL, fp);
930 break;
931 case NCMD:
932 shcmd(n, fp);
933 if (ind >= 0)
934 putc('\n', fp);
935 break;
936 case NPIPE:
937 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
938 shcmd(lp->n, fp);
939 if (lp->next)
940 fputs(" | ", fp);
941 }
942 if (n->npipe.backgnd)
943 fputs(" &", fp);
944 if (ind >= 0)
945 putc('\n', fp);
946 break;
947 default:
948 fprintf(fp, "<node type %d>", n->type);
949 if (ind >= 0)
950 putc('\n', fp);
951 break;
952 }
953}
954
955static void
956showtree(union node *n)
957{
958 trace_puts("showtree called\n");
959 shtree(n, 1, NULL, stdout);
960}
961
962#define TRACE(param) trace_printf param
963#define TRACEV(param) trace_vprintf param
964
965#else
966
967#define TRACE(param)
968#define TRACEV(param)
969
970#endif /* DEBUG */
971
972
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000973/* ============ Parser data */
974
975/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000976 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
977 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000978struct strlist {
979 struct strlist *next;
980 char *text;
981};
982
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000983#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000984struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000985#endif
986
Denis Vlasenkob012b102007-02-19 22:43:01 +0000987struct strpush {
988 struct strpush *prev; /* preceding string on stack */
989 char *prevstring;
990 int prevnleft;
991#if ENABLE_ASH_ALIAS
992 struct alias *ap; /* if push was associated with an alias */
993#endif
994 char *string; /* remember the string since it may change */
995};
996
997struct parsefile {
998 struct parsefile *prev; /* preceding file on stack */
999 int linno; /* current line */
1000 int fd; /* file descriptor (or -1 if string) */
1001 int nleft; /* number of chars left in this line */
1002 int lleft; /* number of chars left in this buffer */
1003 char *nextc; /* next char in buffer */
1004 char *buf; /* input buffer */
1005 struct strpush *strpush; /* for pushing strings at this level */
1006 struct strpush basestrpush; /* so pushing one is fast */
1007};
1008
1009static struct parsefile basepf; /* top level input file */
1010static struct parsefile *parsefile = &basepf; /* current input file */
1011static int startlinno; /* line # where last token started */
1012static char *commandname; /* currently executing command */
1013static struct strlist *cmdenviron; /* environment for builtin command */
1014static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001015
1016
1017/* ============ Message printing */
1018
1019static void
1020ash_vmsg(const char *msg, va_list ap)
1021{
1022 fprintf(stderr, "%s: ", arg0);
1023 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001024 if (strcmp(arg0, commandname))
1025 fprintf(stderr, "%s: ", commandname);
1026 if (!iflag || parsefile->fd)
1027 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001028 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001029 vfprintf(stderr, msg, ap);
1030 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001031}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001032
1033/*
1034 * Exverror is called to raise the error exception. If the second argument
1035 * is not NULL then error prints an error message using printf style
1036 * formatting. It then raises the error exception.
1037 */
1038static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1039static void
1040ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001041{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001042#if DEBUG
1043 if (msg) {
1044 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1045 TRACEV((msg, ap));
1046 TRACE(("\") pid=%d\n", getpid()));
1047 } else
1048 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1049 if (msg)
1050#endif
1051 ash_vmsg(msg, ap);
1052
1053 flush_stdout_stderr();
1054 raise_exception(cond);
1055 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001056}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001057
1058static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1059static void
1060ash_msg_and_raise_error(const char *msg, ...)
1061{
1062 va_list ap;
1063
1064 va_start(ap, msg);
1065 ash_vmsg_and_raise(EXERROR, msg, ap);
1066 /* NOTREACHED */
1067 va_end(ap);
1068}
1069
1070static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1071static void
1072ash_msg_and_raise(int cond, const char *msg, ...)
1073{
1074 va_list ap;
1075
1076 va_start(ap, msg);
1077 ash_vmsg_and_raise(cond, msg, ap);
1078 /* NOTREACHED */
1079 va_end(ap);
1080}
1081
1082/*
1083 * error/warning routines for external builtins
1084 */
1085static void
1086ash_msg(const char *fmt, ...)
1087{
1088 va_list ap;
1089
1090 va_start(ap, fmt);
1091 ash_vmsg(fmt, ap);
1092 va_end(ap);
1093}
1094
1095/*
1096 * Return a string describing an error. The returned string may be a
1097 * pointer to a static buffer that will be overwritten on the next call.
1098 * Action describes the operation that got the error.
1099 */
1100static const char *
1101errmsg(int e, const char *em)
1102{
1103 if (e == ENOENT || e == ENOTDIR) {
1104 return em;
1105 }
1106 return strerror(e);
1107}
1108
1109
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001110/* ============ Memory allocation */
1111
1112/*
1113 * It appears that grabstackstr() will barf with such alignments
1114 * because stalloc() will return a string allocated in a new stackblock.
1115 */
1116#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1117enum {
1118 /* Most machines require the value returned from malloc to be aligned
1119 * in some way. The following macro will get this right
1120 * on many machines. */
1121 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1122 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001123 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001124};
1125
1126struct stack_block {
1127 struct stack_block *prev;
1128 char space[MINSIZE];
1129};
1130
1131struct stackmark {
1132 struct stack_block *stackp;
1133 char *stacknxt;
1134 size_t stacknleft;
1135 struct stackmark *marknext;
1136};
1137
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001138
Denis Vlasenko01631112007-12-16 17:20:38 +00001139struct globals_memstack {
1140 struct stack_block *g_stackp; // = &stackbase;
1141 struct stackmark *markp;
1142 char *g_stacknxt; // = stackbase.space;
1143 char *sstrend; // = stackbase.space + MINSIZE;
1144 size_t g_stacknleft; // = MINSIZE;
1145 int herefd; // = -1;
1146 struct stack_block stackbase;
1147};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001148extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1149#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001150#define g_stackp (G_memstack.g_stackp )
1151#define markp (G_memstack.markp )
1152#define g_stacknxt (G_memstack.g_stacknxt )
1153#define sstrend (G_memstack.sstrend )
1154#define g_stacknleft (G_memstack.g_stacknleft)
1155#define herefd (G_memstack.herefd )
1156#define stackbase (G_memstack.stackbase )
1157#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001158 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1159 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001160 g_stackp = &stackbase; \
1161 g_stacknxt = stackbase.space; \
1162 g_stacknleft = MINSIZE; \
1163 sstrend = stackbase.space + MINSIZE; \
1164 herefd = -1; \
1165} while (0)
1166
1167#define stackblock() ((void *)g_stacknxt)
1168#define stackblocksize() g_stacknleft
1169
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001170
1171static void *
1172ckrealloc(void * p, size_t nbytes)
1173{
1174 p = realloc(p, nbytes);
1175 if (!p)
1176 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1177 return p;
1178}
1179
1180static void *
1181ckmalloc(size_t nbytes)
1182{
1183 return ckrealloc(NULL, nbytes);
1184}
1185
Denis Vlasenko597906c2008-02-20 16:38:54 +00001186static void *
1187ckzalloc(size_t nbytes)
1188{
1189 return memset(ckmalloc(nbytes), 0, nbytes);
1190}
1191
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001192/*
1193 * Make a copy of a string in safe storage.
1194 */
1195static char *
1196ckstrdup(const char *s)
1197{
1198 char *p = strdup(s);
1199 if (!p)
1200 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1201 return p;
1202}
1203
1204/*
1205 * Parse trees for commands are allocated in lifo order, so we use a stack
1206 * to make this more efficient, and also to avoid all sorts of exception
1207 * handling code to handle interrupts in the middle of a parse.
1208 *
1209 * The size 504 was chosen because the Ultrix malloc handles that size
1210 * well.
1211 */
1212static void *
1213stalloc(size_t nbytes)
1214{
1215 char *p;
1216 size_t aligned;
1217
1218 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001219 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001220 size_t len;
1221 size_t blocksize;
1222 struct stack_block *sp;
1223
1224 blocksize = aligned;
1225 if (blocksize < MINSIZE)
1226 blocksize = MINSIZE;
1227 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1228 if (len < blocksize)
1229 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1230 INT_OFF;
1231 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001232 sp->prev = g_stackp;
1233 g_stacknxt = sp->space;
1234 g_stacknleft = blocksize;
1235 sstrend = g_stacknxt + blocksize;
1236 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001237 INT_ON;
1238 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001239 p = g_stacknxt;
1240 g_stacknxt += aligned;
1241 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001242 return p;
1243}
1244
Denis Vlasenko597906c2008-02-20 16:38:54 +00001245static void *
1246stzalloc(size_t nbytes)
1247{
1248 return memset(stalloc(nbytes), 0, nbytes);
1249}
1250
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001251static void
1252stunalloc(void *p)
1253{
1254#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001255 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256 write(2, "stunalloc\n", 10);
1257 abort();
1258 }
1259#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001260 g_stacknleft += g_stacknxt - (char *)p;
1261 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001262}
1263
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001264/*
1265 * Like strdup but works with the ash stack.
1266 */
1267static char *
1268ststrdup(const char *p)
1269{
1270 size_t len = strlen(p) + 1;
1271 return memcpy(stalloc(len), p, len);
1272}
1273
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274static void
1275setstackmark(struct stackmark *mark)
1276{
Denis Vlasenko01631112007-12-16 17:20:38 +00001277 mark->stackp = g_stackp;
1278 mark->stacknxt = g_stacknxt;
1279 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001280 mark->marknext = markp;
1281 markp = mark;
1282}
1283
1284static void
1285popstackmark(struct stackmark *mark)
1286{
1287 struct stack_block *sp;
1288
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001289 if (!mark->stackp)
1290 return;
1291
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001292 INT_OFF;
1293 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001294 while (g_stackp != mark->stackp) {
1295 sp = g_stackp;
1296 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297 free(sp);
1298 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001299 g_stacknxt = mark->stacknxt;
1300 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001301 sstrend = mark->stacknxt + mark->stacknleft;
1302 INT_ON;
1303}
1304
1305/*
1306 * When the parser reads in a string, it wants to stick the string on the
1307 * stack and only adjust the stack pointer when it knows how big the
1308 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1309 * of space on top of the stack and stackblocklen returns the length of
1310 * this block. Growstackblock will grow this space by at least one byte,
1311 * possibly moving it (like realloc). Grabstackblock actually allocates the
1312 * part of the block that has been used.
1313 */
1314static void
1315growstackblock(void)
1316{
1317 size_t newlen;
1318
Denis Vlasenko01631112007-12-16 17:20:38 +00001319 newlen = g_stacknleft * 2;
1320 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1322 if (newlen < 128)
1323 newlen += 128;
1324
Denis Vlasenko01631112007-12-16 17:20:38 +00001325 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 struct stack_block *oldstackp;
1327 struct stackmark *xmark;
1328 struct stack_block *sp;
1329 struct stack_block *prevstackp;
1330 size_t grosslen;
1331
1332 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001333 oldstackp = g_stackp;
1334 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001335 prevstackp = sp->prev;
1336 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1337 sp = ckrealloc(sp, grosslen);
1338 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001339 g_stackp = sp;
1340 g_stacknxt = sp->space;
1341 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001342 sstrend = sp->space + newlen;
1343
1344 /*
1345 * Stack marks pointing to the start of the old block
1346 * must be relocated to point to the new block
1347 */
1348 xmark = markp;
1349 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001350 xmark->stackp = g_stackp;
1351 xmark->stacknxt = g_stacknxt;
1352 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001353 xmark = xmark->marknext;
1354 }
1355 INT_ON;
1356 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001357 char *oldspace = g_stacknxt;
1358 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001359 char *p = stalloc(newlen);
1360
1361 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 g_stacknxt = memcpy(p, oldspace, oldlen);
1363 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001364 }
1365}
1366
1367static void
1368grabstackblock(size_t len)
1369{
1370 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001371 g_stacknxt += len;
1372 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001373}
1374
1375/*
1376 * The following routines are somewhat easier to use than the above.
1377 * The user declares a variable of type STACKSTR, which may be declared
1378 * to be a register. The macro STARTSTACKSTR initializes things. Then
1379 * the user uses the macro STPUTC to add characters to the string. In
1380 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1381 * grown as necessary. When the user is done, she can just leave the
1382 * string there and refer to it using stackblock(). Or she can allocate
1383 * the space for it using grabstackstr(). If it is necessary to allow
1384 * someone else to use the stack temporarily and then continue to grow
1385 * the string, the user should use grabstack to allocate the space, and
1386 * then call ungrabstr(p) to return to the previous mode of operation.
1387 *
1388 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1389 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1390 * is space for at least one character.
1391 */
1392static void *
1393growstackstr(void)
1394{
1395 size_t len = stackblocksize();
1396 if (herefd >= 0 && len >= 1024) {
1397 full_write(herefd, stackblock(), len);
1398 return stackblock();
1399 }
1400 growstackblock();
1401 return stackblock() + len;
1402}
1403
1404/*
1405 * Called from CHECKSTRSPACE.
1406 */
1407static char *
1408makestrspace(size_t newlen, char *p)
1409{
Denis Vlasenko01631112007-12-16 17:20:38 +00001410 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001411 size_t size = stackblocksize();
1412
1413 for (;;) {
1414 size_t nleft;
1415
1416 size = stackblocksize();
1417 nleft = size - len;
1418 if (nleft >= newlen)
1419 break;
1420 growstackblock();
1421 }
1422 return stackblock() + len;
1423}
1424
1425static char *
1426stack_nputstr(const char *s, size_t n, char *p)
1427{
1428 p = makestrspace(n, p);
1429 p = memcpy(p, s, n) + n;
1430 return p;
1431}
1432
1433static char *
1434stack_putstr(const char *s, char *p)
1435{
1436 return stack_nputstr(s, strlen(s), p);
1437}
1438
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001439static char *
1440_STPUTC(int c, char *p)
1441{
1442 if (p == sstrend)
1443 p = growstackstr();
1444 *p++ = c;
1445 return p;
1446}
1447
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001448#define STARTSTACKSTR(p) ((p) = stackblock())
1449#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001450#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001451 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001452 char *q = (p); \
1453 size_t l = (n); \
1454 size_t m = sstrend - q; \
1455 if (l > m) \
1456 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001457 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001458#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001459#define STACKSTRNUL(p) \
1460 do { \
1461 if ((p) == sstrend) \
1462 p = growstackstr(); \
1463 *p = '\0'; \
1464 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001465#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001466#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001467#define STADJUST(amount, p) (p += (amount))
1468
1469#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1470#define ungrabstackstr(s, p) stunalloc((s))
1471#define stackstrend() ((void *)sstrend)
1472
1473
1474/* ============ String helpers */
1475
1476/*
1477 * prefix -- see if pfx is a prefix of string.
1478 */
1479static char *
1480prefix(const char *string, const char *pfx)
1481{
1482 while (*pfx) {
1483 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001484 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001485 }
1486 return (char *) string;
1487}
1488
1489/*
1490 * Check for a valid number. This should be elsewhere.
1491 */
1492static int
1493is_number(const char *p)
1494{
1495 do {
1496 if (!isdigit(*p))
1497 return 0;
1498 } while (*++p != '\0');
1499 return 1;
1500}
1501
1502/*
1503 * Convert a string of digits to an integer, printing an error message on
1504 * failure.
1505 */
1506static int
1507number(const char *s)
1508{
1509 if (!is_number(s))
1510 ash_msg_and_raise_error(illnum, s);
1511 return atoi(s);
1512}
1513
1514/*
1515 * Produce a possibly single quoted string suitable as input to the shell.
1516 * The return string is allocated on the stack.
1517 */
1518static char *
1519single_quote(const char *s)
1520{
1521 char *p;
1522
1523 STARTSTACKSTR(p);
1524
1525 do {
1526 char *q;
1527 size_t len;
1528
1529 len = strchrnul(s, '\'') - s;
1530
1531 q = p = makestrspace(len + 3, p);
1532
1533 *q++ = '\'';
1534 q = memcpy(q, s, len) + len;
1535 *q++ = '\'';
1536 s += len;
1537
1538 STADJUST(q - p, p);
1539
1540 len = strspn(s, "'");
1541 if (!len)
1542 break;
1543
1544 q = p = makestrspace(len + 3, p);
1545
1546 *q++ = '"';
1547 q = memcpy(q, s, len) + len;
1548 *q++ = '"';
1549 s += len;
1550
1551 STADJUST(q - p, p);
1552 } while (*s);
1553
1554 USTPUTC(0, p);
1555
1556 return stackblock();
1557}
1558
1559
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001560/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001561
1562static char **argptr; /* argument list for builtin commands */
1563static char *optionarg; /* set by nextopt (like getopt) */
1564static char *optptr; /* used by nextopt */
1565
1566/*
1567 * XXX - should get rid of. have all builtins use getopt(3). the
1568 * library getopt must have the BSD extension static variable "optreset"
1569 * otherwise it can't be used within the shell safely.
1570 *
1571 * Standard option processing (a la getopt) for builtin routines. The
1572 * only argument that is passed to nextopt is the option string; the
1573 * other arguments are unnecessary. It return the character, or '\0' on
1574 * end of input.
1575 */
1576static int
1577nextopt(const char *optstring)
1578{
1579 char *p;
1580 const char *q;
1581 char c;
1582
1583 p = optptr;
1584 if (p == NULL || *p == '\0') {
1585 p = *argptr;
1586 if (p == NULL || *p != '-' || *++p == '\0')
1587 return '\0';
1588 argptr++;
1589 if (LONE_DASH(p)) /* check for "--" */
1590 return '\0';
1591 }
1592 c = *p++;
1593 for (q = optstring; *q != c; ) {
1594 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001595 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001596 if (*++q == ':')
1597 q++;
1598 }
1599 if (*++q == ':') {
1600 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001601 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001602 optionarg = p;
1603 p = NULL;
1604 }
1605 optptr = p;
1606 return c;
1607}
1608
1609
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001610/* ============ Math support definitions */
1611
1612#if ENABLE_ASH_MATH_SUPPORT_64
1613typedef int64_t arith_t;
1614#define arith_t_type long long
1615#else
1616typedef long arith_t;
1617#define arith_t_type long
1618#endif
1619
1620#if ENABLE_ASH_MATH_SUPPORT
1621static arith_t dash_arith(const char *);
1622static arith_t arith(const char *expr, int *perrcode);
1623#endif
1624
1625#if ENABLE_ASH_RANDOM_SUPPORT
1626static unsigned long rseed;
1627#ifndef DYNAMIC_VAR
1628#define DYNAMIC_VAR
1629#endif
1630#endif
1631
1632
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001633/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001634
Denis Vlasenko01631112007-12-16 17:20:38 +00001635/*
1636 * The parsefile structure pointed to by the global variable parsefile
1637 * contains information about the current file being read.
1638 */
1639struct redirtab {
1640 struct redirtab *next;
1641 int renamed[10];
1642 int nullredirs;
1643};
1644
1645struct shparam {
1646 int nparam; /* # of positional parameters (without $0) */
1647#if ENABLE_ASH_GETOPTS
1648 int optind; /* next parameter to be processed by getopts */
1649 int optoff; /* used by getopts */
1650#endif
1651 unsigned char malloced; /* if parameter list dynamically allocated */
1652 char **p; /* parameter list */
1653};
1654
1655/*
1656 * Free the list of positional parameters.
1657 */
1658static void
1659freeparam(volatile struct shparam *param)
1660{
1661 char **ap;
1662
1663 if (param->malloced) {
1664 for (ap = param->p; *ap; ap++)
1665 free(*ap);
1666 free(param->p);
1667 }
1668}
1669
1670#if ENABLE_ASH_GETOPTS
1671static void getoptsreset(const char *value);
1672#endif
1673
1674struct var {
1675 struct var *next; /* next entry in hash list */
1676 int flags; /* flags are defined above */
1677 const char *text; /* name=value */
1678 void (*func)(const char *); /* function to be called when */
1679 /* the variable gets set/unset */
1680};
1681
1682struct localvar {
1683 struct localvar *next; /* next local variable in list */
1684 struct var *vp; /* the variable that was made local */
1685 int flags; /* saved flags */
1686 const char *text; /* saved text */
1687};
1688
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001689/* flags */
1690#define VEXPORT 0x01 /* variable is exported */
1691#define VREADONLY 0x02 /* variable cannot be modified */
1692#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1693#define VTEXTFIXED 0x08 /* text is statically allocated */
1694#define VSTACK 0x10 /* text is allocated on the stack */
1695#define VUNSET 0x20 /* the variable is not set */
1696#define VNOFUNC 0x40 /* don't call the callback function */
1697#define VNOSET 0x80 /* do not set variable - just readonly test */
1698#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1699#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001700# define VDYNAMIC 0x200 /* dynamic variable */
1701#else
1702# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001703#endif
1704
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001706static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001707#define defifs (defifsvar + 4)
1708#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001709static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001710#endif
1711
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001712
Denis Vlasenko01631112007-12-16 17:20:38 +00001713/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001714#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001715static void
1716change_lc_all(const char *value)
1717{
1718 if (value && *value != '\0')
1719 setlocale(LC_ALL, value);
1720}
1721static void
1722change_lc_ctype(const char *value)
1723{
1724 if (value && *value != '\0')
1725 setlocale(LC_CTYPE, value);
1726}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001727#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728#if ENABLE_ASH_MAIL
1729static void chkmail(void);
1730static void changemail(const char *);
1731#endif
1732static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#if ENABLE_ASH_RANDOM_SUPPORT
1734static void change_random(const char *);
1735#endif
1736
Denis Vlasenko01631112007-12-16 17:20:38 +00001737static const struct {
1738 int flags;
1739 const char *text;
1740 void (*func)(const char *);
1741} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001745 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001748 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1749 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001751 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1752 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1753 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1754 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001756 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#endif
1758#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001759 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760#endif
1761#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001762 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1763 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001764#endif
1765#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001766 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001767#endif
1768};
1769
Denis Vlasenko01631112007-12-16 17:20:38 +00001770
1771struct globals_var {
1772 struct shparam shellparam; /* $@ current positional parameters */
1773 struct redirtab *redirlist;
1774 int g_nullredirs;
1775 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1776 struct var *vartab[VTABSIZE];
1777 struct var varinit[ARRAY_SIZE(varinit_data)];
1778};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001779extern struct globals_var *const ash_ptr_to_globals_var;
1780#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001781#define shellparam (G_var.shellparam )
1782#define redirlist (G_var.redirlist )
1783#define g_nullredirs (G_var.g_nullredirs )
1784#define preverrout_fd (G_var.preverrout_fd)
1785#define vartab (G_var.vartab )
1786#define varinit (G_var.varinit )
1787#define INIT_G_var() do { \
1788 int i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001789 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1790 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001791 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1792 varinit[i].flags = varinit_data[i].flags; \
1793 varinit[i].text = varinit_data[i].text; \
1794 varinit[i].func = varinit_data[i].func; \
1795 } \
1796} while (0)
1797
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001798#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001799#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001800# define vmail (&vifs)[1]
1801# define vmpath (&vmail)[1]
1802# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001804# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001806#define vps1 (&vpath)[1]
1807#define vps2 (&vps1)[1]
1808#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001809#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001810# define voptind (&vps4)[1]
1811# if ENABLE_ASH_RANDOM_SUPPORT
1812# define vrandom (&voptind)[1]
1813# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001814#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001815# if ENABLE_ASH_RANDOM_SUPPORT
1816# define vrandom (&vps4)[1]
1817# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001818#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819
1820/*
1821 * The following macros access the values of the above variables.
1822 * They have to skip over the name. They return the null string
1823 * for unset variables.
1824 */
1825#define ifsval() (vifs.text + 4)
1826#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001827#if ENABLE_ASH_MAIL
1828# define mailval() (vmail.text + 5)
1829# define mpathval() (vmpath.text + 9)
1830# define mpathset() ((vmpath.flags & VUNSET) == 0)
1831#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001832#define pathval() (vpath.text + 5)
1833#define ps1val() (vps1.text + 4)
1834#define ps2val() (vps2.text + 4)
1835#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001836#if ENABLE_ASH_GETOPTS
1837# define optindval() (voptind.text + 7)
1838#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001839
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001841#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1842#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1843
Denis Vlasenko01631112007-12-16 17:20:38 +00001844#if ENABLE_ASH_GETOPTS
1845static void
1846getoptsreset(const char *value)
1847{
1848 shellparam.optind = number(value);
1849 shellparam.optoff = -1;
1850}
1851#endif
1852
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001853/*
1854 * Return of a legal variable name (a letter or underscore followed by zero or
1855 * more letters, underscores, and digits).
1856 */
1857static char *
1858endofname(const char *name)
1859{
1860 char *p;
1861
1862 p = (char *) name;
1863 if (!is_name(*p))
1864 return p;
1865 while (*++p) {
1866 if (!is_in_name(*p))
1867 break;
1868 }
1869 return p;
1870}
1871
1872/*
1873 * Compares two strings up to the first = or '\0'. The first
1874 * string must be terminated by '='; the second may be terminated by
1875 * either '=' or '\0'.
1876 */
1877static int
1878varcmp(const char *p, const char *q)
1879{
1880 int c, d;
1881
1882 while ((c = *p) == (d = *q)) {
1883 if (!c || c == '=')
1884 goto out;
1885 p++;
1886 q++;
1887 }
1888 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001889 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001891 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 out:
1893 return c - d;
1894}
1895
1896static int
1897varequal(const char *a, const char *b)
1898{
1899 return !varcmp(a, b);
1900}
1901
1902/*
1903 * Find the appropriate entry in the hash table from the name.
1904 */
1905static struct var **
1906hashvar(const char *p)
1907{
1908 unsigned hashval;
1909
1910 hashval = ((unsigned char) *p) << 4;
1911 while (*p && *p != '=')
1912 hashval += (unsigned char) *p++;
1913 return &vartab[hashval % VTABSIZE];
1914}
1915
1916static int
1917vpcmp(const void *a, const void *b)
1918{
1919 return varcmp(*(const char **)a, *(const char **)b);
1920}
1921
1922/*
1923 * This routine initializes the builtin variables.
1924 */
1925static void
1926initvar(void)
1927{
1928 struct var *vp;
1929 struct var *end;
1930 struct var **vpp;
1931
1932 /*
1933 * PS1 depends on uid
1934 */
1935#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1936 vps1.text = "PS1=\\w \\$ ";
1937#else
1938 if (!geteuid())
1939 vps1.text = "PS1=# ";
1940#endif
1941 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001942 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001943 do {
1944 vpp = hashvar(vp->text);
1945 vp->next = *vpp;
1946 *vpp = vp;
1947 } while (++vp < end);
1948}
1949
1950static struct var **
1951findvar(struct var **vpp, const char *name)
1952{
1953 for (; *vpp; vpp = &(*vpp)->next) {
1954 if (varequal((*vpp)->text, name)) {
1955 break;
1956 }
1957 }
1958 return vpp;
1959}
1960
1961/*
1962 * Find the value of a variable. Returns NULL if not set.
1963 */
1964static char *
1965lookupvar(const char *name)
1966{
1967 struct var *v;
1968
1969 v = *findvar(hashvar(name), name);
1970 if (v) {
1971#ifdef DYNAMIC_VAR
1972 /*
1973 * Dynamic variables are implemented roughly the same way they are
1974 * in bash. Namely, they're "special" so long as they aren't unset.
1975 * As soon as they're unset, they're no longer dynamic, and dynamic
1976 * lookup will no longer happen at that point. -- PFM.
1977 */
1978 if ((v->flags & VDYNAMIC))
1979 (*v->func)(NULL);
1980#endif
1981 if (!(v->flags & VUNSET))
1982 return strchrnul(v->text, '=') + 1;
1983 }
1984 return NULL;
1985}
1986
1987/*
1988 * Search the environment of a builtin command.
1989 */
1990static char *
1991bltinlookup(const char *name)
1992{
1993 struct strlist *sp;
1994
1995 for (sp = cmdenviron; sp; sp = sp->next) {
1996 if (varequal(sp->text, name))
1997 return strchrnul(sp->text, '=') + 1;
1998 }
1999 return lookupvar(name);
2000}
2001
2002/*
2003 * Same as setvar except that the variable and value are passed in
2004 * the first argument as name=value. Since the first argument will
2005 * be actually stored in the table, it should not be a string that
2006 * will go away.
2007 * Called with interrupts off.
2008 */
2009static void
2010setvareq(char *s, int flags)
2011{
2012 struct var *vp, **vpp;
2013
2014 vpp = hashvar(s);
2015 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2016 vp = *findvar(vpp, s);
2017 if (vp) {
2018 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2019 const char *n;
2020
2021 if (flags & VNOSAVE)
2022 free(s);
2023 n = vp->text;
2024 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2025 }
2026
2027 if (flags & VNOSET)
2028 return;
2029
2030 if (vp->func && (flags & VNOFUNC) == 0)
2031 (*vp->func)(strchrnul(s, '=') + 1);
2032
2033 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2034 free((char*)vp->text);
2035
2036 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2037 } else {
2038 if (flags & VNOSET)
2039 return;
2040 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002041 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002042 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002043 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002044 *vpp = vp;
2045 }
2046 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2047 s = ckstrdup(s);
2048 vp->text = s;
2049 vp->flags = flags;
2050}
2051
2052/*
2053 * Set the value of a variable. The flags argument is ored with the
2054 * flags of the variable. If val is NULL, the variable is unset.
2055 */
2056static void
2057setvar(const char *name, const char *val, int flags)
2058{
2059 char *p, *q;
2060 size_t namelen;
2061 char *nameeq;
2062 size_t vallen;
2063
2064 q = endofname(name);
2065 p = strchrnul(q, '=');
2066 namelen = p - name;
2067 if (!namelen || p != q)
2068 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2069 vallen = 0;
2070 if (val == NULL) {
2071 flags |= VUNSET;
2072 } else {
2073 vallen = strlen(val);
2074 }
2075 INT_OFF;
2076 nameeq = ckmalloc(namelen + vallen + 2);
2077 p = memcpy(nameeq, name, namelen) + namelen;
2078 if (val) {
2079 *p++ = '=';
2080 p = memcpy(p, val, vallen) + vallen;
2081 }
2082 *p = '\0';
2083 setvareq(nameeq, flags | VNOSAVE);
2084 INT_ON;
2085}
2086
2087#if ENABLE_ASH_GETOPTS
2088/*
2089 * Safe version of setvar, returns 1 on success 0 on failure.
2090 */
2091static int
2092setvarsafe(const char *name, const char *val, int flags)
2093{
2094 int err;
2095 volatile int saveint;
2096 struct jmploc *volatile savehandler = exception_handler;
2097 struct jmploc jmploc;
2098
2099 SAVE_INT(saveint);
2100 if (setjmp(jmploc.loc))
2101 err = 1;
2102 else {
2103 exception_handler = &jmploc;
2104 setvar(name, val, flags);
2105 err = 0;
2106 }
2107 exception_handler = savehandler;
2108 RESTORE_INT(saveint);
2109 return err;
2110}
2111#endif
2112
2113/*
2114 * Unset the specified variable.
2115 */
2116static int
2117unsetvar(const char *s)
2118{
2119 struct var **vpp;
2120 struct var *vp;
2121 int retval;
2122
2123 vpp = findvar(hashvar(s), s);
2124 vp = *vpp;
2125 retval = 2;
2126 if (vp) {
2127 int flags = vp->flags;
2128
2129 retval = 1;
2130 if (flags & VREADONLY)
2131 goto out;
2132#ifdef DYNAMIC_VAR
2133 vp->flags &= ~VDYNAMIC;
2134#endif
2135 if (flags & VUNSET)
2136 goto ok;
2137 if ((flags & VSTRFIXED) == 0) {
2138 INT_OFF;
2139 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2140 free((char*)vp->text);
2141 *vpp = vp->next;
2142 free(vp);
2143 INT_ON;
2144 } else {
2145 setvar(s, 0, 0);
2146 vp->flags &= ~VEXPORT;
2147 }
2148 ok:
2149 retval = 0;
2150 }
2151 out:
2152 return retval;
2153}
2154
2155/*
2156 * Process a linked list of variable assignments.
2157 */
2158static void
2159listsetvar(struct strlist *list_set_var, int flags)
2160{
2161 struct strlist *lp = list_set_var;
2162
2163 if (!lp)
2164 return;
2165 INT_OFF;
2166 do {
2167 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002168 lp = lp->next;
2169 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002170 INT_ON;
2171}
2172
2173/*
2174 * Generate a list of variables satisfying the given conditions.
2175 */
2176static char **
2177listvars(int on, int off, char ***end)
2178{
2179 struct var **vpp;
2180 struct var *vp;
2181 char **ep;
2182 int mask;
2183
2184 STARTSTACKSTR(ep);
2185 vpp = vartab;
2186 mask = on | off;
2187 do {
2188 for (vp = *vpp; vp; vp = vp->next) {
2189 if ((vp->flags & mask) == on) {
2190 if (ep == stackstrend())
2191 ep = growstackstr();
2192 *ep++ = (char *) vp->text;
2193 }
2194 }
2195 } while (++vpp < vartab + VTABSIZE);
2196 if (ep == stackstrend())
2197 ep = growstackstr();
2198 if (end)
2199 *end = ep;
2200 *ep++ = NULL;
2201 return grabstackstr(ep);
2202}
2203
2204
2205/* ============ Path search helper
2206 *
2207 * The variable path (passed by reference) should be set to the start
2208 * of the path before the first call; padvance will update
2209 * this value as it proceeds. Successive calls to padvance will return
2210 * the possible path expansions in sequence. If an option (indicated by
2211 * a percent sign) appears in the path entry then the global variable
2212 * pathopt will be set to point to it; otherwise pathopt will be set to
2213 * NULL.
2214 */
2215static const char *pathopt; /* set by padvance */
2216
2217static char *
2218padvance(const char **path, const char *name)
2219{
2220 const char *p;
2221 char *q;
2222 const char *start;
2223 size_t len;
2224
2225 if (*path == NULL)
2226 return NULL;
2227 start = *path;
2228 for (p = start; *p && *p != ':' && *p != '%'; p++);
2229 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2230 while (stackblocksize() < len)
2231 growstackblock();
2232 q = stackblock();
2233 if (p != start) {
2234 memcpy(q, start, p - start);
2235 q += p - start;
2236 *q++ = '/';
2237 }
2238 strcpy(q, name);
2239 pathopt = NULL;
2240 if (*p == '%') {
2241 pathopt = ++p;
2242 while (*p && *p != ':') p++;
2243 }
2244 if (*p == ':')
2245 *path = p + 1;
2246 else
2247 *path = NULL;
2248 return stalloc(len);
2249}
2250
2251
2252/* ============ Prompt */
2253
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002254static smallint doprompt; /* if set, prompt the user */
2255static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256
2257#if ENABLE_FEATURE_EDITING
2258static line_input_t *line_input_state;
2259static const char *cmdedit_prompt;
2260static void
2261putprompt(const char *s)
2262{
2263 if (ENABLE_ASH_EXPAND_PRMT) {
2264 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002265 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002266 return;
2267 }
2268 cmdedit_prompt = s;
2269}
2270#else
2271static void
2272putprompt(const char *s)
2273{
2274 out2str(s);
2275}
2276#endif
2277
2278#if ENABLE_ASH_EXPAND_PRMT
2279/* expandstr() needs parsing machinery, so it is far away ahead... */
2280static const char *expandstr(const char *ps);
2281#else
2282#define expandstr(s) s
2283#endif
2284
2285static void
2286setprompt(int whichprompt)
2287{
2288 const char *prompt;
2289#if ENABLE_ASH_EXPAND_PRMT
2290 struct stackmark smark;
2291#endif
2292
2293 needprompt = 0;
2294
2295 switch (whichprompt) {
2296 case 1:
2297 prompt = ps1val();
2298 break;
2299 case 2:
2300 prompt = ps2val();
2301 break;
2302 default: /* 0 */
2303 prompt = nullstr;
2304 }
2305#if ENABLE_ASH_EXPAND_PRMT
2306 setstackmark(&smark);
2307 stalloc(stackblocksize());
2308#endif
2309 putprompt(expandstr(prompt));
2310#if ENABLE_ASH_EXPAND_PRMT
2311 popstackmark(&smark);
2312#endif
2313}
2314
2315
2316/* ============ The cd and pwd commands */
2317
2318#define CD_PHYSICAL 1
2319#define CD_PRINT 2
2320
2321static int docd(const char *, int);
2322
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002323static int
2324cdopt(void)
2325{
2326 int flags = 0;
2327 int i, j;
2328
2329 j = 'L';
2330 while ((i = nextopt("LP"))) {
2331 if (i != j) {
2332 flags ^= CD_PHYSICAL;
2333 j = i;
2334 }
2335 }
2336
2337 return flags;
2338}
2339
2340/*
2341 * Update curdir (the name of the current directory) in response to a
2342 * cd command.
2343 */
2344static const char *
2345updatepwd(const char *dir)
2346{
2347 char *new;
2348 char *p;
2349 char *cdcomppath;
2350 const char *lim;
2351
2352 cdcomppath = ststrdup(dir);
2353 STARTSTACKSTR(new);
2354 if (*dir != '/') {
2355 if (curdir == nullstr)
2356 return 0;
2357 new = stack_putstr(curdir, new);
2358 }
2359 new = makestrspace(strlen(dir) + 2, new);
2360 lim = stackblock() + 1;
2361 if (*dir != '/') {
2362 if (new[-1] != '/')
2363 USTPUTC('/', new);
2364 if (new > lim && *lim == '/')
2365 lim++;
2366 } else {
2367 USTPUTC('/', new);
2368 cdcomppath++;
2369 if (dir[1] == '/' && dir[2] != '/') {
2370 USTPUTC('/', new);
2371 cdcomppath++;
2372 lim++;
2373 }
2374 }
2375 p = strtok(cdcomppath, "/");
2376 while (p) {
2377 switch (*p) {
2378 case '.':
2379 if (p[1] == '.' && p[2] == '\0') {
2380 while (new > lim) {
2381 STUNPUTC(new);
2382 if (new[-1] == '/')
2383 break;
2384 }
2385 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002386 }
2387 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002388 break;
2389 /* fall through */
2390 default:
2391 new = stack_putstr(p, new);
2392 USTPUTC('/', new);
2393 }
2394 p = strtok(0, "/");
2395 }
2396 if (new > lim)
2397 STUNPUTC(new);
2398 *new = 0;
2399 return stackblock();
2400}
2401
2402/*
2403 * Find out what the current directory is. If we already know the current
2404 * directory, this routine returns immediately.
2405 */
2406static char *
2407getpwd(void)
2408{
Denis Vlasenko01631112007-12-16 17:20:38 +00002409 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002410 return dir ? dir : nullstr;
2411}
2412
2413static void
2414setpwd(const char *val, int setold)
2415{
2416 char *oldcur, *dir;
2417
2418 oldcur = dir = curdir;
2419
2420 if (setold) {
2421 setvar("OLDPWD", oldcur, VEXPORT);
2422 }
2423 INT_OFF;
2424 if (physdir != nullstr) {
2425 if (physdir != oldcur)
2426 free(physdir);
2427 physdir = nullstr;
2428 }
2429 if (oldcur == val || !val) {
2430 char *s = getpwd();
2431 physdir = s;
2432 if (!val)
2433 dir = s;
2434 } else
2435 dir = ckstrdup(val);
2436 if (oldcur != dir && oldcur != nullstr) {
2437 free(oldcur);
2438 }
2439 curdir = dir;
2440 INT_ON;
2441 setvar("PWD", dir, VEXPORT);
2442}
2443
2444static void hashcd(void);
2445
2446/*
2447 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2448 * know that the current directory has changed.
2449 */
2450static int
2451docd(const char *dest, int flags)
2452{
2453 const char *dir = 0;
2454 int err;
2455
2456 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2457
2458 INT_OFF;
2459 if (!(flags & CD_PHYSICAL)) {
2460 dir = updatepwd(dest);
2461 if (dir)
2462 dest = dir;
2463 }
2464 err = chdir(dest);
2465 if (err)
2466 goto out;
2467 setpwd(dir, 1);
2468 hashcd();
2469 out:
2470 INT_ON;
2471 return err;
2472}
2473
2474static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002475cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476{
2477 const char *dest;
2478 const char *path;
2479 const char *p;
2480 char c;
2481 struct stat statb;
2482 int flags;
2483
2484 flags = cdopt();
2485 dest = *argptr;
2486 if (!dest)
2487 dest = bltinlookup(homestr);
2488 else if (LONE_DASH(dest)) {
2489 dest = bltinlookup("OLDPWD");
2490 flags |= CD_PRINT;
2491 }
2492 if (!dest)
2493 dest = nullstr;
2494 if (*dest == '/')
2495 goto step7;
2496 if (*dest == '.') {
2497 c = dest[1];
2498 dotdot:
2499 switch (c) {
2500 case '\0':
2501 case '/':
2502 goto step6;
2503 case '.':
2504 c = dest[2];
2505 if (c != '.')
2506 goto dotdot;
2507 }
2508 }
2509 if (!*dest)
2510 dest = ".";
2511 path = bltinlookup("CDPATH");
2512 if (!path) {
2513 step6:
2514 step7:
2515 p = dest;
2516 goto docd;
2517 }
2518 do {
2519 c = *path;
2520 p = padvance(&path, dest);
2521 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2522 if (c && c != ':')
2523 flags |= CD_PRINT;
2524 docd:
2525 if (!docd(p, flags))
2526 goto out;
2527 break;
2528 }
2529 } while (path);
2530 ash_msg_and_raise_error("can't cd to %s", dest);
2531 /* NOTREACHED */
2532 out:
2533 if (flags & CD_PRINT)
2534 out1fmt(snlfmt, curdir);
2535 return 0;
2536}
2537
2538static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002539pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002540{
2541 int flags;
2542 const char *dir = curdir;
2543
2544 flags = cdopt();
2545 if (flags) {
2546 if (physdir == nullstr)
2547 setpwd(dir, 0);
2548 dir = physdir;
2549 }
2550 out1fmt(snlfmt, dir);
2551 return 0;
2552}
2553
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002554
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002555/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002556
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002557#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002558#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002559
Eric Andersenc470f442003-07-28 09:56:35 +00002560/* Syntax classes */
2561#define CWORD 0 /* character is nothing special */
2562#define CNL 1 /* newline character */
2563#define CBACK 2 /* a backslash character */
2564#define CSQUOTE 3 /* single quote */
2565#define CDQUOTE 4 /* double quote */
2566#define CENDQUOTE 5 /* a terminating quote */
2567#define CBQUOTE 6 /* backwards single quote */
2568#define CVAR 7 /* a dollar sign */
2569#define CENDVAR 8 /* a '}' character */
2570#define CLP 9 /* a left paren in arithmetic */
2571#define CRP 10 /* a right paren in arithmetic */
2572#define CENDFILE 11 /* end of file */
2573#define CCTL 12 /* like CWORD, except it must be escaped */
2574#define CSPCL 13 /* these terminate a word */
2575#define CIGN 14 /* character should be ignored */
2576
Denis Vlasenko131ae172007-02-18 13:00:19 +00002577#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002578#define SYNBASE 130
2579#define PEOF -130
2580#define PEOA -129
2581#define PEOA_OR_PEOF PEOA
2582#else
2583#define SYNBASE 129
2584#define PEOF -129
2585#define PEOA_OR_PEOF PEOF
2586#endif
2587
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002588/* number syntax index */
2589#define BASESYNTAX 0 /* not in quotes */
2590#define DQSYNTAX 1 /* in double quotes */
2591#define SQSYNTAX 2 /* in single quotes */
2592#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002593#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596#define USE_SIT_FUNCTION
2597#endif
2598
Denis Vlasenko131ae172007-02-18 13:00:19 +00002599#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002600static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002601#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002602 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002603#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002604 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2605 { CNL, CNL, CNL, CNL }, /* 2, \n */
2606 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2607 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2608 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2609 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2610 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2611 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2612 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2613 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2614 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002615#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002616 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2617 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2618 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002619#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002620};
Eric Andersenc470f442003-07-28 09:56:35 +00002621#else
2622static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002623#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002624 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002625#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002626 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2627 { CNL, CNL, CNL }, /* 2, \n */
2628 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2629 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2630 { CVAR, CVAR, CWORD }, /* 5, $ */
2631 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2632 { CSPCL, CWORD, CWORD }, /* 7, ( */
2633 { CSPCL, CWORD, CWORD }, /* 8, ) */
2634 { CBACK, CBACK, CCTL }, /* 9, \ */
2635 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2636 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002637#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002638 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2639 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2640 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002641#endif
2642};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002643#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002644
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002645#ifdef USE_SIT_FUNCTION
2646
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002647static int
2648SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002649{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002650 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002651#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002652 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002653 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2654 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2655 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2656 11, 3 /* "}~" */
2657 };
2658#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002659 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002660 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2661 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2662 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2663 10, 2 /* "}~" */
2664 };
2665#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002666 const char *s;
2667 int indx;
2668
Eric Andersenc470f442003-07-28 09:56:35 +00002669 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002671#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002672 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002674 else
2675#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002676#define U_C(c) ((unsigned char)(c))
2677
2678 if ((unsigned char)c >= (unsigned char)(CTLESC)
2679 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2680 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002681 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002682 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002683 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002684 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002685 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002686 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002687 }
2688 return S_I_T[indx][syntax];
2689}
2690
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002691#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692
Denis Vlasenko131ae172007-02-18 13:00:19 +00002693#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694#define CSPCL_CIGN_CIGN_CIGN 0
2695#define CSPCL_CWORD_CWORD_CWORD 1
2696#define CNL_CNL_CNL_CNL 2
2697#define CWORD_CCTL_CCTL_CWORD 3
2698#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2699#define CVAR_CVAR_CWORD_CVAR 5
2700#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2701#define CSPCL_CWORD_CWORD_CLP 7
2702#define CSPCL_CWORD_CWORD_CRP 8
2703#define CBACK_CBACK_CCTL_CBACK 9
2704#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2705#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2706#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2707#define CWORD_CWORD_CWORD_CWORD 13
2708#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002709#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002710#define CSPCL_CWORD_CWORD_CWORD 0
2711#define CNL_CNL_CNL_CNL 1
2712#define CWORD_CCTL_CCTL_CWORD 2
2713#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2714#define CVAR_CVAR_CWORD_CVAR 4
2715#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2716#define CSPCL_CWORD_CWORD_CLP 6
2717#define CSPCL_CWORD_CWORD_CRP 7
2718#define CBACK_CBACK_CCTL_CBACK 8
2719#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2720#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2721#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2722#define CWORD_CWORD_CWORD_CWORD 12
2723#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002724#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002725
2726static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002727 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002728 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002729#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002730 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2731#endif
2732 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2734 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2735 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2736 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2737 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2738 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2739 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2740 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002741 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2870 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2871 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2893 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002894 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002895 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2897 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002899 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002900 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2901 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2902 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2905 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2907 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2919 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2920 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2921 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2922 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2923 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2953 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2954 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2957 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2985 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2986 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2987 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002988};
2989
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002990#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2991
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002992#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002993
Eric Andersen2870d962001-07-02 17:27:21 +00002994
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002995/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002996
Denis Vlasenko131ae172007-02-18 13:00:19 +00002997#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002998
2999#define ALIASINUSE 1
3000#define ALIASDEAD 2
3001
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003002struct alias {
3003 struct alias *next;
3004 char *name;
3005 char *val;
3006 int flag;
3007};
3008
Denis Vlasenko01631112007-12-16 17:20:38 +00003009
3010static struct alias **atab; // [ATABSIZE];
3011#define INIT_G_alias() do { \
3012 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3013} while (0)
3014
Eric Andersen2870d962001-07-02 17:27:21 +00003015
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003016static struct alias **
3017__lookupalias(const char *name) {
3018 unsigned int hashval;
3019 struct alias **app;
3020 const char *p;
3021 unsigned int ch;
3022
3023 p = name;
3024
3025 ch = (unsigned char)*p;
3026 hashval = ch << 4;
3027 while (ch) {
3028 hashval += ch;
3029 ch = (unsigned char)*++p;
3030 }
3031 app = &atab[hashval % ATABSIZE];
3032
3033 for (; *app; app = &(*app)->next) {
3034 if (strcmp(name, (*app)->name) == 0) {
3035 break;
3036 }
3037 }
3038
3039 return app;
3040}
3041
3042static struct alias *
3043lookupalias(const char *name, int check)
3044{
3045 struct alias *ap = *__lookupalias(name);
3046
3047 if (check && ap && (ap->flag & ALIASINUSE))
3048 return NULL;
3049 return ap;
3050}
3051
3052static struct alias *
3053freealias(struct alias *ap)
3054{
3055 struct alias *next;
3056
3057 if (ap->flag & ALIASINUSE) {
3058 ap->flag |= ALIASDEAD;
3059 return ap;
3060 }
3061
3062 next = ap->next;
3063 free(ap->name);
3064 free(ap->val);
3065 free(ap);
3066 return next;
3067}
Eric Andersencb57d552001-06-28 07:25:16 +00003068
Eric Andersenc470f442003-07-28 09:56:35 +00003069static void
3070setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003071{
3072 struct alias *ap, **app;
3073
3074 app = __lookupalias(name);
3075 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003076 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003077 if (ap) {
3078 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003079 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003080 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003081 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003082 ap->flag &= ~ALIASDEAD;
3083 } else {
3084 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003085 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003086 ap->name = ckstrdup(name);
3087 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003088 /*ap->flag = 0; - ckzalloc did it */
3089 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003090 *app = ap;
3091 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003092 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003093}
3094
Eric Andersenc470f442003-07-28 09:56:35 +00003095static int
3096unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003097{
Eric Andersencb57d552001-06-28 07:25:16 +00003098 struct alias **app;
3099
3100 app = __lookupalias(name);
3101
3102 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003103 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003104 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003106 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003107 }
3108
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003109 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003110}
3111
Eric Andersenc470f442003-07-28 09:56:35 +00003112static void
3113rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003114{
Eric Andersencb57d552001-06-28 07:25:16 +00003115 struct alias *ap, **app;
3116 int i;
3117
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003119 for (i = 0; i < ATABSIZE; i++) {
3120 app = &atab[i];
3121 for (ap = *app; ap; ap = *app) {
3122 *app = freealias(*app);
3123 if (ap == *app) {
3124 app = &ap->next;
3125 }
3126 }
3127 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003128 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003129}
3130
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003131static void
3132printalias(const struct alias *ap)
3133{
3134 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3135}
3136
Eric Andersencb57d552001-06-28 07:25:16 +00003137/*
3138 * TODO - sort output
3139 */
Eric Andersenc470f442003-07-28 09:56:35 +00003140static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003141aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003142{
3143 char *n, *v;
3144 int ret = 0;
3145 struct alias *ap;
3146
Denis Vlasenko68404f12008-03-17 09:00:54 +00003147 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003148 int i;
3149
Denis Vlasenko68404f12008-03-17 09:00:54 +00003150 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003151 for (ap = atab[i]; ap; ap = ap->next) {
3152 printalias(ap);
3153 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003154 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003155 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003156 }
3157 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003158 v = strchr(n+1, '=');
3159 if (v == NULL) { /* n+1: funny ksh stuff */
3160 ap = *__lookupalias(n);
3161 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003162 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003163 ret = 1;
3164 } else
3165 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003166 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003167 *v++ = '\0';
3168 setalias(n, v);
3169 }
3170 }
3171
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003172 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003173}
3174
Eric Andersenc470f442003-07-28 09:56:35 +00003175static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003176unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003177{
3178 int i;
3179
3180 while ((i = nextopt("a")) != '\0') {
3181 if (i == 'a') {
3182 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003183 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003184 }
3185 }
3186 for (i = 0; *argptr; argptr++) {
3187 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003188 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003189 i = 1;
3190 }
3191 }
3192
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003193 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003194}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003195
Denis Vlasenko131ae172007-02-18 13:00:19 +00003196#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003197
Eric Andersenc470f442003-07-28 09:56:35 +00003198
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003199/* ============ jobs.c */
3200
3201/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3202#define FORK_FG 0
3203#define FORK_BG 1
3204#define FORK_NOJOB 2
3205
3206/* mode flags for showjob(s) */
3207#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3208#define SHOW_PID 0x04 /* include process pid */
3209#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3210
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003211/*
3212 * A job structure contains information about a job. A job is either a
3213 * single process or a set of processes contained in a pipeline. In the
3214 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3215 * array of pids.
3216 */
3217
3218struct procstat {
3219 pid_t pid; /* process id */
3220 int status; /* last process status from wait() */
3221 char *cmd; /* text of command being run */
3222};
3223
3224struct job {
3225 struct procstat ps0; /* status of process */
3226 struct procstat *ps; /* status or processes when more than one */
3227#if JOBS
3228 int stopstatus; /* status of a stopped job */
3229#endif
3230 uint32_t
3231 nprocs: 16, /* number of processes */
3232 state: 8,
3233#define JOBRUNNING 0 /* at least one proc running */
3234#define JOBSTOPPED 1 /* all procs are stopped */
3235#define JOBDONE 2 /* all procs are completed */
3236#if JOBS
3237 sigint: 1, /* job was killed by SIGINT */
3238 jobctl: 1, /* job running under job control */
3239#endif
3240 waited: 1, /* true if this entry has been waited for */
3241 used: 1, /* true if this entry is in used */
3242 changed: 1; /* true if status has changed */
3243 struct job *prev_job; /* previous job */
3244};
3245
3246static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003247static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003248
Denis Vlasenko68404f12008-03-17 09:00:54 +00003249static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003250#if !JOBS
3251#define forkshell(job, node, mode) forkshell(job, mode)
3252#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003253static int forkshell(struct job *, union node *, int);
3254static int waitforjob(struct job *);
3255
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003256#if !JOBS
3257enum { jobctl = 0 };
3258#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003260static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003261static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003262#endif
3263
3264/*
3265 * Set the signal handler for the specified signal. The routine figures
3266 * out what it should be set to.
3267 */
3268static void
3269setsignal(int signo)
3270{
3271 int action;
3272 char *t, tsig;
3273 struct sigaction act;
3274
3275 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003276 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277 if (t == NULL)
3278 action = S_DFL;
3279 else if (*t != '\0')
3280 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003281 if (rootshell && action == S_DFL) {
3282 switch (signo) {
3283 case SIGINT:
3284 if (iflag || minusc || sflag == 0)
3285 action = S_CATCH;
3286 break;
3287 case SIGQUIT:
3288#if DEBUG
3289 if (debug)
3290 break;
3291#endif
3292 /* FALLTHROUGH */
3293 case SIGTERM:
3294 if (iflag)
3295 action = S_IGN;
3296 break;
3297#if JOBS
3298 case SIGTSTP:
3299 case SIGTTOU:
3300 if (mflag)
3301 action = S_IGN;
3302 break;
3303#endif
3304 }
3305 }
3306
3307 t = &sigmode[signo - 1];
3308 tsig = *t;
3309 if (tsig == 0) {
3310 /*
3311 * current setting unknown
3312 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003313 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314 /*
3315 * Pretend it worked; maybe we should give a warning
3316 * here, but other shells don't. We don't alter
3317 * sigmode, so that we retry every time.
3318 */
3319 return;
3320 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003321 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003322 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003323 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003324 if (mflag
3325 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3326 ) {
3327 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 }
3330 }
3331 if (tsig == S_HARD_IGN || tsig == action)
3332 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003333 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334 switch (action) {
3335 case S_CATCH:
3336 act.sa_handler = onsig;
3337 break;
3338 case S_IGN:
3339 act.sa_handler = SIG_IGN;
3340 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 }
3342 *t = action;
3343 act.sa_flags = 0;
3344 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003345 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003346}
3347
3348/* mode flags for set_curjob */
3349#define CUR_DELETE 2
3350#define CUR_RUNNING 1
3351#define CUR_STOPPED 0
3352
3353/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003354#define DOWAIT_NONBLOCK WNOHANG
3355#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356
3357#if JOBS
3358/* pgrp of shell on invocation */
3359static int initialpgrp;
3360static int ttyfd = -1;
3361#endif
3362/* array of jobs */
3363static struct job *jobtab;
3364/* size of array */
3365static unsigned njobs;
3366/* current job */
3367static struct job *curjob;
3368/* number of presumed living untracked jobs */
3369static int jobless;
3370
3371static void
3372set_curjob(struct job *jp, unsigned mode)
3373{
3374 struct job *jp1;
3375 struct job **jpp, **curp;
3376
3377 /* first remove from list */
3378 jpp = curp = &curjob;
3379 do {
3380 jp1 = *jpp;
3381 if (jp1 == jp)
3382 break;
3383 jpp = &jp1->prev_job;
3384 } while (1);
3385 *jpp = jp1->prev_job;
3386
3387 /* Then re-insert in correct position */
3388 jpp = curp;
3389 switch (mode) {
3390 default:
3391#if DEBUG
3392 abort();
3393#endif
3394 case CUR_DELETE:
3395 /* job being deleted */
3396 break;
3397 case CUR_RUNNING:
3398 /* newly created job or backgrounded job,
3399 put after all stopped jobs. */
3400 do {
3401 jp1 = *jpp;
3402#if JOBS
3403 if (!jp1 || jp1->state != JOBSTOPPED)
3404#endif
3405 break;
3406 jpp = &jp1->prev_job;
3407 } while (1);
3408 /* FALLTHROUGH */
3409#if JOBS
3410 case CUR_STOPPED:
3411#endif
3412 /* newly stopped job - becomes curjob */
3413 jp->prev_job = *jpp;
3414 *jpp = jp;
3415 break;
3416 }
3417}
3418
3419#if JOBS || DEBUG
3420static int
3421jobno(const struct job *jp)
3422{
3423 return jp - jobtab + 1;
3424}
3425#endif
3426
3427/*
3428 * Convert a job name to a job structure.
3429 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003430#if !JOBS
3431#define getjob(name, getctl) getjob(name)
3432#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003433static struct job *
3434getjob(const char *name, int getctl)
3435{
3436 struct job *jp;
3437 struct job *found;
3438 const char *err_msg = "No such job: %s";
3439 unsigned num;
3440 int c;
3441 const char *p;
3442 char *(*match)(const char *, const char *);
3443
3444 jp = curjob;
3445 p = name;
3446 if (!p)
3447 goto currentjob;
3448
3449 if (*p != '%')
3450 goto err;
3451
3452 c = *++p;
3453 if (!c)
3454 goto currentjob;
3455
3456 if (!p[1]) {
3457 if (c == '+' || c == '%') {
3458 currentjob:
3459 err_msg = "No current job";
3460 goto check;
3461 }
3462 if (c == '-') {
3463 if (jp)
3464 jp = jp->prev_job;
3465 err_msg = "No previous job";
3466 check:
3467 if (!jp)
3468 goto err;
3469 goto gotit;
3470 }
3471 }
3472
3473 if (is_number(p)) {
3474 num = atoi(p);
3475 if (num < njobs) {
3476 jp = jobtab + num - 1;
3477 if (jp->used)
3478 goto gotit;
3479 goto err;
3480 }
3481 }
3482
3483 match = prefix;
3484 if (*p == '?') {
3485 match = strstr;
3486 p++;
3487 }
3488
3489 found = 0;
3490 while (1) {
3491 if (!jp)
3492 goto err;
3493 if (match(jp->ps[0].cmd, p)) {
3494 if (found)
3495 goto err;
3496 found = jp;
3497 err_msg = "%s: ambiguous";
3498 }
3499 jp = jp->prev_job;
3500 }
3501
3502 gotit:
3503#if JOBS
3504 err_msg = "job %s not created under job control";
3505 if (getctl && jp->jobctl == 0)
3506 goto err;
3507#endif
3508 return jp;
3509 err:
3510 ash_msg_and_raise_error(err_msg, name);
3511}
3512
3513/*
3514 * Mark a job structure as unused.
3515 */
3516static void
3517freejob(struct job *jp)
3518{
3519 struct procstat *ps;
3520 int i;
3521
3522 INT_OFF;
3523 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3524 if (ps->cmd != nullstr)
3525 free(ps->cmd);
3526 }
3527 if (jp->ps != &jp->ps0)
3528 free(jp->ps);
3529 jp->used = 0;
3530 set_curjob(jp, CUR_DELETE);
3531 INT_ON;
3532}
3533
3534#if JOBS
3535static void
3536xtcsetpgrp(int fd, pid_t pgrp)
3537{
3538 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003539 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003540}
3541
3542/*
3543 * Turn job control on and off.
3544 *
3545 * Note: This code assumes that the third arg to ioctl is a character
3546 * pointer, which is true on Berkeley systems but not System V. Since
3547 * System V doesn't have job control yet, this isn't a problem now.
3548 *
3549 * Called with interrupts off.
3550 */
3551static void
3552setjobctl(int on)
3553{
3554 int fd;
3555 int pgrp;
3556
3557 if (on == jobctl || rootshell == 0)
3558 return;
3559 if (on) {
3560 int ofd;
3561 ofd = fd = open(_PATH_TTY, O_RDWR);
3562 if (fd < 0) {
3563 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3564 * That sometimes helps to acquire controlling tty.
3565 * Obviously, a workaround for bugs when someone
3566 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003567 fd = 2;
3568 while (!isatty(fd))
3569 if (--fd < 0)
3570 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003571 }
3572 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003573 if (ofd >= 0)
3574 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575 if (fd < 0)
3576 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003577 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003578 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003579 do { /* while we are in the background */
3580 pgrp = tcgetpgrp(fd);
3581 if (pgrp < 0) {
3582 out:
3583 ash_msg("can't access tty; job control turned off");
3584 mflag = on = 0;
3585 goto close;
3586 }
3587 if (pgrp == getpgrp())
3588 break;
3589 killpg(0, SIGTTIN);
3590 } while (1);
3591 initialpgrp = pgrp;
3592
3593 setsignal(SIGTSTP);
3594 setsignal(SIGTTOU);
3595 setsignal(SIGTTIN);
3596 pgrp = rootpid;
3597 setpgid(0, pgrp);
3598 xtcsetpgrp(fd, pgrp);
3599 } else {
3600 /* turning job control off */
3601 fd = ttyfd;
3602 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003603 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003604 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003605 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003606 setpgid(0, pgrp);
3607 setsignal(SIGTSTP);
3608 setsignal(SIGTTOU);
3609 setsignal(SIGTTIN);
3610 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003611 if (fd >= 0)
3612 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003613 fd = -1;
3614 }
3615 ttyfd = fd;
3616 jobctl = on;
3617}
3618
3619static int
3620killcmd(int argc, char **argv)
3621{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003622 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003623 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003624 do {
3625 if (argv[i][0] == '%') {
3626 struct job *jp = getjob(argv[i], 0);
3627 unsigned pid = jp->ps[0].pid;
3628 /* Enough space for ' -NNN<nul>' */
3629 argv[i] = alloca(sizeof(int)*3 + 3);
3630 /* kill_main has matching code to expect
3631 * leading space. Needed to not confuse
3632 * negative pids with "kill -SIGNAL_NO" syntax */
3633 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003635 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003636 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003637 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003638}
3639
3640static void
3641showpipe(struct job *jp, FILE *out)
3642{
3643 struct procstat *sp;
3644 struct procstat *spend;
3645
3646 spend = jp->ps + jp->nprocs;
3647 for (sp = jp->ps + 1; sp < spend; sp++)
3648 fprintf(out, " | %s", sp->cmd);
3649 outcslow('\n', out);
3650 flush_stdout_stderr();
3651}
3652
3653
3654static int
3655restartjob(struct job *jp, int mode)
3656{
3657 struct procstat *ps;
3658 int i;
3659 int status;
3660 pid_t pgid;
3661
3662 INT_OFF;
3663 if (jp->state == JOBDONE)
3664 goto out;
3665 jp->state = JOBRUNNING;
3666 pgid = jp->ps->pid;
3667 if (mode == FORK_FG)
3668 xtcsetpgrp(ttyfd, pgid);
3669 killpg(pgid, SIGCONT);
3670 ps = jp->ps;
3671 i = jp->nprocs;
3672 do {
3673 if (WIFSTOPPED(ps->status)) {
3674 ps->status = -1;
3675 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003676 ps++;
3677 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678 out:
3679 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3680 INT_ON;
3681 return status;
3682}
3683
3684static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003685fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686{
3687 struct job *jp;
3688 FILE *out;
3689 int mode;
3690 int retval;
3691
3692 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3693 nextopt(nullstr);
3694 argv = argptr;
3695 out = stdout;
3696 do {
3697 jp = getjob(*argv, 1);
3698 if (mode == FORK_BG) {
3699 set_curjob(jp, CUR_RUNNING);
3700 fprintf(out, "[%d] ", jobno(jp));
3701 }
3702 outstr(jp->ps->cmd, out);
3703 showpipe(jp, out);
3704 retval = restartjob(jp, mode);
3705 } while (*argv && *++argv);
3706 return retval;
3707}
3708#endif
3709
3710static int
3711sprint_status(char *s, int status, int sigonly)
3712{
3713 int col;
3714 int st;
3715
3716 col = 0;
3717 if (!WIFEXITED(status)) {
3718#if JOBS
3719 if (WIFSTOPPED(status))
3720 st = WSTOPSIG(status);
3721 else
3722#endif
3723 st = WTERMSIG(status);
3724 if (sigonly) {
3725 if (st == SIGINT || st == SIGPIPE)
3726 goto out;
3727#if JOBS
3728 if (WIFSTOPPED(status))
3729 goto out;
3730#endif
3731 }
3732 st &= 0x7f;
3733 col = fmtstr(s, 32, strsignal(st));
3734 if (WCOREDUMP(status)) {
3735 col += fmtstr(s + col, 16, " (core dumped)");
3736 }
3737 } else if (!sigonly) {
3738 st = WEXITSTATUS(status);
3739 if (st)
3740 col = fmtstr(s, 16, "Done(%d)", st);
3741 else
3742 col = fmtstr(s, 16, "Done");
3743 }
3744 out:
3745 return col;
3746}
3747
3748/*
3749 * Do a wait system call. If job control is compiled in, we accept
3750 * stopped processes. If block is zero, we return a value of zero
3751 * rather than blocking.
3752 *
3753 * System V doesn't have a non-blocking wait system call. It does
3754 * have a SIGCLD signal that is sent to a process when one of it's
3755 * children dies. The obvious way to use SIGCLD would be to install
3756 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3757 * was received, and have waitproc bump another counter when it got
3758 * the status of a process. Waitproc would then know that a wait
3759 * system call would not block if the two counters were different.
3760 * This approach doesn't work because if a process has children that
3761 * have not been waited for, System V will send it a SIGCLD when it
3762 * installs a signal handler for SIGCLD. What this means is that when
3763 * a child exits, the shell will be sent SIGCLD signals continuously
3764 * until is runs out of stack space, unless it does a wait call before
3765 * restoring the signal handler. The code below takes advantage of
3766 * this (mis)feature by installing a signal handler for SIGCLD and
3767 * then checking to see whether it was called. If there are any
3768 * children to be waited for, it will be.
3769 *
3770 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3771 * waits at all. In this case, the user will not be informed when
3772 * a background process until the next time she runs a real program
3773 * (as opposed to running a builtin command or just typing return),
3774 * and the jobs command may give out of date information.
3775 */
3776static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003777waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003778{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779#if JOBS
3780 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003781 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003783 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3784 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785}
3786
3787/*
3788 * Wait for a process to terminate.
3789 */
3790static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003791dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792{
3793 int pid;
3794 int status;
3795 struct job *jp;
3796 struct job *thisjob;
3797 int state;
3798
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003799 TRACE(("dowait(%d) called\n", wait_flags));
3800 pid = waitproc(wait_flags, &status);
3801 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003802 if (pid <= 0) {
3803 /* If we were doing blocking wait and (probably) got EINTR,
3804 * check for pending sigs received while waiting.
3805 * (NB: can be moved into callers if needed) */
3806 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3807 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003808 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003809 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003810 INT_OFF;
3811 thisjob = NULL;
3812 for (jp = curjob; jp; jp = jp->prev_job) {
3813 struct procstat *sp;
3814 struct procstat *spend;
3815 if (jp->state == JOBDONE)
3816 continue;
3817 state = JOBDONE;
3818 spend = jp->ps + jp->nprocs;
3819 sp = jp->ps;
3820 do {
3821 if (sp->pid == pid) {
3822 TRACE(("Job %d: changing status of proc %d "
3823 "from 0x%x to 0x%x\n",
3824 jobno(jp), pid, sp->status, status));
3825 sp->status = status;
3826 thisjob = jp;
3827 }
3828 if (sp->status == -1)
3829 state = JOBRUNNING;
3830#if JOBS
3831 if (state == JOBRUNNING)
3832 continue;
3833 if (WIFSTOPPED(sp->status)) {
3834 jp->stopstatus = sp->status;
3835 state = JOBSTOPPED;
3836 }
3837#endif
3838 } while (++sp < spend);
3839 if (thisjob)
3840 goto gotjob;
3841 }
3842#if JOBS
3843 if (!WIFSTOPPED(status))
3844#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003845 jobless--;
3846 goto out;
3847
3848 gotjob:
3849 if (state != JOBRUNNING) {
3850 thisjob->changed = 1;
3851
3852 if (thisjob->state != state) {
3853 TRACE(("Job %d: changing state from %d to %d\n",
3854 jobno(thisjob), thisjob->state, state));
3855 thisjob->state = state;
3856#if JOBS
3857 if (state == JOBSTOPPED) {
3858 set_curjob(thisjob, CUR_STOPPED);
3859 }
3860#endif
3861 }
3862 }
3863
3864 out:
3865 INT_ON;
3866
3867 if (thisjob && thisjob == job) {
3868 char s[48 + 1];
3869 int len;
3870
3871 len = sprint_status(s, status, 1);
3872 if (len) {
3873 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003874 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003875 out2str(s);
3876 }
3877 }
3878 return pid;
3879}
3880
3881#if JOBS
3882static void
3883showjob(FILE *out, struct job *jp, int mode)
3884{
3885 struct procstat *ps;
3886 struct procstat *psend;
3887 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003888 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889 char s[80];
3890
3891 ps = jp->ps;
3892
3893 if (mode & SHOW_PGID) {
3894 /* just output process (group) id of pipeline */
3895 fprintf(out, "%d\n", ps->pid);
3896 return;
3897 }
3898
3899 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003900 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003901
3902 if (jp == curjob)
3903 s[col - 2] = '+';
3904 else if (curjob && jp == curjob->prev_job)
3905 s[col - 2] = '-';
3906
3907 if (mode & SHOW_PID)
3908 col += fmtstr(s + col, 16, "%d ", ps->pid);
3909
3910 psend = ps + jp->nprocs;
3911
3912 if (jp->state == JOBRUNNING) {
3913 strcpy(s + col, "Running");
3914 col += sizeof("Running") - 1;
3915 } else {
3916 int status = psend[-1].status;
3917 if (jp->state == JOBSTOPPED)
3918 status = jp->stopstatus;
3919 col += sprint_status(s + col, status, 0);
3920 }
3921
3922 goto start;
3923
3924 do {
3925 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003926 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003927 start:
3928 fprintf(out, "%s%*c%s",
3929 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3930 );
3931 if (!(mode & SHOW_PID)) {
3932 showpipe(jp, out);
3933 break;
3934 }
3935 if (++ps == psend) {
3936 outcslow('\n', out);
3937 break;
3938 }
3939 } while (1);
3940
3941 jp->changed = 0;
3942
3943 if (jp->state == JOBDONE) {
3944 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3945 freejob(jp);
3946 }
3947}
3948
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949/*
3950 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3951 * statuses have changed since the last call to showjobs.
3952 */
3953static void
3954showjobs(FILE *out, int mode)
3955{
3956 struct job *jp;
3957
3958 TRACE(("showjobs(%x) called\n", mode));
3959
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003960 /* If not even one job changed, there is nothing to do */
3961 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003962 continue;
3963
3964 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003965 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003967 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 }
3969}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003970
3971static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003972jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973{
3974 int mode, m;
3975
3976 mode = 0;
3977 while ((m = nextopt("lp"))) {
3978 if (m == 'l')
3979 mode = SHOW_PID;
3980 else
3981 mode = SHOW_PGID;
3982 }
3983
3984 argv = argptr;
3985 if (*argv) {
3986 do
3987 showjob(stdout, getjob(*argv,0), mode);
3988 while (*++argv);
3989 } else
3990 showjobs(stdout, mode);
3991
3992 return 0;
3993}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003994#endif /* JOBS */
3995
3996static int
3997getstatus(struct job *job)
3998{
3999 int status;
4000 int retval;
4001
4002 status = job->ps[job->nprocs - 1].status;
4003 retval = WEXITSTATUS(status);
4004 if (!WIFEXITED(status)) {
4005#if JOBS
4006 retval = WSTOPSIG(status);
4007 if (!WIFSTOPPED(status))
4008#endif
4009 {
4010 /* XXX: limits number of signals */
4011 retval = WTERMSIG(status);
4012#if JOBS
4013 if (retval == SIGINT)
4014 job->sigint = 1;
4015#endif
4016 }
4017 retval += 128;
4018 }
4019 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4020 jobno(job), job->nprocs, status, retval));
4021 return retval;
4022}
4023
4024static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004025waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026{
4027 struct job *job;
4028 int retval;
4029 struct job *jp;
4030
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004031// exsig++;
4032// xbarrier();
4033 if (pendingsig)
4034 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035
4036 nextopt(nullstr);
4037 retval = 0;
4038
4039 argv = argptr;
4040 if (!*argv) {
4041 /* wait for all jobs */
4042 for (;;) {
4043 jp = curjob;
4044 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004045 if (!jp) /* no running procs */
4046 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 if (jp->state == JOBRUNNING)
4048 break;
4049 jp->waited = 1;
4050 jp = jp->prev_job;
4051 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004052 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004053 }
4054 }
4055
4056 retval = 127;
4057 do {
4058 if (**argv != '%') {
4059 pid_t pid = number(*argv);
4060 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004061 while (1) {
4062 if (!job)
4063 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064 if (job->ps[job->nprocs - 1].pid == pid)
4065 break;
4066 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004067 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 } else
4069 job = getjob(*argv, 0);
4070 /* loop until process terminated or stopped */
4071 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004072 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004073 job->waited = 1;
4074 retval = getstatus(job);
4075 repeat:
4076 ;
4077 } while (*++argv);
4078
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004079 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004080 return retval;
4081}
4082
4083static struct job *
4084growjobtab(void)
4085{
4086 size_t len;
4087 ptrdiff_t offset;
4088 struct job *jp, *jq;
4089
4090 len = njobs * sizeof(*jp);
4091 jq = jobtab;
4092 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4093
4094 offset = (char *)jp - (char *)jq;
4095 if (offset) {
4096 /* Relocate pointers */
4097 size_t l = len;
4098
4099 jq = (struct job *)((char *)jq + l);
4100 while (l) {
4101 l -= sizeof(*jp);
4102 jq--;
4103#define joff(p) ((struct job *)((char *)(p) + l))
4104#define jmove(p) (p) = (void *)((char *)(p) + offset)
4105 if (joff(jp)->ps == &jq->ps0)
4106 jmove(joff(jp)->ps);
4107 if (joff(jp)->prev_job)
4108 jmove(joff(jp)->prev_job);
4109 }
4110 if (curjob)
4111 jmove(curjob);
4112#undef joff
4113#undef jmove
4114 }
4115
4116 njobs += 4;
4117 jobtab = jp;
4118 jp = (struct job *)((char *)jp + len);
4119 jq = jp + 3;
4120 do {
4121 jq->used = 0;
4122 } while (--jq >= jp);
4123 return jp;
4124}
4125
4126/*
4127 * Return a new job structure.
4128 * Called with interrupts off.
4129 */
4130static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004131makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004132{
4133 int i;
4134 struct job *jp;
4135
4136 for (i = njobs, jp = jobtab; ; jp++) {
4137 if (--i < 0) {
4138 jp = growjobtab();
4139 break;
4140 }
4141 if (jp->used == 0)
4142 break;
4143 if (jp->state != JOBDONE || !jp->waited)
4144 continue;
4145#if JOBS
4146 if (jobctl)
4147 continue;
4148#endif
4149 freejob(jp);
4150 break;
4151 }
4152 memset(jp, 0, sizeof(*jp));
4153#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004154 /* jp->jobctl is a bitfield.
4155 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004156 if (jobctl)
4157 jp->jobctl = 1;
4158#endif
4159 jp->prev_job = curjob;
4160 curjob = jp;
4161 jp->used = 1;
4162 jp->ps = &jp->ps0;
4163 if (nprocs > 1) {
4164 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4165 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004166 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004167 jobno(jp)));
4168 return jp;
4169}
4170
4171#if JOBS
4172/*
4173 * Return a string identifying a command (to be printed by the
4174 * jobs command).
4175 */
4176static char *cmdnextc;
4177
4178static void
4179cmdputs(const char *s)
4180{
4181 const char *p, *str;
4182 char c, cc[2] = " ";
4183 char *nextc;
4184 int subtype = 0;
4185 int quoted = 0;
4186 static const char vstype[VSTYPE + 1][4] = {
4187 "", "}", "-", "+", "?", "=",
4188 "%", "%%", "#", "##"
4189 };
4190
4191 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4192 p = s;
4193 while ((c = *p++) != 0) {
4194 str = 0;
4195 switch (c) {
4196 case CTLESC:
4197 c = *p++;
4198 break;
4199 case CTLVAR:
4200 subtype = *p++;
4201 if ((subtype & VSTYPE) == VSLENGTH)
4202 str = "${#";
4203 else
4204 str = "${";
4205 if (!(subtype & VSQUOTE) == !(quoted & 1))
4206 goto dostr;
4207 quoted ^= 1;
4208 c = '"';
4209 break;
4210 case CTLENDVAR:
4211 str = "\"}" + !(quoted & 1);
4212 quoted >>= 1;
4213 subtype = 0;
4214 goto dostr;
4215 case CTLBACKQ:
4216 str = "$(...)";
4217 goto dostr;
4218 case CTLBACKQ+CTLQUOTE:
4219 str = "\"$(...)\"";
4220 goto dostr;
4221#if ENABLE_ASH_MATH_SUPPORT
4222 case CTLARI:
4223 str = "$((";
4224 goto dostr;
4225 case CTLENDARI:
4226 str = "))";
4227 goto dostr;
4228#endif
4229 case CTLQUOTEMARK:
4230 quoted ^= 1;
4231 c = '"';
4232 break;
4233 case '=':
4234 if (subtype == 0)
4235 break;
4236 if ((subtype & VSTYPE) != VSNORMAL)
4237 quoted <<= 1;
4238 str = vstype[subtype & VSTYPE];
4239 if (subtype & VSNUL)
4240 c = ':';
4241 else
4242 goto checkstr;
4243 break;
4244 case '\'':
4245 case '\\':
4246 case '"':
4247 case '$':
4248 /* These can only happen inside quotes */
4249 cc[0] = c;
4250 str = cc;
4251 c = '\\';
4252 break;
4253 default:
4254 break;
4255 }
4256 USTPUTC(c, nextc);
4257 checkstr:
4258 if (!str)
4259 continue;
4260 dostr:
4261 while ((c = *str++)) {
4262 USTPUTC(c, nextc);
4263 }
4264 }
4265 if (quoted & 1) {
4266 USTPUTC('"', nextc);
4267 }
4268 *nextc = 0;
4269 cmdnextc = nextc;
4270}
4271
4272/* cmdtxt() and cmdlist() call each other */
4273static void cmdtxt(union node *n);
4274
4275static void
4276cmdlist(union node *np, int sep)
4277{
4278 for (; np; np = np->narg.next) {
4279 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004280 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004281 cmdtxt(np);
4282 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004283 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004284 }
4285}
4286
4287static void
4288cmdtxt(union node *n)
4289{
4290 union node *np;
4291 struct nodelist *lp;
4292 const char *p;
4293 char s[2];
4294
4295 if (!n)
4296 return;
4297 switch (n->type) {
4298 default:
4299#if DEBUG
4300 abort();
4301#endif
4302 case NPIPE:
4303 lp = n->npipe.cmdlist;
4304 for (;;) {
4305 cmdtxt(lp->n);
4306 lp = lp->next;
4307 if (!lp)
4308 break;
4309 cmdputs(" | ");
4310 }
4311 break;
4312 case NSEMI:
4313 p = "; ";
4314 goto binop;
4315 case NAND:
4316 p = " && ";
4317 goto binop;
4318 case NOR:
4319 p = " || ";
4320 binop:
4321 cmdtxt(n->nbinary.ch1);
4322 cmdputs(p);
4323 n = n->nbinary.ch2;
4324 goto donode;
4325 case NREDIR:
4326 case NBACKGND:
4327 n = n->nredir.n;
4328 goto donode;
4329 case NNOT:
4330 cmdputs("!");
4331 n = n->nnot.com;
4332 donode:
4333 cmdtxt(n);
4334 break;
4335 case NIF:
4336 cmdputs("if ");
4337 cmdtxt(n->nif.test);
4338 cmdputs("; then ");
4339 n = n->nif.ifpart;
4340 if (n->nif.elsepart) {
4341 cmdtxt(n);
4342 cmdputs("; else ");
4343 n = n->nif.elsepart;
4344 }
4345 p = "; fi";
4346 goto dotail;
4347 case NSUBSHELL:
4348 cmdputs("(");
4349 n = n->nredir.n;
4350 p = ")";
4351 goto dotail;
4352 case NWHILE:
4353 p = "while ";
4354 goto until;
4355 case NUNTIL:
4356 p = "until ";
4357 until:
4358 cmdputs(p);
4359 cmdtxt(n->nbinary.ch1);
4360 n = n->nbinary.ch2;
4361 p = "; done";
4362 dodo:
4363 cmdputs("; do ");
4364 dotail:
4365 cmdtxt(n);
4366 goto dotail2;
4367 case NFOR:
4368 cmdputs("for ");
4369 cmdputs(n->nfor.var);
4370 cmdputs(" in ");
4371 cmdlist(n->nfor.args, 1);
4372 n = n->nfor.body;
4373 p = "; done";
4374 goto dodo;
4375 case NDEFUN:
4376 cmdputs(n->narg.text);
4377 p = "() { ... }";
4378 goto dotail2;
4379 case NCMD:
4380 cmdlist(n->ncmd.args, 1);
4381 cmdlist(n->ncmd.redirect, 0);
4382 break;
4383 case NARG:
4384 p = n->narg.text;
4385 dotail2:
4386 cmdputs(p);
4387 break;
4388 case NHERE:
4389 case NXHERE:
4390 p = "<<...";
4391 goto dotail2;
4392 case NCASE:
4393 cmdputs("case ");
4394 cmdputs(n->ncase.expr->narg.text);
4395 cmdputs(" in ");
4396 for (np = n->ncase.cases; np; np = np->nclist.next) {
4397 cmdtxt(np->nclist.pattern);
4398 cmdputs(") ");
4399 cmdtxt(np->nclist.body);
4400 cmdputs(";; ");
4401 }
4402 p = "esac";
4403 goto dotail2;
4404 case NTO:
4405 p = ">";
4406 goto redir;
4407 case NCLOBBER:
4408 p = ">|";
4409 goto redir;
4410 case NAPPEND:
4411 p = ">>";
4412 goto redir;
4413 case NTOFD:
4414 p = ">&";
4415 goto redir;
4416 case NFROM:
4417 p = "<";
4418 goto redir;
4419 case NFROMFD:
4420 p = "<&";
4421 goto redir;
4422 case NFROMTO:
4423 p = "<>";
4424 redir:
4425 s[0] = n->nfile.fd + '0';
4426 s[1] = '\0';
4427 cmdputs(s);
4428 cmdputs(p);
4429 if (n->type == NTOFD || n->type == NFROMFD) {
4430 s[0] = n->ndup.dupfd + '0';
4431 p = s;
4432 goto dotail2;
4433 }
4434 n = n->nfile.fname;
4435 goto donode;
4436 }
4437}
4438
4439static char *
4440commandtext(union node *n)
4441{
4442 char *name;
4443
4444 STARTSTACKSTR(cmdnextc);
4445 cmdtxt(n);
4446 name = stackblock();
4447 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4448 name, cmdnextc, cmdnextc));
4449 return ckstrdup(name);
4450}
4451#endif /* JOBS */
4452
4453/*
4454 * Fork off a subshell. If we are doing job control, give the subshell its
4455 * own process group. Jp is a job structure that the job is to be added to.
4456 * N is the command that will be evaluated by the child. Both jp and n may
4457 * be NULL. The mode parameter can be one of the following:
4458 * FORK_FG - Fork off a foreground process.
4459 * FORK_BG - Fork off a background process.
4460 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4461 * process group even if job control is on.
4462 *
4463 * When job control is turned off, background processes have their standard
4464 * input redirected to /dev/null (except for the second and later processes
4465 * in a pipeline).
4466 *
4467 * Called with interrupts off.
4468 */
4469/*
4470 * Clear traps on a fork.
4471 */
4472static void
4473clear_traps(void)
4474{
4475 char **tp;
4476
4477 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004478 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004479 INT_OFF;
4480 free(*tp);
4481 *tp = NULL;
4482 if (tp != &trap[0])
4483 setsignal(tp - trap);
4484 INT_ON;
4485 }
4486 }
4487}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004488
4489/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004491
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004492/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004494forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495{
4496 int oldlvl;
4497
4498 TRACE(("Child shell %d\n", getpid()));
4499 oldlvl = shlvl;
4500 shlvl++;
4501
4502 closescript();
4503 clear_traps();
4504#if JOBS
4505 /* do job control only in root shell */
4506 jobctl = 0;
4507 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4508 pid_t pgrp;
4509
4510 if (jp->nprocs == 0)
4511 pgrp = getpid();
4512 else
4513 pgrp = jp->ps[0].pid;
4514 /* This can fail because we are doing it in the parent also */
4515 (void)setpgid(0, pgrp);
4516 if (mode == FORK_FG)
4517 xtcsetpgrp(ttyfd, pgrp);
4518 setsignal(SIGTSTP);
4519 setsignal(SIGTTOU);
4520 } else
4521#endif
4522 if (mode == FORK_BG) {
4523 ignoresig(SIGINT);
4524 ignoresig(SIGQUIT);
4525 if (jp->nprocs == 0) {
4526 close(0);
4527 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004528 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004529 }
4530 }
4531 if (!oldlvl && iflag) {
4532 setsignal(SIGINT);
4533 setsignal(SIGQUIT);
4534 setsignal(SIGTERM);
4535 }
4536 for (jp = curjob; jp; jp = jp->prev_job)
4537 freejob(jp);
4538 jobless = 0;
4539}
4540
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004541/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004542#if !JOBS
4543#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4544#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545static void
4546forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4547{
4548 TRACE(("In parent shell: child = %d\n", pid));
4549 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004550 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4551 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004552 jobless++;
4553 return;
4554 }
4555#if JOBS
4556 if (mode != FORK_NOJOB && jp->jobctl) {
4557 int pgrp;
4558
4559 if (jp->nprocs == 0)
4560 pgrp = pid;
4561 else
4562 pgrp = jp->ps[0].pid;
4563 /* This can fail because we are doing it in the child also */
4564 setpgid(pid, pgrp);
4565 }
4566#endif
4567 if (mode == FORK_BG) {
4568 backgndpid = pid; /* set $! */
4569 set_curjob(jp, CUR_RUNNING);
4570 }
4571 if (jp) {
4572 struct procstat *ps = &jp->ps[jp->nprocs++];
4573 ps->pid = pid;
4574 ps->status = -1;
4575 ps->cmd = nullstr;
4576#if JOBS
4577 if (jobctl && n)
4578 ps->cmd = commandtext(n);
4579#endif
4580 }
4581}
4582
4583static int
4584forkshell(struct job *jp, union node *n, int mode)
4585{
4586 int pid;
4587
4588 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4589 pid = fork();
4590 if (pid < 0) {
4591 TRACE(("Fork failed, errno=%d", errno));
4592 if (jp)
4593 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004594 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 }
4596 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004597 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004598 else
4599 forkparent(jp, n, mode, pid);
4600 return pid;
4601}
4602
4603/*
4604 * Wait for job to finish.
4605 *
4606 * Under job control we have the problem that while a child process is
4607 * running interrupts generated by the user are sent to the child but not
4608 * to the shell. This means that an infinite loop started by an inter-
4609 * active user may be hard to kill. With job control turned off, an
4610 * interactive user may place an interactive program inside a loop. If
4611 * the interactive program catches interrupts, the user doesn't want
4612 * these interrupts to also abort the loop. The approach we take here
4613 * is to have the shell ignore interrupt signals while waiting for a
4614 * foreground process to terminate, and then send itself an interrupt
4615 * signal if the child process was terminated by an interrupt signal.
4616 * Unfortunately, some programs want to do a bit of cleanup and then
4617 * exit on interrupt; unless these processes terminate themselves by
4618 * sending a signal to themselves (instead of calling exit) they will
4619 * confuse this approach.
4620 *
4621 * Called with interrupts off.
4622 */
4623static int
4624waitforjob(struct job *jp)
4625{
4626 int st;
4627
4628 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4629 while (jp->state == JOBRUNNING) {
4630 dowait(DOWAIT_BLOCK, jp);
4631 }
4632 st = getstatus(jp);
4633#if JOBS
4634 if (jp->jobctl) {
4635 xtcsetpgrp(ttyfd, rootpid);
4636 /*
4637 * This is truly gross.
4638 * If we're doing job control, then we did a TIOCSPGRP which
4639 * caused us (the shell) to no longer be in the controlling
4640 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4641 * intuit from the subprocess exit status whether a SIGINT
4642 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4643 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004644 if (jp->sigint) /* TODO: do the same with all signals */
4645 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004646 }
4647 if (jp->state == JOBDONE)
4648#endif
4649 freejob(jp);
4650 return st;
4651}
4652
4653/*
4654 * return 1 if there are stopped jobs, otherwise 0
4655 */
4656static int
4657stoppedjobs(void)
4658{
4659 struct job *jp;
4660 int retval;
4661
4662 retval = 0;
4663 if (job_warning)
4664 goto out;
4665 jp = curjob;
4666 if (jp && jp->state == JOBSTOPPED) {
4667 out2str("You have stopped jobs.\n");
4668 job_warning = 2;
4669 retval++;
4670 }
4671 out:
4672 return retval;
4673}
4674
4675
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004676/* ============ redir.c
4677 *
4678 * Code for dealing with input/output redirection.
4679 */
4680
4681#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004682#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004683#ifndef PIPE_BUF
4684# define PIPESIZE 4096 /* amount of buffering in a pipe */
4685#else
4686# define PIPESIZE PIPE_BUF
4687#endif
4688
4689/*
4690 * Open a file in noclobber mode.
4691 * The code was copied from bash.
4692 */
4693static int
4694noclobberopen(const char *fname)
4695{
4696 int r, fd;
4697 struct stat finfo, finfo2;
4698
4699 /*
4700 * If the file exists and is a regular file, return an error
4701 * immediately.
4702 */
4703 r = stat(fname, &finfo);
4704 if (r == 0 && S_ISREG(finfo.st_mode)) {
4705 errno = EEXIST;
4706 return -1;
4707 }
4708
4709 /*
4710 * If the file was not present (r != 0), make sure we open it
4711 * exclusively so that if it is created before we open it, our open
4712 * will fail. Make sure that we do not truncate an existing file.
4713 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4714 * file was not a regular file, we leave O_EXCL off.
4715 */
4716 if (r != 0)
4717 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4718 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4719
4720 /* If the open failed, return the file descriptor right away. */
4721 if (fd < 0)
4722 return fd;
4723
4724 /*
4725 * OK, the open succeeded, but the file may have been changed from a
4726 * non-regular file to a regular file between the stat and the open.
4727 * We are assuming that the O_EXCL open handles the case where FILENAME
4728 * did not exist and is symlinked to an existing file between the stat
4729 * and open.
4730 */
4731
4732 /*
4733 * If we can open it and fstat the file descriptor, and neither check
4734 * revealed that it was a regular file, and the file has not been
4735 * replaced, return the file descriptor.
4736 */
4737 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4738 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4739 return fd;
4740
4741 /* The file has been replaced. badness. */
4742 close(fd);
4743 errno = EEXIST;
4744 return -1;
4745}
4746
4747/*
4748 * Handle here documents. Normally we fork off a process to write the
4749 * data to a pipe. If the document is short, we can stuff the data in
4750 * the pipe without forking.
4751 */
4752/* openhere needs this forward reference */
4753static void expandhere(union node *arg, int fd);
4754static int
4755openhere(union node *redir)
4756{
4757 int pip[2];
4758 size_t len = 0;
4759
4760 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004761 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004762 if (redir->type == NHERE) {
4763 len = strlen(redir->nhere.doc->narg.text);
4764 if (len <= PIPESIZE) {
4765 full_write(pip[1], redir->nhere.doc->narg.text, len);
4766 goto out;
4767 }
4768 }
4769 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4770 close(pip[0]);
4771 signal(SIGINT, SIG_IGN);
4772 signal(SIGQUIT, SIG_IGN);
4773 signal(SIGHUP, SIG_IGN);
4774#ifdef SIGTSTP
4775 signal(SIGTSTP, SIG_IGN);
4776#endif
4777 signal(SIGPIPE, SIG_DFL);
4778 if (redir->type == NHERE)
4779 full_write(pip[1], redir->nhere.doc->narg.text, len);
4780 else
4781 expandhere(redir->nhere.doc, pip[1]);
4782 _exit(0);
4783 }
4784 out:
4785 close(pip[1]);
4786 return pip[0];
4787}
4788
4789static int
4790openredirect(union node *redir)
4791{
4792 char *fname;
4793 int f;
4794
4795 switch (redir->nfile.type) {
4796 case NFROM:
4797 fname = redir->nfile.expfname;
4798 f = open(fname, O_RDONLY);
4799 if (f < 0)
4800 goto eopen;
4801 break;
4802 case NFROMTO:
4803 fname = redir->nfile.expfname;
4804 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4805 if (f < 0)
4806 goto ecreate;
4807 break;
4808 case NTO:
4809 /* Take care of noclobber mode. */
4810 if (Cflag) {
4811 fname = redir->nfile.expfname;
4812 f = noclobberopen(fname);
4813 if (f < 0)
4814 goto ecreate;
4815 break;
4816 }
4817 /* FALLTHROUGH */
4818 case NCLOBBER:
4819 fname = redir->nfile.expfname;
4820 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4821 if (f < 0)
4822 goto ecreate;
4823 break;
4824 case NAPPEND:
4825 fname = redir->nfile.expfname;
4826 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4827 if (f < 0)
4828 goto ecreate;
4829 break;
4830 default:
4831#if DEBUG
4832 abort();
4833#endif
4834 /* Fall through to eliminate warning. */
4835 case NTOFD:
4836 case NFROMFD:
4837 f = -1;
4838 break;
4839 case NHERE:
4840 case NXHERE:
4841 f = openhere(redir);
4842 break;
4843 }
4844
4845 return f;
4846 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004847 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004848 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004849 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004850}
4851
4852/*
4853 * Copy a file descriptor to be >= to. Returns -1
4854 * if the source file descriptor is closed, EMPTY if there are no unused
4855 * file descriptors left.
4856 */
4857static int
4858copyfd(int from, int to)
4859{
4860 int newfd;
4861
4862 newfd = fcntl(from, F_DUPFD, to);
4863 if (newfd < 0) {
4864 if (errno == EMFILE)
4865 return EMPTY;
4866 ash_msg_and_raise_error("%d: %m", from);
4867 }
4868 return newfd;
4869}
4870
4871static void
4872dupredirect(union node *redir, int f)
4873{
4874 int fd = redir->nfile.fd;
4875
4876 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4877 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4878 copyfd(redir->ndup.dupfd, fd);
4879 }
4880 return;
4881 }
4882
4883 if (f != fd) {
4884 copyfd(f, fd);
4885 close(f);
4886 }
4887}
4888
4889/*
4890 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4891 * old file descriptors are stashed away so that the redirection can be
4892 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4893 * standard output, and the standard error if it becomes a duplicate of
4894 * stdout, is saved in memory.
4895 */
4896/* flags passed to redirect */
4897#define REDIR_PUSH 01 /* save previous values of file descriptors */
4898#define REDIR_SAVEFD2 03 /* set preverrout */
4899static void
4900redirect(union node *redir, int flags)
4901{
4902 union node *n;
4903 struct redirtab *sv;
4904 int i;
4905 int fd;
4906 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004907
Denis Vlasenko01631112007-12-16 17:20:38 +00004908 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004909 if (!redir) {
4910 return;
4911 }
4912 sv = NULL;
4913 INT_OFF;
4914 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004915 sv = ckmalloc(sizeof(*sv));
4916 sv->next = redirlist;
4917 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004918 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004920 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004921 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004922 }
4923 n = redir;
4924 do {
4925 fd = n->nfile.fd;
4926 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4927 && n->ndup.dupfd == fd)
4928 continue; /* redirect from/to same file descriptor */
4929
4930 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004931 if (fd == newfd) {
4932 /* Descriptor wasn't open before redirect.
4933 * Mark it for close in the future */
4934 if (sv && sv->renamed[fd] == EMPTY)
4935 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004937 }
4938 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004939 i = fcntl(fd, F_DUPFD, 10);
4940
4941 if (i == -1) {
4942 i = errno;
4943 if (i != EBADF) {
4944 close(newfd);
4945 errno = i;
4946 ash_msg_and_raise_error("%d: %m", fd);
4947 /* NOTREACHED */
4948 }
4949 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004950 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951 close(fd);
4952 }
4953 } else {
4954 close(fd);
4955 }
4956 dupredirect(n, newfd);
4957 } while ((n = n->nfile.next));
4958 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004959 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004960 preverrout_fd = sv->renamed[2];
4961}
4962
4963/*
4964 * Undo the effects of the last redirection.
4965 */
4966static void
4967popredir(int drop)
4968{
4969 struct redirtab *rp;
4970 int i;
4971
Denis Vlasenko01631112007-12-16 17:20:38 +00004972 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004973 return;
4974 INT_OFF;
4975 rp = redirlist;
4976 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004977 if (rp->renamed[i] == CLOSED) {
4978 if (!drop)
4979 close(i);
4980 continue;
4981 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982 if (rp->renamed[i] != EMPTY) {
4983 if (!drop) {
4984 close(i);
4985 copyfd(rp->renamed[i], i);
4986 }
4987 close(rp->renamed[i]);
4988 }
4989 }
4990 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004991 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004992 free(rp);
4993 INT_ON;
4994}
4995
4996/*
4997 * Undo all redirections. Called on error or interrupt.
4998 */
4999
5000/*
5001 * Discard all saved file descriptors.
5002 */
5003static void
5004clearredir(int drop)
5005{
5006 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005007 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005008 if (!redirlist)
5009 break;
5010 popredir(drop);
5011 }
5012}
5013
5014static int
5015redirectsafe(union node *redir, int flags)
5016{
5017 int err;
5018 volatile int saveint;
5019 struct jmploc *volatile savehandler = exception_handler;
5020 struct jmploc jmploc;
5021
5022 SAVE_INT(saveint);
5023 err = setjmp(jmploc.loc) * 2;
5024 if (!err) {
5025 exception_handler = &jmploc;
5026 redirect(redir, flags);
5027 }
5028 exception_handler = savehandler;
5029 if (err && exception != EXERROR)
5030 longjmp(exception_handler->loc, 1);
5031 RESTORE_INT(saveint);
5032 return err;
5033}
5034
5035
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005036/* ============ Routines to expand arguments to commands
5037 *
5038 * We have to deal with backquotes, shell variables, and file metacharacters.
5039 */
5040
5041/*
5042 * expandarg flags
5043 */
5044#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5045#define EXP_TILDE 0x2 /* do normal tilde expansion */
5046#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5047#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5048#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5049#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5050#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5051#define EXP_WORD 0x80 /* expand word in parameter expansion */
5052#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5053/*
5054 * _rmescape() flags
5055 */
5056#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5057#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5058#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5059#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5060#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5061
5062/*
5063 * Structure specifying which parts of the string should be searched
5064 * for IFS characters.
5065 */
5066struct ifsregion {
5067 struct ifsregion *next; /* next region in list */
5068 int begoff; /* offset of start of region */
5069 int endoff; /* offset of end of region */
5070 int nulonly; /* search for nul bytes only */
5071};
5072
5073struct arglist {
5074 struct strlist *list;
5075 struct strlist **lastp;
5076};
5077
5078/* output of current string */
5079static char *expdest;
5080/* list of back quote expressions */
5081static struct nodelist *argbackq;
5082/* first struct in list of ifs regions */
5083static struct ifsregion ifsfirst;
5084/* last struct in list */
5085static struct ifsregion *ifslastp;
5086/* holds expanded arg list */
5087static struct arglist exparg;
5088
5089/*
5090 * Our own itoa().
5091 */
5092static int
5093cvtnum(arith_t num)
5094{
5095 int len;
5096
5097 expdest = makestrspace(32, expdest);
5098#if ENABLE_ASH_MATH_SUPPORT_64
5099 len = fmtstr(expdest, 32, "%lld", (long long) num);
5100#else
5101 len = fmtstr(expdest, 32, "%ld", num);
5102#endif
5103 STADJUST(len, expdest);
5104 return len;
5105}
5106
5107static size_t
5108esclen(const char *start, const char *p)
5109{
5110 size_t esc = 0;
5111
5112 while (p > start && *--p == CTLESC) {
5113 esc++;
5114 }
5115 return esc;
5116}
5117
5118/*
5119 * Remove any CTLESC characters from a string.
5120 */
5121static char *
5122_rmescapes(char *str, int flag)
5123{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005124 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005125
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005126 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005127 unsigned inquotes;
5128 int notescaped;
5129 int globbing;
5130
5131 p = strpbrk(str, qchars);
5132 if (!p) {
5133 return str;
5134 }
5135 q = p;
5136 r = str;
5137 if (flag & RMESCAPE_ALLOC) {
5138 size_t len = p - str;
5139 size_t fulllen = len + strlen(p) + 1;
5140
5141 if (flag & RMESCAPE_GROW) {
5142 r = makestrspace(fulllen, expdest);
5143 } else if (flag & RMESCAPE_HEAP) {
5144 r = ckmalloc(fulllen);
5145 } else {
5146 r = stalloc(fulllen);
5147 }
5148 q = r;
5149 if (len > 0) {
5150 q = memcpy(q, str, len) + len;
5151 }
5152 }
5153 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5154 globbing = flag & RMESCAPE_GLOB;
5155 notescaped = globbing;
5156 while (*p) {
5157 if (*p == CTLQUOTEMARK) {
5158 inquotes = ~inquotes;
5159 p++;
5160 notescaped = globbing;
5161 continue;
5162 }
5163 if (*p == '\\') {
5164 /* naked back slash */
5165 notescaped = 0;
5166 goto copy;
5167 }
5168 if (*p == CTLESC) {
5169 p++;
5170 if (notescaped && inquotes && *p != '/') {
5171 *q++ = '\\';
5172 }
5173 }
5174 notescaped = globbing;
5175 copy:
5176 *q++ = *p++;
5177 }
5178 *q = '\0';
5179 if (flag & RMESCAPE_GROW) {
5180 expdest = r;
5181 STADJUST(q - r + 1, expdest);
5182 }
5183 return r;
5184}
5185#define rmescapes(p) _rmescapes((p), 0)
5186
5187#define pmatch(a, b) !fnmatch((a), (b), 0)
5188
5189/*
5190 * Prepare a pattern for a expmeta (internal glob(3)) call.
5191 *
5192 * Returns an stalloced string.
5193 */
5194static char *
5195preglob(const char *pattern, int quoted, int flag)
5196{
5197 flag |= RMESCAPE_GLOB;
5198 if (quoted) {
5199 flag |= RMESCAPE_QUOTED;
5200 }
5201 return _rmescapes((char *)pattern, flag);
5202}
5203
5204/*
5205 * Put a string on the stack.
5206 */
5207static void
5208memtodest(const char *p, size_t len, int syntax, int quotes)
5209{
5210 char *q = expdest;
5211
5212 q = makestrspace(len * 2, q);
5213
5214 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005215 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005216 if (!c)
5217 continue;
5218 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5219 USTPUTC(CTLESC, q);
5220 USTPUTC(c, q);
5221 }
5222
5223 expdest = q;
5224}
5225
5226static void
5227strtodest(const char *p, int syntax, int quotes)
5228{
5229 memtodest(p, strlen(p), syntax, quotes);
5230}
5231
5232/*
5233 * Record the fact that we have to scan this region of the
5234 * string for IFS characters.
5235 */
5236static void
5237recordregion(int start, int end, int nulonly)
5238{
5239 struct ifsregion *ifsp;
5240
5241 if (ifslastp == NULL) {
5242 ifsp = &ifsfirst;
5243 } else {
5244 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005245 ifsp = ckzalloc(sizeof(*ifsp));
5246 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005247 ifslastp->next = ifsp;
5248 INT_ON;
5249 }
5250 ifslastp = ifsp;
5251 ifslastp->begoff = start;
5252 ifslastp->endoff = end;
5253 ifslastp->nulonly = nulonly;
5254}
5255
5256static void
5257removerecordregions(int endoff)
5258{
5259 if (ifslastp == NULL)
5260 return;
5261
5262 if (ifsfirst.endoff > endoff) {
5263 while (ifsfirst.next != NULL) {
5264 struct ifsregion *ifsp;
5265 INT_OFF;
5266 ifsp = ifsfirst.next->next;
5267 free(ifsfirst.next);
5268 ifsfirst.next = ifsp;
5269 INT_ON;
5270 }
5271 if (ifsfirst.begoff > endoff)
5272 ifslastp = NULL;
5273 else {
5274 ifslastp = &ifsfirst;
5275 ifsfirst.endoff = endoff;
5276 }
5277 return;
5278 }
5279
5280 ifslastp = &ifsfirst;
5281 while (ifslastp->next && ifslastp->next->begoff < endoff)
5282 ifslastp=ifslastp->next;
5283 while (ifslastp->next != NULL) {
5284 struct ifsregion *ifsp;
5285 INT_OFF;
5286 ifsp = ifslastp->next->next;
5287 free(ifslastp->next);
5288 ifslastp->next = ifsp;
5289 INT_ON;
5290 }
5291 if (ifslastp->endoff > endoff)
5292 ifslastp->endoff = endoff;
5293}
5294
5295static char *
5296exptilde(char *startp, char *p, int flag)
5297{
5298 char c;
5299 char *name;
5300 struct passwd *pw;
5301 const char *home;
5302 int quotes = flag & (EXP_FULL | EXP_CASE);
5303 int startloc;
5304
5305 name = p + 1;
5306
5307 while ((c = *++p) != '\0') {
5308 switch (c) {
5309 case CTLESC:
5310 return startp;
5311 case CTLQUOTEMARK:
5312 return startp;
5313 case ':':
5314 if (flag & EXP_VARTILDE)
5315 goto done;
5316 break;
5317 case '/':
5318 case CTLENDVAR:
5319 goto done;
5320 }
5321 }
5322 done:
5323 *p = '\0';
5324 if (*name == '\0') {
5325 home = lookupvar(homestr);
5326 } else {
5327 pw = getpwnam(name);
5328 if (pw == NULL)
5329 goto lose;
5330 home = pw->pw_dir;
5331 }
5332 if (!home || !*home)
5333 goto lose;
5334 *p = c;
5335 startloc = expdest - (char *)stackblock();
5336 strtodest(home, SQSYNTAX, quotes);
5337 recordregion(startloc, expdest - (char *)stackblock(), 0);
5338 return p;
5339 lose:
5340 *p = c;
5341 return startp;
5342}
5343
5344/*
5345 * Execute a command inside back quotes. If it's a builtin command, we
5346 * want to save its output in a block obtained from malloc. Otherwise
5347 * we fork off a subprocess and get the output of the command via a pipe.
5348 * Should be called with interrupts off.
5349 */
5350struct backcmd { /* result of evalbackcmd */
5351 int fd; /* file descriptor to read from */
5352 char *buf; /* buffer */
5353 int nleft; /* number of chars in buffer */
5354 struct job *jp; /* job structure for command */
5355};
5356
5357/* These forward decls are needed to use "eval" code for backticks handling: */
5358static int back_exitstatus; /* exit status of backquoted command */
5359#define EV_EXIT 01 /* exit after evaluating tree */
5360static void evaltree(union node *, int);
5361
5362static void
5363evalbackcmd(union node *n, struct backcmd *result)
5364{
5365 int saveherefd;
5366
5367 result->fd = -1;
5368 result->buf = NULL;
5369 result->nleft = 0;
5370 result->jp = NULL;
5371 if (n == NULL) {
5372 goto out;
5373 }
5374
5375 saveherefd = herefd;
5376 herefd = -1;
5377
5378 {
5379 int pip[2];
5380 struct job *jp;
5381
5382 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005383 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005384 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005385 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5386 FORCE_INT_ON;
5387 close(pip[0]);
5388 if (pip[1] != 1) {
5389 close(1);
5390 copyfd(pip[1], 1);
5391 close(pip[1]);
5392 }
5393 eflag = 0;
5394 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5395 /* NOTREACHED */
5396 }
5397 close(pip[1]);
5398 result->fd = pip[0];
5399 result->jp = jp;
5400 }
5401 herefd = saveherefd;
5402 out:
5403 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5404 result->fd, result->buf, result->nleft, result->jp));
5405}
5406
5407/*
5408 * Expand stuff in backwards quotes.
5409 */
5410static void
5411expbackq(union node *cmd, int quoted, int quotes)
5412{
5413 struct backcmd in;
5414 int i;
5415 char buf[128];
5416 char *p;
5417 char *dest;
5418 int startloc;
5419 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5420 struct stackmark smark;
5421
5422 INT_OFF;
5423 setstackmark(&smark);
5424 dest = expdest;
5425 startloc = dest - (char *)stackblock();
5426 grabstackstr(dest);
5427 evalbackcmd(cmd, &in);
5428 popstackmark(&smark);
5429
5430 p = in.buf;
5431 i = in.nleft;
5432 if (i == 0)
5433 goto read;
5434 for (;;) {
5435 memtodest(p, i, syntax, quotes);
5436 read:
5437 if (in.fd < 0)
5438 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005439 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005440 TRACE(("expbackq: read returns %d\n", i));
5441 if (i <= 0)
5442 break;
5443 p = buf;
5444 }
5445
Denis Vlasenko60818682007-09-28 22:07:23 +00005446 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005447 if (in.fd >= 0) {
5448 close(in.fd);
5449 back_exitstatus = waitforjob(in.jp);
5450 }
5451 INT_ON;
5452
5453 /* Eat all trailing newlines */
5454 dest = expdest;
5455 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5456 STUNPUTC(dest);
5457 expdest = dest;
5458
5459 if (quoted == 0)
5460 recordregion(startloc, dest - (char *)stackblock(), 0);
5461 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5462 (dest - (char *)stackblock()) - startloc,
5463 (dest - (char *)stackblock()) - startloc,
5464 stackblock() + startloc));
5465}
5466
5467#if ENABLE_ASH_MATH_SUPPORT
5468/*
5469 * Expand arithmetic expression. Backup to start of expression,
5470 * evaluate, place result in (backed up) result, adjust string position.
5471 */
5472static void
5473expari(int quotes)
5474{
5475 char *p, *start;
5476 int begoff;
5477 int flag;
5478 int len;
5479
5480 /* ifsfree(); */
5481
5482 /*
5483 * This routine is slightly over-complicated for
5484 * efficiency. Next we scan backwards looking for the
5485 * start of arithmetic.
5486 */
5487 start = stackblock();
5488 p = expdest - 1;
5489 *p = '\0';
5490 p--;
5491 do {
5492 int esc;
5493
5494 while (*p != CTLARI) {
5495 p--;
5496#if DEBUG
5497 if (p < start) {
5498 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5499 }
5500#endif
5501 }
5502
5503 esc = esclen(start, p);
5504 if (!(esc % 2)) {
5505 break;
5506 }
5507
5508 p -= esc + 1;
5509 } while (1);
5510
5511 begoff = p - start;
5512
5513 removerecordregions(begoff);
5514
5515 flag = p[1];
5516
5517 expdest = p;
5518
5519 if (quotes)
5520 rmescapes(p + 2);
5521
5522 len = cvtnum(dash_arith(p + 2));
5523
5524 if (flag != '"')
5525 recordregion(begoff, begoff + len, 0);
5526}
5527#endif
5528
5529/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005530static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005531
5532/*
5533 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5534 * characters to allow for further processing. Otherwise treat
5535 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005536 *
5537 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5538 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5539 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540 */
5541static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005542argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005543{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005544 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005545 '=',
5546 ':',
5547 CTLQUOTEMARK,
5548 CTLENDVAR,
5549 CTLESC,
5550 CTLVAR,
5551 CTLBACKQ,
5552 CTLBACKQ | CTLQUOTE,
5553#if ENABLE_ASH_MATH_SUPPORT
5554 CTLENDARI,
5555#endif
5556 0
5557 };
5558 const char *reject = spclchars;
5559 int c;
5560 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5561 int breakall = flag & EXP_WORD;
5562 int inquotes;
5563 size_t length;
5564 int startloc;
5565
5566 if (!(flag & EXP_VARTILDE)) {
5567 reject += 2;
5568 } else if (flag & EXP_VARTILDE2) {
5569 reject++;
5570 }
5571 inquotes = 0;
5572 length = 0;
5573 if (flag & EXP_TILDE) {
5574 char *q;
5575
5576 flag &= ~EXP_TILDE;
5577 tilde:
5578 q = p;
5579 if (*q == CTLESC && (flag & EXP_QWORD))
5580 q++;
5581 if (*q == '~')
5582 p = exptilde(p, q, flag);
5583 }
5584 start:
5585 startloc = expdest - (char *)stackblock();
5586 for (;;) {
5587 length += strcspn(p + length, reject);
5588 c = p[length];
5589 if (c && (!(c & 0x80)
5590#if ENABLE_ASH_MATH_SUPPORT
5591 || c == CTLENDARI
5592#endif
5593 )) {
5594 /* c == '=' || c == ':' || c == CTLENDARI */
5595 length++;
5596 }
5597 if (length > 0) {
5598 int newloc;
5599 expdest = stack_nputstr(p, length, expdest);
5600 newloc = expdest - (char *)stackblock();
5601 if (breakall && !inquotes && newloc > startloc) {
5602 recordregion(startloc, newloc, 0);
5603 }
5604 startloc = newloc;
5605 }
5606 p += length + 1;
5607 length = 0;
5608
5609 switch (c) {
5610 case '\0':
5611 goto breakloop;
5612 case '=':
5613 if (flag & EXP_VARTILDE2) {
5614 p--;
5615 continue;
5616 }
5617 flag |= EXP_VARTILDE2;
5618 reject++;
5619 /* fall through */
5620 case ':':
5621 /*
5622 * sort of a hack - expand tildes in variable
5623 * assignments (after the first '=' and after ':'s).
5624 */
5625 if (*--p == '~') {
5626 goto tilde;
5627 }
5628 continue;
5629 }
5630
5631 switch (c) {
5632 case CTLENDVAR: /* ??? */
5633 goto breakloop;
5634 case CTLQUOTEMARK:
5635 /* "$@" syntax adherence hack */
5636 if (
5637 !inquotes &&
5638 !memcmp(p, dolatstr, 4) &&
5639 (p[4] == CTLQUOTEMARK || (
5640 p[4] == CTLENDVAR &&
5641 p[5] == CTLQUOTEMARK
5642 ))
5643 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005644 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005645 goto start;
5646 }
5647 inquotes = !inquotes;
5648 addquote:
5649 if (quotes) {
5650 p--;
5651 length++;
5652 startloc++;
5653 }
5654 break;
5655 case CTLESC:
5656 startloc++;
5657 length++;
5658 goto addquote;
5659 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005660 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005661 goto start;
5662 case CTLBACKQ:
5663 c = 0;
5664 case CTLBACKQ|CTLQUOTE:
5665 expbackq(argbackq->n, c, quotes);
5666 argbackq = argbackq->next;
5667 goto start;
5668#if ENABLE_ASH_MATH_SUPPORT
5669 case CTLENDARI:
5670 p--;
5671 expari(quotes);
5672 goto start;
5673#endif
5674 }
5675 }
5676 breakloop:
5677 ;
5678}
5679
5680static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005681scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005682 int zero)
5683{
5684 char *loc;
5685 char *loc2;
5686 char c;
5687
5688 loc = startp;
5689 loc2 = rmesc;
5690 do {
5691 int match;
5692 const char *s = loc2;
5693 c = *loc2;
5694 if (zero) {
5695 *loc2 = '\0';
5696 s = rmesc;
5697 }
5698 match = pmatch(str, s);
5699 *loc2 = c;
5700 if (match)
5701 return loc;
5702 if (quotes && *loc == CTLESC)
5703 loc++;
5704 loc++;
5705 loc2++;
5706 } while (c);
5707 return 0;
5708}
5709
5710static char *
5711scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5712 int zero)
5713{
5714 int esc = 0;
5715 char *loc;
5716 char *loc2;
5717
5718 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5719 int match;
5720 char c = *loc2;
5721 const char *s = loc2;
5722 if (zero) {
5723 *loc2 = '\0';
5724 s = rmesc;
5725 }
5726 match = pmatch(str, s);
5727 *loc2 = c;
5728 if (match)
5729 return loc;
5730 loc--;
5731 if (quotes) {
5732 if (--esc < 0) {
5733 esc = esclen(startp, loc);
5734 }
5735 if (esc % 2) {
5736 esc--;
5737 loc--;
5738 }
5739 }
5740 }
5741 return 0;
5742}
5743
5744static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5745static void
5746varunset(const char *end, const char *var, const char *umsg, int varflags)
5747{
5748 const char *msg;
5749 const char *tail;
5750
5751 tail = nullstr;
5752 msg = "parameter not set";
5753 if (umsg) {
5754 if (*end == CTLENDVAR) {
5755 if (varflags & VSNUL)
5756 tail = " or null";
5757 } else
5758 msg = umsg;
5759 }
5760 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5761}
5762
5763static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005764subevalvar(char *p, char *str, int strloc, int subtype,
5765 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005766{
5767 char *startp;
5768 char *loc;
5769 int saveherefd = herefd;
5770 struct nodelist *saveargbackq = argbackq;
5771 int amount;
5772 char *rmesc, *rmescend;
5773 int zero;
5774 char *(*scan)(char *, char *, char *, char *, int , int);
5775
5776 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005777 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5778 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005779 STPUTC('\0', expdest);
5780 herefd = saveherefd;
5781 argbackq = saveargbackq;
5782 startp = stackblock() + startloc;
5783
5784 switch (subtype) {
5785 case VSASSIGN:
5786 setvar(str, startp, 0);
5787 amount = startp - expdest;
5788 STADJUST(amount, expdest);
5789 return startp;
5790
5791 case VSQUESTION:
5792 varunset(p, str, startp, varflags);
5793 /* NOTREACHED */
5794 }
5795
5796 subtype -= VSTRIMRIGHT;
5797#if DEBUG
5798 if (subtype < 0 || subtype > 3)
5799 abort();
5800#endif
5801
5802 rmesc = startp;
5803 rmescend = stackblock() + strloc;
5804 if (quotes) {
5805 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5806 if (rmesc != startp) {
5807 rmescend = expdest;
5808 startp = stackblock() + startloc;
5809 }
5810 }
5811 rmescend--;
5812 str = stackblock() + strloc;
5813 preglob(str, varflags & VSQUOTE, 0);
5814
5815 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5816 zero = subtype >> 1;
5817 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5818 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5819
5820 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5821 if (loc) {
5822 if (zero) {
5823 memmove(startp, loc, str - loc);
5824 loc = startp + (str - loc) - 1;
5825 }
5826 *loc = '\0';
5827 amount = loc - expdest;
5828 STADJUST(amount, expdest);
5829 }
5830 return loc;
5831}
5832
5833/*
5834 * Add the value of a specialized variable to the stack string.
5835 */
5836static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005837varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005838{
5839 int num;
5840 char *p;
5841 int i;
5842 int sep = 0;
5843 int sepq = 0;
5844 ssize_t len = 0;
5845 char **ap;
5846 int syntax;
5847 int quoted = varflags & VSQUOTE;
5848 int subtype = varflags & VSTYPE;
5849 int quotes = flags & (EXP_FULL | EXP_CASE);
5850
5851 if (quoted && (flags & EXP_FULL))
5852 sep = 1 << CHAR_BIT;
5853
5854 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5855 switch (*name) {
5856 case '$':
5857 num = rootpid;
5858 goto numvar;
5859 case '?':
5860 num = exitstatus;
5861 goto numvar;
5862 case '#':
5863 num = shellparam.nparam;
5864 goto numvar;
5865 case '!':
5866 num = backgndpid;
5867 if (num == 0)
5868 return -1;
5869 numvar:
5870 len = cvtnum(num);
5871 break;
5872 case '-':
5873 p = makestrspace(NOPTS, expdest);
5874 for (i = NOPTS - 1; i >= 0; i--) {
5875 if (optlist[i]) {
5876 USTPUTC(optletters(i), p);
5877 len++;
5878 }
5879 }
5880 expdest = p;
5881 break;
5882 case '@':
5883 if (sep)
5884 goto param;
5885 /* fall through */
5886 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005887 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005888 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5889 sepq = 1;
5890 param:
5891 ap = shellparam.p;
5892 if (!ap)
5893 return -1;
5894 while ((p = *ap++)) {
5895 size_t partlen;
5896
5897 partlen = strlen(p);
5898 len += partlen;
5899
5900 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5901 memtodest(p, partlen, syntax, quotes);
5902
5903 if (*ap && sep) {
5904 char *q;
5905
5906 len++;
5907 if (subtype == VSPLUS || subtype == VSLENGTH) {
5908 continue;
5909 }
5910 q = expdest;
5911 if (sepq)
5912 STPUTC(CTLESC, q);
5913 STPUTC(sep, q);
5914 expdest = q;
5915 }
5916 }
5917 return len;
5918 case '0':
5919 case '1':
5920 case '2':
5921 case '3':
5922 case '4':
5923 case '5':
5924 case '6':
5925 case '7':
5926 case '8':
5927 case '9':
5928 num = atoi(name);
5929 if (num < 0 || num > shellparam.nparam)
5930 return -1;
5931 p = num ? shellparam.p[num - 1] : arg0;
5932 goto value;
5933 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005934 /* NB: name has form "VAR=..." */
5935
5936 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
5937 * which should be considered before we check variables. */
5938 if (var_str_list) {
5939 unsigned name_len = (strchrnul(name, '=') - name) + 1;
5940 p = NULL;
5941 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00005942 char *str, *eq;
5943 str = var_str_list->text;
5944 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005945 if (!eq) /* stop at first non-assignment */
5946 break;
5947 eq++;
5948 if (name_len == (eq - str)
5949 && strncmp(str, name, name_len) == 0) {
5950 p = eq;
5951 /* goto value; - WRONG! */
5952 /* think "A=1 A=2 B=$A" */
5953 }
5954 var_str_list = var_str_list->next;
5955 } while (var_str_list);
5956 if (p)
5957 goto value;
5958 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005959 p = lookupvar(name);
5960 value:
5961 if (!p)
5962 return -1;
5963
5964 len = strlen(p);
5965 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5966 memtodest(p, len, syntax, quotes);
5967 return len;
5968 }
5969
5970 if (subtype == VSPLUS || subtype == VSLENGTH)
5971 STADJUST(-len, expdest);
5972 return len;
5973}
5974
5975/*
5976 * Expand a variable, and return a pointer to the next character in the
5977 * input string.
5978 */
5979static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005980evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005982 char varflags;
5983 char subtype;
5984 char quoted;
5985 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986 char *var;
5987 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 int startloc;
5989 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005990
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005991 varflags = *p++;
5992 subtype = varflags & VSTYPE;
5993 quoted = varflags & VSQUOTE;
5994 var = p;
5995 easy = (!quoted || (*var == '@' && shellparam.nparam));
5996 startloc = expdest - (char *)stackblock();
5997 p = strchr(p, '=') + 1;
5998
5999 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006000 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006001 if (varflags & VSNUL)
6002 varlen--;
6003
6004 if (subtype == VSPLUS) {
6005 varlen = -1 - varlen;
6006 goto vsplus;
6007 }
6008
6009 if (subtype == VSMINUS) {
6010 vsplus:
6011 if (varlen < 0) {
6012 argstr(
6013 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006014 (quoted ? EXP_QWORD : EXP_WORD),
6015 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006016 );
6017 goto end;
6018 }
6019 if (easy)
6020 goto record;
6021 goto end;
6022 }
6023
6024 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6025 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006026 if (subevalvar(p, var, /* strloc: */ 0,
6027 subtype, startloc, varflags,
6028 /* quotes: */ 0,
6029 var_str_list)
6030 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006031 varflags &= ~VSNUL;
6032 /*
6033 * Remove any recorded regions beyond
6034 * start of variable
6035 */
6036 removerecordregions(startloc);
6037 goto again;
6038 }
6039 goto end;
6040 }
6041 if (easy)
6042 goto record;
6043 goto end;
6044 }
6045
6046 if (varlen < 0 && uflag)
6047 varunset(p, var, 0, 0);
6048
6049 if (subtype == VSLENGTH) {
6050 cvtnum(varlen > 0 ? varlen : 0);
6051 goto record;
6052 }
6053
6054 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006055 if (easy)
6056 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006057 goto end;
6058 }
6059
6060#if DEBUG
6061 switch (subtype) {
6062 case VSTRIMLEFT:
6063 case VSTRIMLEFTMAX:
6064 case VSTRIMRIGHT:
6065 case VSTRIMRIGHTMAX:
6066 break;
6067 default:
6068 abort();
6069 }
6070#endif
6071
6072 if (varlen >= 0) {
6073 /*
6074 * Terminate the string and start recording the pattern
6075 * right after it
6076 */
6077 STPUTC('\0', expdest);
6078 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006079 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6080 startloc, varflags,
6081 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6082 var_str_list)
6083 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006084 int amount = expdest - (
6085 (char *)stackblock() + patloc - 1
6086 );
6087 STADJUST(-amount, expdest);
6088 }
6089 /* Remove any recorded regions beyond start of variable */
6090 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006091 record:
6092 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006093 }
6094
6095 end:
6096 if (subtype != VSNORMAL) { /* skip to end of alternative */
6097 int nesting = 1;
6098 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006099 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006100 if (c == CTLESC)
6101 p++;
6102 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6103 if (varlen >= 0)
6104 argbackq = argbackq->next;
6105 } else if (c == CTLVAR) {
6106 if ((*p++ & VSTYPE) != VSNORMAL)
6107 nesting++;
6108 } else if (c == CTLENDVAR) {
6109 if (--nesting == 0)
6110 break;
6111 }
6112 }
6113 }
6114 return p;
6115}
6116
6117/*
6118 * Break the argument string into pieces based upon IFS and add the
6119 * strings to the argument list. The regions of the string to be
6120 * searched for IFS characters have been stored by recordregion.
6121 */
6122static void
6123ifsbreakup(char *string, struct arglist *arglist)
6124{
6125 struct ifsregion *ifsp;
6126 struct strlist *sp;
6127 char *start;
6128 char *p;
6129 char *q;
6130 const char *ifs, *realifs;
6131 int ifsspc;
6132 int nulonly;
6133
6134 start = string;
6135 if (ifslastp != NULL) {
6136 ifsspc = 0;
6137 nulonly = 0;
6138 realifs = ifsset() ? ifsval() : defifs;
6139 ifsp = &ifsfirst;
6140 do {
6141 p = string + ifsp->begoff;
6142 nulonly = ifsp->nulonly;
6143 ifs = nulonly ? nullstr : realifs;
6144 ifsspc = 0;
6145 while (p < string + ifsp->endoff) {
6146 q = p;
6147 if (*p == CTLESC)
6148 p++;
6149 if (!strchr(ifs, *p)) {
6150 p++;
6151 continue;
6152 }
6153 if (!nulonly)
6154 ifsspc = (strchr(defifs, *p) != NULL);
6155 /* Ignore IFS whitespace at start */
6156 if (q == start && ifsspc) {
6157 p++;
6158 start = p;
6159 continue;
6160 }
6161 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006162 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006163 sp->text = start;
6164 *arglist->lastp = sp;
6165 arglist->lastp = &sp->next;
6166 p++;
6167 if (!nulonly) {
6168 for (;;) {
6169 if (p >= string + ifsp->endoff) {
6170 break;
6171 }
6172 q = p;
6173 if (*p == CTLESC)
6174 p++;
6175 if (strchr(ifs, *p) == NULL ) {
6176 p = q;
6177 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006178 }
6179 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006180 if (ifsspc) {
6181 p++;
6182 ifsspc = 0;
6183 } else {
6184 p = q;
6185 break;
6186 }
6187 } else
6188 p++;
6189 }
6190 }
6191 start = p;
6192 } /* while */
6193 ifsp = ifsp->next;
6194 } while (ifsp != NULL);
6195 if (nulonly)
6196 goto add;
6197 }
6198
6199 if (!*start)
6200 return;
6201
6202 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006203 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006204 sp->text = start;
6205 *arglist->lastp = sp;
6206 arglist->lastp = &sp->next;
6207}
6208
6209static void
6210ifsfree(void)
6211{
6212 struct ifsregion *p;
6213
6214 INT_OFF;
6215 p = ifsfirst.next;
6216 do {
6217 struct ifsregion *ifsp;
6218 ifsp = p->next;
6219 free(p);
6220 p = ifsp;
6221 } while (p);
6222 ifslastp = NULL;
6223 ifsfirst.next = NULL;
6224 INT_ON;
6225}
6226
6227/*
6228 * Add a file name to the list.
6229 */
6230static void
6231addfname(const char *name)
6232{
6233 struct strlist *sp;
6234
Denis Vlasenko597906c2008-02-20 16:38:54 +00006235 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006236 sp->text = ststrdup(name);
6237 *exparg.lastp = sp;
6238 exparg.lastp = &sp->next;
6239}
6240
6241static char *expdir;
6242
6243/*
6244 * Do metacharacter (i.e. *, ?, [...]) expansion.
6245 */
6246static void
6247expmeta(char *enddir, char *name)
6248{
6249 char *p;
6250 const char *cp;
6251 char *start;
6252 char *endname;
6253 int metaflag;
6254 struct stat statb;
6255 DIR *dirp;
6256 struct dirent *dp;
6257 int atend;
6258 int matchdot;
6259
6260 metaflag = 0;
6261 start = name;
6262 for (p = name; *p; p++) {
6263 if (*p == '*' || *p == '?')
6264 metaflag = 1;
6265 else if (*p == '[') {
6266 char *q = p + 1;
6267 if (*q == '!')
6268 q++;
6269 for (;;) {
6270 if (*q == '\\')
6271 q++;
6272 if (*q == '/' || *q == '\0')
6273 break;
6274 if (*++q == ']') {
6275 metaflag = 1;
6276 break;
6277 }
6278 }
6279 } else if (*p == '\\')
6280 p++;
6281 else if (*p == '/') {
6282 if (metaflag)
6283 goto out;
6284 start = p + 1;
6285 }
6286 }
6287 out:
6288 if (metaflag == 0) { /* we've reached the end of the file name */
6289 if (enddir != expdir)
6290 metaflag++;
6291 p = name;
6292 do {
6293 if (*p == '\\')
6294 p++;
6295 *enddir++ = *p;
6296 } while (*p++);
6297 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6298 addfname(expdir);
6299 return;
6300 }
6301 endname = p;
6302 if (name < start) {
6303 p = name;
6304 do {
6305 if (*p == '\\')
6306 p++;
6307 *enddir++ = *p++;
6308 } while (p < start);
6309 }
6310 if (enddir == expdir) {
6311 cp = ".";
6312 } else if (enddir == expdir + 1 && *expdir == '/') {
6313 cp = "/";
6314 } else {
6315 cp = expdir;
6316 enddir[-1] = '\0';
6317 }
6318 dirp = opendir(cp);
6319 if (dirp == NULL)
6320 return;
6321 if (enddir != expdir)
6322 enddir[-1] = '/';
6323 if (*endname == 0) {
6324 atend = 1;
6325 } else {
6326 atend = 0;
6327 *endname++ = '\0';
6328 }
6329 matchdot = 0;
6330 p = start;
6331 if (*p == '\\')
6332 p++;
6333 if (*p == '.')
6334 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006335 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 if (dp->d_name[0] == '.' && ! matchdot)
6337 continue;
6338 if (pmatch(start, dp->d_name)) {
6339 if (atend) {
6340 strcpy(enddir, dp->d_name);
6341 addfname(expdir);
6342 } else {
6343 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6344 continue;
6345 p[-1] = '/';
6346 expmeta(p, endname);
6347 }
6348 }
6349 }
6350 closedir(dirp);
6351 if (! atend)
6352 endname[-1] = '/';
6353}
6354
6355static struct strlist *
6356msort(struct strlist *list, int len)
6357{
6358 struct strlist *p, *q = NULL;
6359 struct strlist **lpp;
6360 int half;
6361 int n;
6362
6363 if (len <= 1)
6364 return list;
6365 half = len >> 1;
6366 p = list;
6367 for (n = half; --n >= 0; ) {
6368 q = p;
6369 p = p->next;
6370 }
6371 q->next = NULL; /* terminate first half of list */
6372 q = msort(list, half); /* sort first half of list */
6373 p = msort(p, len - half); /* sort second half */
6374 lpp = &list;
6375 for (;;) {
6376#if ENABLE_LOCALE_SUPPORT
6377 if (strcoll(p->text, q->text) < 0)
6378#else
6379 if (strcmp(p->text, q->text) < 0)
6380#endif
6381 {
6382 *lpp = p;
6383 lpp = &p->next;
6384 p = *lpp;
6385 if (p == NULL) {
6386 *lpp = q;
6387 break;
6388 }
6389 } else {
6390 *lpp = q;
6391 lpp = &q->next;
6392 q = *lpp;
6393 if (q == NULL) {
6394 *lpp = p;
6395 break;
6396 }
6397 }
6398 }
6399 return list;
6400}
6401
6402/*
6403 * Sort the results of file name expansion. It calculates the number of
6404 * strings to sort and then calls msort (short for merge sort) to do the
6405 * work.
6406 */
6407static struct strlist *
6408expsort(struct strlist *str)
6409{
6410 int len;
6411 struct strlist *sp;
6412
6413 len = 0;
6414 for (sp = str; sp; sp = sp->next)
6415 len++;
6416 return msort(str, len);
6417}
6418
6419static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006420expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006421{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006422 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006423 '*', '?', '[', 0
6424 };
6425 /* TODO - EXP_REDIR */
6426
6427 while (str) {
6428 struct strlist **savelastp;
6429 struct strlist *sp;
6430 char *p;
6431
6432 if (fflag)
6433 goto nometa;
6434 if (!strpbrk(str->text, metachars))
6435 goto nometa;
6436 savelastp = exparg.lastp;
6437
6438 INT_OFF;
6439 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6440 {
6441 int i = strlen(str->text);
6442 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6443 }
6444
6445 expmeta(expdir, p);
6446 free(expdir);
6447 if (p != str->text)
6448 free(p);
6449 INT_ON;
6450 if (exparg.lastp == savelastp) {
6451 /*
6452 * no matches
6453 */
6454 nometa:
6455 *exparg.lastp = str;
6456 rmescapes(str->text);
6457 exparg.lastp = &str->next;
6458 } else {
6459 *exparg.lastp = NULL;
6460 *savelastp = sp = expsort(*savelastp);
6461 while (sp->next != NULL)
6462 sp = sp->next;
6463 exparg.lastp = &sp->next;
6464 }
6465 str = str->next;
6466 }
6467}
6468
6469/*
6470 * Perform variable substitution and command substitution on an argument,
6471 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6472 * perform splitting and file name expansion. When arglist is NULL, perform
6473 * here document expansion.
6474 */
6475static void
6476expandarg(union node *arg, struct arglist *arglist, int flag)
6477{
6478 struct strlist *sp;
6479 char *p;
6480
6481 argbackq = arg->narg.backquote;
6482 STARTSTACKSTR(expdest);
6483 ifsfirst.next = NULL;
6484 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006485 argstr(arg->narg.text, flag,
6486 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006487 p = _STPUTC('\0', expdest);
6488 expdest = p - 1;
6489 if (arglist == NULL) {
6490 return; /* here document expanded */
6491 }
6492 p = grabstackstr(p);
6493 exparg.lastp = &exparg.list;
6494 /*
6495 * TODO - EXP_REDIR
6496 */
6497 if (flag & EXP_FULL) {
6498 ifsbreakup(p, &exparg);
6499 *exparg.lastp = NULL;
6500 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006501 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006502 } else {
6503 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6504 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006505 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006506 sp->text = p;
6507 *exparg.lastp = sp;
6508 exparg.lastp = &sp->next;
6509 }
6510 if (ifsfirst.next)
6511 ifsfree();
6512 *exparg.lastp = NULL;
6513 if (exparg.list) {
6514 *arglist->lastp = exparg.list;
6515 arglist->lastp = exparg.lastp;
6516 }
6517}
6518
6519/*
6520 * Expand shell variables and backquotes inside a here document.
6521 */
6522static void
6523expandhere(union node *arg, int fd)
6524{
6525 herefd = fd;
6526 expandarg(arg, (struct arglist *)NULL, 0);
6527 full_write(fd, stackblock(), expdest - (char *)stackblock());
6528}
6529
6530/*
6531 * Returns true if the pattern matches the string.
6532 */
6533static int
6534patmatch(char *pattern, const char *string)
6535{
6536 return pmatch(preglob(pattern, 0, 0), string);
6537}
6538
6539/*
6540 * See if a pattern matches in a case statement.
6541 */
6542static int
6543casematch(union node *pattern, char *val)
6544{
6545 struct stackmark smark;
6546 int result;
6547
6548 setstackmark(&smark);
6549 argbackq = pattern->narg.backquote;
6550 STARTSTACKSTR(expdest);
6551 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006552 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6553 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554 STACKSTRNUL(expdest);
6555 result = patmatch(stackblock(), val);
6556 popstackmark(&smark);
6557 return result;
6558}
6559
6560
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006561/* ============ find_command */
6562
6563struct builtincmd {
6564 const char *name;
6565 int (*builtin)(int, char **);
6566 /* unsigned flags; */
6567};
6568#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006569/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006570 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006571#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006572#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006573
6574struct cmdentry {
6575 int cmdtype;
6576 union param {
6577 int index;
6578 const struct builtincmd *cmd;
6579 struct funcnode *func;
6580 } u;
6581};
6582/* values of cmdtype */
6583#define CMDUNKNOWN -1 /* no entry in table for command */
6584#define CMDNORMAL 0 /* command is an executable program */
6585#define CMDFUNCTION 1 /* command is a shell function */
6586#define CMDBUILTIN 2 /* command is a shell builtin */
6587
6588/* action to find_command() */
6589#define DO_ERR 0x01 /* prints errors */
6590#define DO_ABS 0x02 /* checks absolute paths */
6591#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6592#define DO_ALTPATH 0x08 /* using alternate path */
6593#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6594
6595static void find_command(char *, struct cmdentry *, int, const char *);
6596
6597
6598/* ============ Hashing commands */
6599
6600/*
6601 * When commands are first encountered, they are entered in a hash table.
6602 * This ensures that a full path search will not have to be done for them
6603 * on each invocation.
6604 *
6605 * We should investigate converting to a linear search, even though that
6606 * would make the command name "hash" a misnomer.
6607 */
6608
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006609#define ARB 1 /* actual size determined at run time */
6610
6611struct tblentry {
6612 struct tblentry *next; /* next entry in hash chain */
6613 union param param; /* definition of builtin function */
6614 short cmdtype; /* index identifying command */
6615 char rehash; /* if set, cd done since entry created */
6616 char cmdname[ARB]; /* name of command */
6617};
6618
Denis Vlasenko01631112007-12-16 17:20:38 +00006619static struct tblentry **cmdtable;
6620#define INIT_G_cmdtable() do { \
6621 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6622} while (0)
6623
6624static int builtinloc = -1; /* index in path of %builtin, or -1 */
6625
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006626
6627static void
6628tryexec(char *cmd, char **argv, char **envp)
6629{
6630 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006631
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006632#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006633 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006634 int a = find_applet_by_name(cmd);
6635 if (a >= 0) {
6636 if (APPLET_IS_NOEXEC(a))
6637 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006638 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006639 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006640 /* If they called chroot or otherwise made the binary no longer
6641 * executable, fall through */
6642 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006643 }
6644#endif
6645
6646 repeat:
6647#ifdef SYSV
6648 do {
6649 execve(cmd, argv, envp);
6650 } while (errno == EINTR);
6651#else
6652 execve(cmd, argv, envp);
6653#endif
6654 if (repeated++) {
6655 free(argv);
6656 } else if (errno == ENOEXEC) {
6657 char **ap;
6658 char **new;
6659
6660 for (ap = argv; *ap; ap++)
6661 ;
6662 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6663 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006664 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006665 ap += 2;
6666 argv++;
6667 while ((*ap++ = *argv++))
Denis Vlasenko597906c2008-02-20 16:38:54 +00006668 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006669 argv = new;
6670 goto repeat;
6671 }
6672}
6673
6674/*
6675 * Exec a program. Never returns. If you change this routine, you may
6676 * have to change the find_command routine as well.
6677 */
6678#define environment() listvars(VEXPORT, VUNSET, 0)
6679static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6680static void
6681shellexec(char **argv, const char *path, int idx)
6682{
6683 char *cmdname;
6684 int e;
6685 char **envp;
6686 int exerrno;
6687
6688 clearredir(1);
6689 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006690 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006691#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006692 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006693#endif
6694 ) {
6695 tryexec(argv[0], argv, envp);
6696 e = errno;
6697 } else {
6698 e = ENOENT;
6699 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6700 if (--idx < 0 && pathopt == NULL) {
6701 tryexec(cmdname, argv, envp);
6702 if (errno != ENOENT && errno != ENOTDIR)
6703 e = errno;
6704 }
6705 stunalloc(cmdname);
6706 }
6707 }
6708
6709 /* Map to POSIX errors */
6710 switch (e) {
6711 case EACCES:
6712 exerrno = 126;
6713 break;
6714 case ENOENT:
6715 exerrno = 127;
6716 break;
6717 default:
6718 exerrno = 2;
6719 break;
6720 }
6721 exitstatus = exerrno;
6722 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6723 argv[0], e, suppressint ));
6724 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6725 /* NOTREACHED */
6726}
6727
6728static void
6729printentry(struct tblentry *cmdp)
6730{
6731 int idx;
6732 const char *path;
6733 char *name;
6734
6735 idx = cmdp->param.index;
6736 path = pathval();
6737 do {
6738 name = padvance(&path, cmdp->cmdname);
6739 stunalloc(name);
6740 } while (--idx >= 0);
6741 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6742}
6743
6744/*
6745 * Clear out command entries. The argument specifies the first entry in
6746 * PATH which has changed.
6747 */
6748static void
6749clearcmdentry(int firstchange)
6750{
6751 struct tblentry **tblp;
6752 struct tblentry **pp;
6753 struct tblentry *cmdp;
6754
6755 INT_OFF;
6756 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6757 pp = tblp;
6758 while ((cmdp = *pp) != NULL) {
6759 if ((cmdp->cmdtype == CMDNORMAL &&
6760 cmdp->param.index >= firstchange)
6761 || (cmdp->cmdtype == CMDBUILTIN &&
6762 builtinloc >= firstchange)
6763 ) {
6764 *pp = cmdp->next;
6765 free(cmdp);
6766 } else {
6767 pp = &cmdp->next;
6768 }
6769 }
6770 }
6771 INT_ON;
6772}
6773
6774/*
6775 * Locate a command in the command hash table. If "add" is nonzero,
6776 * add the command to the table if it is not already present. The
6777 * variable "lastcmdentry" is set to point to the address of the link
6778 * pointing to the entry, so that delete_cmd_entry can delete the
6779 * entry.
6780 *
6781 * Interrupts must be off if called with add != 0.
6782 */
6783static struct tblentry **lastcmdentry;
6784
6785static struct tblentry *
6786cmdlookup(const char *name, int add)
6787{
6788 unsigned int hashval;
6789 const char *p;
6790 struct tblentry *cmdp;
6791 struct tblentry **pp;
6792
6793 p = name;
6794 hashval = (unsigned char)*p << 4;
6795 while (*p)
6796 hashval += (unsigned char)*p++;
6797 hashval &= 0x7FFF;
6798 pp = &cmdtable[hashval % CMDTABLESIZE];
6799 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6800 if (strcmp(cmdp->cmdname, name) == 0)
6801 break;
6802 pp = &cmdp->next;
6803 }
6804 if (add && cmdp == NULL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00006805 cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006806 + strlen(name) + 1);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006807 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006808 cmdp->cmdtype = CMDUNKNOWN;
6809 strcpy(cmdp->cmdname, name);
6810 }
6811 lastcmdentry = pp;
6812 return cmdp;
6813}
6814
6815/*
6816 * Delete the command entry returned on the last lookup.
6817 */
6818static void
6819delete_cmd_entry(void)
6820{
6821 struct tblentry *cmdp;
6822
6823 INT_OFF;
6824 cmdp = *lastcmdentry;
6825 *lastcmdentry = cmdp->next;
6826 if (cmdp->cmdtype == CMDFUNCTION)
6827 freefunc(cmdp->param.func);
6828 free(cmdp);
6829 INT_ON;
6830}
6831
6832/*
6833 * Add a new command entry, replacing any existing command entry for
6834 * the same name - except special builtins.
6835 */
6836static void
6837addcmdentry(char *name, struct cmdentry *entry)
6838{
6839 struct tblentry *cmdp;
6840
6841 cmdp = cmdlookup(name, 1);
6842 if (cmdp->cmdtype == CMDFUNCTION) {
6843 freefunc(cmdp->param.func);
6844 }
6845 cmdp->cmdtype = entry->cmdtype;
6846 cmdp->param = entry->u;
6847 cmdp->rehash = 0;
6848}
6849
6850static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00006851hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006852{
6853 struct tblentry **pp;
6854 struct tblentry *cmdp;
6855 int c;
6856 struct cmdentry entry;
6857 char *name;
6858
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006859 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860 clearcmdentry(0);
6861 return 0;
6862 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006863
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006864 if (*argptr == NULL) {
6865 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6866 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6867 if (cmdp->cmdtype == CMDNORMAL)
6868 printentry(cmdp);
6869 }
6870 }
6871 return 0;
6872 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006873
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006874 c = 0;
6875 while ((name = *argptr) != NULL) {
6876 cmdp = cmdlookup(name, 0);
6877 if (cmdp != NULL
6878 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006879 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6880 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006881 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006882 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006883 find_command(name, &entry, DO_ERR, pathval());
6884 if (entry.cmdtype == CMDUNKNOWN)
6885 c = 1;
6886 argptr++;
6887 }
6888 return c;
6889}
6890
6891/*
6892 * Called when a cd is done. Marks all commands so the next time they
6893 * are executed they will be rehashed.
6894 */
6895static void
6896hashcd(void)
6897{
6898 struct tblentry **pp;
6899 struct tblentry *cmdp;
6900
6901 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6902 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006903 if (cmdp->cmdtype == CMDNORMAL
6904 || (cmdp->cmdtype == CMDBUILTIN
6905 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6906 && builtinloc > 0)
6907 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006908 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006909 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006910 }
6911 }
6912}
6913
6914/*
6915 * Fix command hash table when PATH changed.
6916 * Called before PATH is changed. The argument is the new value of PATH;
6917 * pathval() still returns the old value at this point.
6918 * Called with interrupts off.
6919 */
6920static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006921changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006922{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006923 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006924 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006925 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006926 int idx_bltin;
6927
6928 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006929 firstchange = 9999; /* assume no change */
6930 idx = 0;
6931 idx_bltin = -1;
6932 for (;;) {
6933 if (*old != *new) {
6934 firstchange = idx;
6935 if ((*old == '\0' && *new == ':')
6936 || (*old == ':' && *new == '\0'))
6937 firstchange++;
6938 old = new; /* ignore subsequent differences */
6939 }
6940 if (*new == '\0')
6941 break;
6942 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6943 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006944 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006945 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006946 new++, old++;
6947 }
6948 if (builtinloc < 0 && idx_bltin >= 0)
6949 builtinloc = idx_bltin; /* zap builtins */
6950 if (builtinloc >= 0 && idx_bltin < 0)
6951 firstchange = 0;
6952 clearcmdentry(firstchange);
6953 builtinloc = idx_bltin;
6954}
6955
6956#define TEOF 0
6957#define TNL 1
6958#define TREDIR 2
6959#define TWORD 3
6960#define TSEMI 4
6961#define TBACKGND 5
6962#define TAND 6
6963#define TOR 7
6964#define TPIPE 8
6965#define TLP 9
6966#define TRP 10
6967#define TENDCASE 11
6968#define TENDBQUOTE 12
6969#define TNOT 13
6970#define TCASE 14
6971#define TDO 15
6972#define TDONE 16
6973#define TELIF 17
6974#define TELSE 18
6975#define TESAC 19
6976#define TFI 20
6977#define TFOR 21
6978#define TIF 22
6979#define TIN 23
6980#define TTHEN 24
6981#define TUNTIL 25
6982#define TWHILE 26
6983#define TBEGIN 27
6984#define TEND 28
6985
6986/* first char is indicating which tokens mark the end of a list */
6987static const char *const tokname_array[] = {
6988 "\1end of file",
6989 "\0newline",
6990 "\0redirection",
6991 "\0word",
6992 "\0;",
6993 "\0&",
6994 "\0&&",
6995 "\0||",
6996 "\0|",
6997 "\0(",
6998 "\1)",
6999 "\1;;",
7000 "\1`",
7001#define KWDOFFSET 13
7002 /* the following are keywords */
7003 "\0!",
7004 "\0case",
7005 "\1do",
7006 "\1done",
7007 "\1elif",
7008 "\1else",
7009 "\1esac",
7010 "\1fi",
7011 "\0for",
7012 "\0if",
7013 "\0in",
7014 "\1then",
7015 "\0until",
7016 "\0while",
7017 "\0{",
7018 "\1}",
7019};
7020
7021static const char *
7022tokname(int tok)
7023{
7024 static char buf[16];
7025
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007026//try this:
7027//if (tok < TSEMI) return tokname_array[tok] + 1;
7028//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7029//return buf;
7030
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007031 if (tok >= TSEMI)
7032 buf[0] = '"';
7033 sprintf(buf + (tok >= TSEMI), "%s%c",
7034 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7035 return buf;
7036}
7037
7038/* Wrapper around strcmp for qsort/bsearch/... */
7039static int
7040pstrcmp(const void *a, const void *b)
7041{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007042 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043}
7044
7045static const char *const *
7046findkwd(const char *s)
7047{
7048 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007049 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7050 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007051}
7052
7053/*
7054 * Locate and print what a word is...
7055 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007056static int
7057describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007058{
7059 struct cmdentry entry;
7060 struct tblentry *cmdp;
7061#if ENABLE_ASH_ALIAS
7062 const struct alias *ap;
7063#endif
7064 const char *path = pathval();
7065
7066 if (describe_command_verbose) {
7067 out1str(command);
7068 }
7069
7070 /* First look at the keywords */
7071 if (findkwd(command)) {
7072 out1str(describe_command_verbose ? " is a shell keyword" : command);
7073 goto out;
7074 }
7075
7076#if ENABLE_ASH_ALIAS
7077 /* Then look at the aliases */
7078 ap = lookupalias(command, 0);
7079 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007080 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007081 out1str("alias ");
7082 printalias(ap);
7083 return 0;
7084 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007085 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007086 goto out;
7087 }
7088#endif
7089 /* Then check if it is a tracked alias */
7090 cmdp = cmdlookup(command, 0);
7091 if (cmdp != NULL) {
7092 entry.cmdtype = cmdp->cmdtype;
7093 entry.u = cmdp->param;
7094 } else {
7095 /* Finally use brute force */
7096 find_command(command, &entry, DO_ABS, path);
7097 }
7098
7099 switch (entry.cmdtype) {
7100 case CMDNORMAL: {
7101 int j = entry.u.index;
7102 char *p;
7103 if (j == -1) {
7104 p = command;
7105 } else {
7106 do {
7107 p = padvance(&path, command);
7108 stunalloc(p);
7109 } while (--j >= 0);
7110 }
7111 if (describe_command_verbose) {
7112 out1fmt(" is%s %s",
7113 (cmdp ? " a tracked alias for" : nullstr), p
7114 );
7115 } else {
7116 out1str(p);
7117 }
7118 break;
7119 }
7120
7121 case CMDFUNCTION:
7122 if (describe_command_verbose) {
7123 out1str(" is a shell function");
7124 } else {
7125 out1str(command);
7126 }
7127 break;
7128
7129 case CMDBUILTIN:
7130 if (describe_command_verbose) {
7131 out1fmt(" is a %sshell builtin",
7132 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7133 "special " : nullstr
7134 );
7135 } else {
7136 out1str(command);
7137 }
7138 break;
7139
7140 default:
7141 if (describe_command_verbose) {
7142 out1str(": not found\n");
7143 }
7144 return 127;
7145 }
7146 out:
7147 outstr("\n", stdout);
7148 return 0;
7149}
7150
7151static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007152typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007153{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007154 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007155 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007156 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007157
Denis Vlasenko46846e22007-05-20 13:08:31 +00007158 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007159 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007160 i++;
7161 verbose = 0;
7162 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007163 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007164 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007165 }
7166 return err;
7167}
7168
7169#if ENABLE_ASH_CMDCMD
7170static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007171commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007172{
7173 int c;
7174 enum {
7175 VERIFY_BRIEF = 1,
7176 VERIFY_VERBOSE = 2,
7177 } verify = 0;
7178
7179 while ((c = nextopt("pvV")) != '\0')
7180 if (c == 'V')
7181 verify |= VERIFY_VERBOSE;
7182 else if (c == 'v')
7183 verify |= VERIFY_BRIEF;
7184#if DEBUG
7185 else if (c != 'p')
7186 abort();
7187#endif
7188 if (verify)
7189 return describe_command(*argptr, verify - VERIFY_BRIEF);
7190
7191 return 0;
7192}
7193#endif
7194
7195
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007196/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007197
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007198static int funcblocksize; /* size of structures in function */
7199static int funcstringsize; /* size of strings in node */
7200static void *funcblock; /* block to allocate function from */
7201static char *funcstring; /* block to allocate strings from */
7202
Eric Andersencb57d552001-06-28 07:25:16 +00007203/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007204#define EV_EXIT 01 /* exit after evaluating tree */
7205#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7206#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007207
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007208static const short nodesize[26] = {
7209 SHELL_ALIGN(sizeof(struct ncmd)),
7210 SHELL_ALIGN(sizeof(struct npipe)),
7211 SHELL_ALIGN(sizeof(struct nredir)),
7212 SHELL_ALIGN(sizeof(struct nredir)),
7213 SHELL_ALIGN(sizeof(struct nredir)),
7214 SHELL_ALIGN(sizeof(struct nbinary)),
7215 SHELL_ALIGN(sizeof(struct nbinary)),
7216 SHELL_ALIGN(sizeof(struct nbinary)),
7217 SHELL_ALIGN(sizeof(struct nif)),
7218 SHELL_ALIGN(sizeof(struct nbinary)),
7219 SHELL_ALIGN(sizeof(struct nbinary)),
7220 SHELL_ALIGN(sizeof(struct nfor)),
7221 SHELL_ALIGN(sizeof(struct ncase)),
7222 SHELL_ALIGN(sizeof(struct nclist)),
7223 SHELL_ALIGN(sizeof(struct narg)),
7224 SHELL_ALIGN(sizeof(struct narg)),
7225 SHELL_ALIGN(sizeof(struct nfile)),
7226 SHELL_ALIGN(sizeof(struct nfile)),
7227 SHELL_ALIGN(sizeof(struct nfile)),
7228 SHELL_ALIGN(sizeof(struct nfile)),
7229 SHELL_ALIGN(sizeof(struct nfile)),
7230 SHELL_ALIGN(sizeof(struct ndup)),
7231 SHELL_ALIGN(sizeof(struct ndup)),
7232 SHELL_ALIGN(sizeof(struct nhere)),
7233 SHELL_ALIGN(sizeof(struct nhere)),
7234 SHELL_ALIGN(sizeof(struct nnot)),
7235};
7236
7237static void calcsize(union node *n);
7238
7239static void
7240sizenodelist(struct nodelist *lp)
7241{
7242 while (lp) {
7243 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7244 calcsize(lp->n);
7245 lp = lp->next;
7246 }
7247}
7248
7249static void
7250calcsize(union node *n)
7251{
7252 if (n == NULL)
7253 return;
7254 funcblocksize += nodesize[n->type];
7255 switch (n->type) {
7256 case NCMD:
7257 calcsize(n->ncmd.redirect);
7258 calcsize(n->ncmd.args);
7259 calcsize(n->ncmd.assign);
7260 break;
7261 case NPIPE:
7262 sizenodelist(n->npipe.cmdlist);
7263 break;
7264 case NREDIR:
7265 case NBACKGND:
7266 case NSUBSHELL:
7267 calcsize(n->nredir.redirect);
7268 calcsize(n->nredir.n);
7269 break;
7270 case NAND:
7271 case NOR:
7272 case NSEMI:
7273 case NWHILE:
7274 case NUNTIL:
7275 calcsize(n->nbinary.ch2);
7276 calcsize(n->nbinary.ch1);
7277 break;
7278 case NIF:
7279 calcsize(n->nif.elsepart);
7280 calcsize(n->nif.ifpart);
7281 calcsize(n->nif.test);
7282 break;
7283 case NFOR:
7284 funcstringsize += strlen(n->nfor.var) + 1;
7285 calcsize(n->nfor.body);
7286 calcsize(n->nfor.args);
7287 break;
7288 case NCASE:
7289 calcsize(n->ncase.cases);
7290 calcsize(n->ncase.expr);
7291 break;
7292 case NCLIST:
7293 calcsize(n->nclist.body);
7294 calcsize(n->nclist.pattern);
7295 calcsize(n->nclist.next);
7296 break;
7297 case NDEFUN:
7298 case NARG:
7299 sizenodelist(n->narg.backquote);
7300 funcstringsize += strlen(n->narg.text) + 1;
7301 calcsize(n->narg.next);
7302 break;
7303 case NTO:
7304 case NCLOBBER:
7305 case NFROM:
7306 case NFROMTO:
7307 case NAPPEND:
7308 calcsize(n->nfile.fname);
7309 calcsize(n->nfile.next);
7310 break;
7311 case NTOFD:
7312 case NFROMFD:
7313 calcsize(n->ndup.vname);
7314 calcsize(n->ndup.next);
7315 break;
7316 case NHERE:
7317 case NXHERE:
7318 calcsize(n->nhere.doc);
7319 calcsize(n->nhere.next);
7320 break;
7321 case NNOT:
7322 calcsize(n->nnot.com);
7323 break;
7324 };
7325}
7326
7327static char *
7328nodeckstrdup(char *s)
7329{
7330 char *rtn = funcstring;
7331
7332 strcpy(funcstring, s);
7333 funcstring += strlen(s) + 1;
7334 return rtn;
7335}
7336
7337static union node *copynode(union node *);
7338
7339static struct nodelist *
7340copynodelist(struct nodelist *lp)
7341{
7342 struct nodelist *start;
7343 struct nodelist **lpp;
7344
7345 lpp = &start;
7346 while (lp) {
7347 *lpp = funcblock;
7348 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7349 (*lpp)->n = copynode(lp->n);
7350 lp = lp->next;
7351 lpp = &(*lpp)->next;
7352 }
7353 *lpp = NULL;
7354 return start;
7355}
7356
7357static union node *
7358copynode(union node *n)
7359{
7360 union node *new;
7361
7362 if (n == NULL)
7363 return NULL;
7364 new = funcblock;
7365 funcblock = (char *) funcblock + nodesize[n->type];
7366
7367 switch (n->type) {
7368 case NCMD:
7369 new->ncmd.redirect = copynode(n->ncmd.redirect);
7370 new->ncmd.args = copynode(n->ncmd.args);
7371 new->ncmd.assign = copynode(n->ncmd.assign);
7372 break;
7373 case NPIPE:
7374 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7375 new->npipe.backgnd = n->npipe.backgnd;
7376 break;
7377 case NREDIR:
7378 case NBACKGND:
7379 case NSUBSHELL:
7380 new->nredir.redirect = copynode(n->nredir.redirect);
7381 new->nredir.n = copynode(n->nredir.n);
7382 break;
7383 case NAND:
7384 case NOR:
7385 case NSEMI:
7386 case NWHILE:
7387 case NUNTIL:
7388 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7389 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7390 break;
7391 case NIF:
7392 new->nif.elsepart = copynode(n->nif.elsepart);
7393 new->nif.ifpart = copynode(n->nif.ifpart);
7394 new->nif.test = copynode(n->nif.test);
7395 break;
7396 case NFOR:
7397 new->nfor.var = nodeckstrdup(n->nfor.var);
7398 new->nfor.body = copynode(n->nfor.body);
7399 new->nfor.args = copynode(n->nfor.args);
7400 break;
7401 case NCASE:
7402 new->ncase.cases = copynode(n->ncase.cases);
7403 new->ncase.expr = copynode(n->ncase.expr);
7404 break;
7405 case NCLIST:
7406 new->nclist.body = copynode(n->nclist.body);
7407 new->nclist.pattern = copynode(n->nclist.pattern);
7408 new->nclist.next = copynode(n->nclist.next);
7409 break;
7410 case NDEFUN:
7411 case NARG:
7412 new->narg.backquote = copynodelist(n->narg.backquote);
7413 new->narg.text = nodeckstrdup(n->narg.text);
7414 new->narg.next = copynode(n->narg.next);
7415 break;
7416 case NTO:
7417 case NCLOBBER:
7418 case NFROM:
7419 case NFROMTO:
7420 case NAPPEND:
7421 new->nfile.fname = copynode(n->nfile.fname);
7422 new->nfile.fd = n->nfile.fd;
7423 new->nfile.next = copynode(n->nfile.next);
7424 break;
7425 case NTOFD:
7426 case NFROMFD:
7427 new->ndup.vname = copynode(n->ndup.vname);
7428 new->ndup.dupfd = n->ndup.dupfd;
7429 new->ndup.fd = n->ndup.fd;
7430 new->ndup.next = copynode(n->ndup.next);
7431 break;
7432 case NHERE:
7433 case NXHERE:
7434 new->nhere.doc = copynode(n->nhere.doc);
7435 new->nhere.fd = n->nhere.fd;
7436 new->nhere.next = copynode(n->nhere.next);
7437 break;
7438 case NNOT:
7439 new->nnot.com = copynode(n->nnot.com);
7440 break;
7441 };
7442 new->type = n->type;
7443 return new;
7444}
7445
7446/*
7447 * Make a copy of a parse tree.
7448 */
7449static struct funcnode *
7450copyfunc(union node *n)
7451{
7452 struct funcnode *f;
7453 size_t blocksize;
7454
7455 funcblocksize = offsetof(struct funcnode, n);
7456 funcstringsize = 0;
7457 calcsize(n);
7458 blocksize = funcblocksize;
7459 f = ckmalloc(blocksize + funcstringsize);
7460 funcblock = (char *) f + offsetof(struct funcnode, n);
7461 funcstring = (char *) f + blocksize;
7462 copynode(n);
7463 f->count = 0;
7464 return f;
7465}
7466
7467/*
7468 * Define a shell function.
7469 */
7470static void
7471defun(char *name, union node *func)
7472{
7473 struct cmdentry entry;
7474
7475 INT_OFF;
7476 entry.cmdtype = CMDFUNCTION;
7477 entry.u.func = copyfunc(func);
7478 addcmdentry(name, &entry);
7479 INT_ON;
7480}
7481
7482static int evalskip; /* set if we are skipping commands */
7483/* reasons for skipping commands (see comment on breakcmd routine) */
7484#define SKIPBREAK (1 << 0)
7485#define SKIPCONT (1 << 1)
7486#define SKIPFUNC (1 << 2)
7487#define SKIPFILE (1 << 3)
7488#define SKIPEVAL (1 << 4)
7489static int skipcount; /* number of levels to skip */
7490static int funcnest; /* depth of function calls */
7491
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007492/* forward decl way out to parsing code - dotrap needs it */
7493static int evalstring(char *s, int mask);
7494
7495/*
7496 * Called to execute a trap. Perhaps we should avoid entering new trap
7497 * handlers while we are executing a trap handler.
7498 */
7499static int
7500dotrap(void)
7501{
7502 char *p;
7503 char *q;
7504 int i;
7505 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007506 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007507
7508 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007509 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007510 xbarrier();
7511
7512 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7513 if (!*q)
7514 continue;
7515 *q = '\0';
7516
7517 p = trap[i + 1];
7518 if (!p)
7519 continue;
7520 skip = evalstring(p, SKIPEVAL);
7521 exitstatus = savestatus;
7522 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007523 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007524 }
7525
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007526 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007527}
7528
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007529/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007530static void evalloop(union node *, int);
7531static void evalfor(union node *, int);
7532static void evalcase(union node *, int);
7533static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007534static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007535static void evalpipe(union node *, int);
7536static void evalcommand(union node *, int);
7537static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007538static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007539
Eric Andersen62483552001-07-10 06:09:16 +00007540/*
Eric Andersenc470f442003-07-28 09:56:35 +00007541 * Evaluate a parse tree. The value is left in the global variable
7542 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007543 */
Eric Andersenc470f442003-07-28 09:56:35 +00007544static void
7545evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007546{
Eric Andersenc470f442003-07-28 09:56:35 +00007547 int checkexit = 0;
7548 void (*evalfn)(union node *, int);
7549 unsigned isor;
7550 int status;
7551 if (n == NULL) {
7552 TRACE(("evaltree(NULL) called\n"));
7553 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007554 }
Eric Andersenc470f442003-07-28 09:56:35 +00007555 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007556 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007557 switch (n->type) {
7558 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007559#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007560 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007561 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007562 break;
7563#endif
7564 case NNOT:
7565 evaltree(n->nnot.com, EV_TESTED);
7566 status = !exitstatus;
7567 goto setstatus;
7568 case NREDIR:
7569 expredir(n->nredir.redirect);
7570 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7571 if (!status) {
7572 evaltree(n->nredir.n, flags & EV_TESTED);
7573 status = exitstatus;
7574 }
7575 popredir(0);
7576 goto setstatus;
7577 case NCMD:
7578 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007579 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007580 if (eflag && !(flags & EV_TESTED))
7581 checkexit = ~0;
7582 goto calleval;
7583 case NFOR:
7584 evalfn = evalfor;
7585 goto calleval;
7586 case NWHILE:
7587 case NUNTIL:
7588 evalfn = evalloop;
7589 goto calleval;
7590 case NSUBSHELL:
7591 case NBACKGND:
7592 evalfn = evalsubshell;
7593 goto calleval;
7594 case NPIPE:
7595 evalfn = evalpipe;
7596 goto checkexit;
7597 case NCASE:
7598 evalfn = evalcase;
7599 goto calleval;
7600 case NAND:
7601 case NOR:
7602 case NSEMI:
7603#if NAND + 1 != NOR
7604#error NAND + 1 != NOR
7605#endif
7606#if NOR + 1 != NSEMI
7607#error NOR + 1 != NSEMI
7608#endif
7609 isor = n->type - NAND;
7610 evaltree(
7611 n->nbinary.ch1,
7612 (flags | ((isor >> 1) - 1)) & EV_TESTED
7613 );
7614 if (!exitstatus == isor)
7615 break;
7616 if (!evalskip) {
7617 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007618 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007619 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007620 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007621 evalfn(n, flags);
7622 break;
7623 }
7624 break;
7625 case NIF:
7626 evaltree(n->nif.test, EV_TESTED);
7627 if (evalskip)
7628 break;
7629 if (exitstatus == 0) {
7630 n = n->nif.ifpart;
7631 goto evaln;
7632 } else if (n->nif.elsepart) {
7633 n = n->nif.elsepart;
7634 goto evaln;
7635 }
7636 goto success;
7637 case NDEFUN:
7638 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007639 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007640 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007641 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007642 exitstatus = status;
7643 break;
7644 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007645 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007646 if ((checkexit & exitstatus))
7647 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007648 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007649 goto exexit;
7650
7651 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007652 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007653 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007654 }
Eric Andersen62483552001-07-10 06:09:16 +00007655}
7656
Eric Andersenc470f442003-07-28 09:56:35 +00007657#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7658static
7659#endif
7660void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7661
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007662static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007663
7664static void
7665evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007666{
7667 int status;
7668
7669 loopnest++;
7670 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007671 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007672 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007673 int i;
7674
Eric Andersencb57d552001-06-28 07:25:16 +00007675 evaltree(n->nbinary.ch1, EV_TESTED);
7676 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007677 skipping:
7678 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007679 evalskip = 0;
7680 continue;
7681 }
7682 if (evalskip == SKIPBREAK && --skipcount <= 0)
7683 evalskip = 0;
7684 break;
7685 }
Eric Andersenc470f442003-07-28 09:56:35 +00007686 i = exitstatus;
7687 if (n->type != NWHILE)
7688 i = !i;
7689 if (i != 0)
7690 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007691 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007692 status = exitstatus;
7693 if (evalskip)
7694 goto skipping;
7695 }
7696 loopnest--;
7697 exitstatus = status;
7698}
7699
Eric Andersenc470f442003-07-28 09:56:35 +00007700static void
7701evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007702{
7703 struct arglist arglist;
7704 union node *argp;
7705 struct strlist *sp;
7706 struct stackmark smark;
7707
7708 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007709 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007710 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007711 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007712 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007713 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007714 if (evalskip)
7715 goto out;
7716 }
7717 *arglist.lastp = NULL;
7718
7719 exitstatus = 0;
7720 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007721 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007722 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007723 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007724 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007725 if (evalskip) {
7726 if (evalskip == SKIPCONT && --skipcount <= 0) {
7727 evalskip = 0;
7728 continue;
7729 }
7730 if (evalskip == SKIPBREAK && --skipcount <= 0)
7731 evalskip = 0;
7732 break;
7733 }
7734 }
7735 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007736 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007737 popstackmark(&smark);
7738}
7739
Eric Andersenc470f442003-07-28 09:56:35 +00007740static void
7741evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007742{
7743 union node *cp;
7744 union node *patp;
7745 struct arglist arglist;
7746 struct stackmark smark;
7747
7748 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007749 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007750 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007751 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007752 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007753 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7754 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007755 if (casematch(patp, arglist.list->text)) {
7756 if (evalskip == 0) {
7757 evaltree(cp->nclist.body, flags);
7758 }
7759 goto out;
7760 }
7761 }
7762 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007763 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007764 popstackmark(&smark);
7765}
7766
Eric Andersenc470f442003-07-28 09:56:35 +00007767/*
7768 * Kick off a subshell to evaluate a tree.
7769 */
Eric Andersenc470f442003-07-28 09:56:35 +00007770static void
7771evalsubshell(union node *n, int flags)
7772{
7773 struct job *jp;
7774 int backgnd = (n->type == NBACKGND);
7775 int status;
7776
7777 expredir(n->nredir.redirect);
7778 if (!backgnd && flags & EV_EXIT && !trap[0])
7779 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007780 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007781 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007782 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007783 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007784 flags |= EV_EXIT;
7785 if (backgnd)
7786 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007787 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007788 redirect(n->nredir.redirect, 0);
7789 evaltreenr(n->nredir.n, flags);
7790 /* never returns */
7791 }
7792 status = 0;
7793 if (! backgnd)
7794 status = waitforjob(jp);
7795 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007796 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007797}
7798
Eric Andersenc470f442003-07-28 09:56:35 +00007799/*
7800 * Compute the names of the files in a redirection list.
7801 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007802static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007803static void
7804expredir(union node *n)
7805{
7806 union node *redir;
7807
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007808 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007809 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007810
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007811 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00007812 fn.lastp = &fn.list;
7813 switch (redir->type) {
7814 case NFROMTO:
7815 case NFROM:
7816 case NTO:
7817 case NCLOBBER:
7818 case NAPPEND:
7819 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7820 redir->nfile.expfname = fn.list->text;
7821 break;
7822 case NFROMFD:
7823 case NTOFD:
7824 if (redir->ndup.vname) {
7825 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007826 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007827 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007828 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007829 }
7830 break;
7831 }
7832 }
7833}
7834
Eric Andersencb57d552001-06-28 07:25:16 +00007835/*
Eric Andersencb57d552001-06-28 07:25:16 +00007836 * Evaluate a pipeline. All the processes in the pipeline are children
7837 * of the process creating the pipeline. (This differs from some versions
7838 * of the shell, which make the last process in a pipeline the parent
7839 * of all the rest.)
7840 */
Eric Andersenc470f442003-07-28 09:56:35 +00007841static void
7842evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007843{
7844 struct job *jp;
7845 struct nodelist *lp;
7846 int pipelen;
7847 int prevfd;
7848 int pip[2];
7849
Eric Andersenc470f442003-07-28 09:56:35 +00007850 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007851 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007852 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007853 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007854 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007855 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007856 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00007857 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007858 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007859 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007860 pip[1] = -1;
7861 if (lp->next) {
7862 if (pipe(pip) < 0) {
7863 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007864 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007865 }
7866 }
7867 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007868 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007869 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007870 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007871 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007872 if (prevfd > 0) {
7873 dup2(prevfd, 0);
7874 close(prevfd);
7875 }
7876 if (pip[1] > 1) {
7877 dup2(pip[1], 1);
7878 close(pip[1]);
7879 }
Eric Andersenc470f442003-07-28 09:56:35 +00007880 evaltreenr(lp->n, flags);
7881 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007882 }
7883 if (prevfd >= 0)
7884 close(prevfd);
7885 prevfd = pip[0];
7886 close(pip[1]);
7887 }
Eric Andersencb57d552001-06-28 07:25:16 +00007888 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007889 exitstatus = waitforjob(jp);
7890 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007891 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007892 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007893}
7894
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007895/*
7896 * Controls whether the shell is interactive or not.
7897 */
7898static void
7899setinteractive(int on)
7900{
7901 static int is_interactive;
7902
7903 if (++on == is_interactive)
7904 return;
7905 is_interactive = on;
7906 setsignal(SIGINT);
7907 setsignal(SIGQUIT);
7908 setsignal(SIGTERM);
7909#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7910 if (is_interactive > 1) {
7911 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007912 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007913
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007914 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007915 out1fmt(
7916 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007917 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007918 "Enter 'help' for a list of built-in commands."
7919 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007920 bb_banner);
7921 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007922 }
7923 }
7924#endif
7925}
7926
7927#if ENABLE_FEATURE_EDITING_VI
7928#define setvimode(on) do { \
7929 if (on) line_input_state->flags |= VI_MODE; \
7930 else line_input_state->flags &= ~VI_MODE; \
7931} while (0)
7932#else
7933#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7934#endif
7935
7936static void
7937optschanged(void)
7938{
7939#if DEBUG
7940 opentrace();
7941#endif
7942 setinteractive(iflag);
7943 setjobctl(mflag);
7944 setvimode(viflag);
7945}
7946
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007947static struct localvar *localvars;
7948
7949/*
7950 * Called after a function returns.
7951 * Interrupts must be off.
7952 */
7953static void
7954poplocalvars(void)
7955{
7956 struct localvar *lvp;
7957 struct var *vp;
7958
7959 while ((lvp = localvars) != NULL) {
7960 localvars = lvp->next;
7961 vp = lvp->vp;
7962 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7963 if (vp == NULL) { /* $- saved */
7964 memcpy(optlist, lvp->text, sizeof(optlist));
7965 free((char*)lvp->text);
7966 optschanged();
7967 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7968 unsetvar(vp->text);
7969 } else {
7970 if (vp->func)
7971 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7972 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7973 free((char*)vp->text);
7974 vp->flags = lvp->flags;
7975 vp->text = lvp->text;
7976 }
7977 free(lvp);
7978 }
7979}
7980
7981static int
7982evalfun(struct funcnode *func, int argc, char **argv, int flags)
7983{
7984 volatile struct shparam saveparam;
7985 struct localvar *volatile savelocalvars;
7986 struct jmploc *volatile savehandler;
7987 struct jmploc jmploc;
7988 int e;
7989
7990 saveparam = shellparam;
7991 savelocalvars = localvars;
7992 e = setjmp(jmploc.loc);
7993 if (e) {
7994 goto funcdone;
7995 }
7996 INT_OFF;
7997 savehandler = exception_handler;
7998 exception_handler = &jmploc;
7999 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008000 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008001 func->count++;
8002 funcnest++;
8003 INT_ON;
8004 shellparam.nparam = argc - 1;
8005 shellparam.p = argv + 1;
8006#if ENABLE_ASH_GETOPTS
8007 shellparam.optind = 1;
8008 shellparam.optoff = -1;
8009#endif
8010 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008011 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008012 INT_OFF;
8013 funcnest--;
8014 freefunc(func);
8015 poplocalvars();
8016 localvars = savelocalvars;
8017 freeparam(&shellparam);
8018 shellparam = saveparam;
8019 exception_handler = savehandler;
8020 INT_ON;
8021 evalskip &= ~SKIPFUNC;
8022 return e;
8023}
8024
Denis Vlasenko131ae172007-02-18 13:00:19 +00008025#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008026static char **
8027parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008028{
8029 char *cp, c;
8030
8031 for (;;) {
8032 cp = *++argv;
8033 if (!cp)
8034 return 0;
8035 if (*cp++ != '-')
8036 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008037 c = *cp++;
8038 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008039 break;
8040 if (c == '-' && !*cp) {
8041 argv++;
8042 break;
8043 }
8044 do {
8045 switch (c) {
8046 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008047 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008048 break;
8049 default:
8050 /* run 'typecmd' for other options */
8051 return 0;
8052 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008053 c = *cp++;
8054 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008055 }
8056 return argv;
8057}
8058#endif
8059
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008060/*
8061 * Make a variable a local variable. When a variable is made local, it's
8062 * value and flags are saved in a localvar structure. The saved values
8063 * will be restored when the shell function returns. We handle the name
8064 * "-" as a special case.
8065 */
8066static void
8067mklocal(char *name)
8068{
8069 struct localvar *lvp;
8070 struct var **vpp;
8071 struct var *vp;
8072
8073 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008074 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008075 if (LONE_DASH(name)) {
8076 char *p;
8077 p = ckmalloc(sizeof(optlist));
8078 lvp->text = memcpy(p, optlist, sizeof(optlist));
8079 vp = NULL;
8080 } else {
8081 char *eq;
8082
8083 vpp = hashvar(name);
8084 vp = *findvar(vpp, name);
8085 eq = strchr(name, '=');
8086 if (vp == NULL) {
8087 if (eq)
8088 setvareq(name, VSTRFIXED);
8089 else
8090 setvar(name, NULL, VSTRFIXED);
8091 vp = *vpp; /* the new variable */
8092 lvp->flags = VUNSET;
8093 } else {
8094 lvp->text = vp->text;
8095 lvp->flags = vp->flags;
8096 vp->flags |= VSTRFIXED|VTEXTFIXED;
8097 if (eq)
8098 setvareq(name, 0);
8099 }
8100 }
8101 lvp->vp = vp;
8102 lvp->next = localvars;
8103 localvars = lvp;
8104 INT_ON;
8105}
8106
8107/*
8108 * The "local" command.
8109 */
8110static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008111localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008112{
8113 char *name;
8114
8115 argv = argptr;
8116 while ((name = *argv++) != NULL) {
8117 mklocal(name);
8118 }
8119 return 0;
8120}
8121
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008122static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008123falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008124{
8125 return 1;
8126}
8127
8128static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008129truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008130{
8131 return 0;
8132}
8133
8134static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008135execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008136{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008137 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008138 iflag = 0; /* exit on error */
8139 mflag = 0;
8140 optschanged();
8141 shellexec(argv + 1, pathval(), 0);
8142 }
8143 return 0;
8144}
8145
8146/*
8147 * The return command.
8148 */
8149static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008150returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008151{
8152 /*
8153 * If called outside a function, do what ksh does;
8154 * skip the rest of the file.
8155 */
8156 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8157 return argv[1] ? number(argv[1]) : exitstatus;
8158}
8159
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008160/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008161static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008162static int dotcmd(int, char **);
8163static int evalcmd(int, char **);
8164#if ENABLE_ASH_BUILTIN_ECHO
8165static int echocmd(int, char **);
8166#endif
8167#if ENABLE_ASH_BUILTIN_TEST
8168static int testcmd(int, char **);
8169#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008170static int exitcmd(int, char **);
8171static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008172#if ENABLE_ASH_GETOPTS
8173static int getoptscmd(int, char **);
8174#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008175#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8176static int helpcmd(int argc, char **argv);
8177#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008178#if ENABLE_ASH_MATH_SUPPORT
8179static int letcmd(int, char **);
8180#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008181static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008182static int setcmd(int, char **);
8183static int shiftcmd(int, char **);
8184static int timescmd(int, char **);
8185static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008186static int umaskcmd(int, char **);
8187static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008188static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008189
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008190#define BUILTIN_NOSPEC "0"
8191#define BUILTIN_SPECIAL "1"
8192#define BUILTIN_REGULAR "2"
8193#define BUILTIN_SPEC_REG "3"
8194#define BUILTIN_ASSIGN "4"
8195#define BUILTIN_SPEC_ASSG "5"
8196#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008197#define BUILTIN_SPEC_REG_ASSG "7"
8198
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008199/* make sure to keep these in proper order since it is searched via bsearch() */
8200static const struct builtincmd builtintab[] = {
8201 { BUILTIN_SPEC_REG ".", dotcmd },
8202 { BUILTIN_SPEC_REG ":", truecmd },
8203#if ENABLE_ASH_BUILTIN_TEST
8204 { BUILTIN_REGULAR "[", testcmd },
8205 { BUILTIN_REGULAR "[[", testcmd },
8206#endif
8207#if ENABLE_ASH_ALIAS
8208 { BUILTIN_REG_ASSG "alias", aliascmd },
8209#endif
8210#if JOBS
8211 { BUILTIN_REGULAR "bg", fg_bgcmd },
8212#endif
8213 { BUILTIN_SPEC_REG "break", breakcmd },
8214 { BUILTIN_REGULAR "cd", cdcmd },
8215 { BUILTIN_NOSPEC "chdir", cdcmd },
8216#if ENABLE_ASH_CMDCMD
8217 { BUILTIN_REGULAR "command", commandcmd },
8218#endif
8219 { BUILTIN_SPEC_REG "continue", breakcmd },
8220#if ENABLE_ASH_BUILTIN_ECHO
8221 { BUILTIN_REGULAR "echo", echocmd },
8222#endif
8223 { BUILTIN_SPEC_REG "eval", evalcmd },
8224 { BUILTIN_SPEC_REG "exec", execcmd },
8225 { BUILTIN_SPEC_REG "exit", exitcmd },
8226 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8227 { BUILTIN_REGULAR "false", falsecmd },
8228#if JOBS
8229 { BUILTIN_REGULAR "fg", fg_bgcmd },
8230#endif
8231#if ENABLE_ASH_GETOPTS
8232 { BUILTIN_REGULAR "getopts", getoptscmd },
8233#endif
8234 { BUILTIN_NOSPEC "hash", hashcmd },
8235#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8236 { BUILTIN_NOSPEC "help", helpcmd },
8237#endif
8238#if JOBS
8239 { BUILTIN_REGULAR "jobs", jobscmd },
8240 { BUILTIN_REGULAR "kill", killcmd },
8241#endif
8242#if ENABLE_ASH_MATH_SUPPORT
8243 { BUILTIN_NOSPEC "let", letcmd },
8244#endif
8245 { BUILTIN_ASSIGN "local", localcmd },
8246 { BUILTIN_NOSPEC "pwd", pwdcmd },
8247 { BUILTIN_REGULAR "read", readcmd },
8248 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8249 { BUILTIN_SPEC_REG "return", returncmd },
8250 { BUILTIN_SPEC_REG "set", setcmd },
8251 { BUILTIN_SPEC_REG "shift", shiftcmd },
8252 { BUILTIN_SPEC_REG "source", dotcmd },
8253#if ENABLE_ASH_BUILTIN_TEST
8254 { BUILTIN_REGULAR "test", testcmd },
8255#endif
8256 { BUILTIN_SPEC_REG "times", timescmd },
8257 { BUILTIN_SPEC_REG "trap", trapcmd },
8258 { BUILTIN_REGULAR "true", truecmd },
8259 { BUILTIN_NOSPEC "type", typecmd },
8260 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8261 { BUILTIN_REGULAR "umask", umaskcmd },
8262#if ENABLE_ASH_ALIAS
8263 { BUILTIN_REGULAR "unalias", unaliascmd },
8264#endif
8265 { BUILTIN_SPEC_REG "unset", unsetcmd },
8266 { BUILTIN_REGULAR "wait", waitcmd },
8267};
8268
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008269
8270#define COMMANDCMD (builtintab + 5 + \
8271 2 * ENABLE_ASH_BUILTIN_TEST + \
8272 ENABLE_ASH_ALIAS + \
8273 ENABLE_ASH_JOB_CONTROL)
8274#define EXECCMD (builtintab + 7 + \
8275 2 * ENABLE_ASH_BUILTIN_TEST + \
8276 ENABLE_ASH_ALIAS + \
8277 ENABLE_ASH_JOB_CONTROL + \
8278 ENABLE_ASH_CMDCMD + \
8279 ENABLE_ASH_BUILTIN_ECHO)
8280
8281/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008282 * Search the table of builtin commands.
8283 */
8284static struct builtincmd *
8285find_builtin(const char *name)
8286{
8287 struct builtincmd *bp;
8288
8289 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008290 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008291 pstrcmp
8292 );
8293 return bp;
8294}
8295
8296/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008297 * Execute a simple command.
8298 */
8299static int back_exitstatus; /* exit status of backquoted command */
8300static int
8301isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008302{
8303 const char *q = endofname(p);
8304 if (p == q)
8305 return 0;
8306 return *q == '=';
8307}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008308static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008309bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008310{
8311 /* Preserve exitstatus of a previous possible redirection
8312 * as POSIX mandates */
8313 return back_exitstatus;
8314}
Eric Andersenc470f442003-07-28 09:56:35 +00008315static void
8316evalcommand(union node *cmd, int flags)
8317{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008318 static const struct builtincmd null_bltin = {
8319 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008320 };
Eric Andersenc470f442003-07-28 09:56:35 +00008321 struct stackmark smark;
8322 union node *argp;
8323 struct arglist arglist;
8324 struct arglist varlist;
8325 char **argv;
8326 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008327 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008328 struct cmdentry cmdentry;
8329 struct job *jp;
8330 char *lastarg;
8331 const char *path;
8332 int spclbltin;
8333 int cmd_is_exec;
8334 int status;
8335 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008336 struct builtincmd *bcmd;
8337 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008338
8339 /* First expand the arguments. */
8340 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8341 setstackmark(&smark);
8342 back_exitstatus = 0;
8343
8344 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008345 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008346 varlist.lastp = &varlist.list;
8347 *varlist.lastp = NULL;
8348 arglist.lastp = &arglist.list;
8349 *arglist.lastp = NULL;
8350
8351 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008352 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008353 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8354 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8355 }
8356
Eric Andersenc470f442003-07-28 09:56:35 +00008357 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8358 struct strlist **spp;
8359
8360 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008361 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008362 expandarg(argp, &arglist, EXP_VARTILDE);
8363 else
8364 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8365
Eric Andersenc470f442003-07-28 09:56:35 +00008366 for (sp = *spp; sp; sp = sp->next)
8367 argc++;
8368 }
8369
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008370 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008371 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008372 TRACE(("evalcommand arg: %s\n", sp->text));
8373 *nargv++ = sp->text;
8374 }
8375 *nargv = NULL;
8376
8377 lastarg = NULL;
8378 if (iflag && funcnest == 0 && argc > 0)
8379 lastarg = nargv[-1];
8380
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008381 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008382 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008383 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008384
8385 path = vpath.text;
8386 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8387 struct strlist **spp;
8388 char *p;
8389
8390 spp = varlist.lastp;
8391 expandarg(argp, &varlist, EXP_VARTILDE);
8392
8393 /*
8394 * Modify the command lookup path, if a PATH= assignment
8395 * is present
8396 */
8397 p = (*spp)->text;
8398 if (varequal(p, path))
8399 path = p;
8400 }
8401
8402 /* Print the command if xflag is set. */
8403 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008404 int n;
8405 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008406
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008407 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008408 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008409
8410 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008411 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008412 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008413 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008414 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008415 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008416 p--;
8417 }
8418 }
8419 sp = arglist.list;
8420 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008421 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008422 }
8423
8424 cmd_is_exec = 0;
8425 spclbltin = -1;
8426
8427 /* Now locate the command. */
8428 if (argc) {
8429 const char *oldpath;
8430 int cmd_flag = DO_ERR;
8431
8432 path += 5;
8433 oldpath = path;
8434 for (;;) {
8435 find_command(argv[0], &cmdentry, cmd_flag, path);
8436 if (cmdentry.cmdtype == CMDUNKNOWN) {
8437 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008438 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008439 goto bail;
8440 }
8441
8442 /* implement bltin and command here */
8443 if (cmdentry.cmdtype != CMDBUILTIN)
8444 break;
8445 if (spclbltin < 0)
8446 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8447 if (cmdentry.u.cmd == EXECCMD)
8448 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008449#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008450 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008451 path = oldpath;
8452 nargv = parse_command_args(argv, &path);
8453 if (!nargv)
8454 break;
8455 argc -= nargv - argv;
8456 argv = nargv;
8457 cmd_flag |= DO_NOFUNC;
8458 } else
8459#endif
8460 break;
8461 }
8462 }
8463
8464 if (status) {
8465 /* We have a redirection error. */
8466 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008467 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008468 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008469 exitstatus = status;
8470 goto out;
8471 }
8472
8473 /* Execute the command. */
8474 switch (cmdentry.cmdtype) {
8475 default:
8476 /* Fork off a child process if necessary. */
8477 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008478 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008479 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008480 if (forkshell(jp, cmd, FORK_FG) != 0) {
8481 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008482 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008483 break;
8484 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008485 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008486 }
8487 listsetvar(varlist.list, VEXPORT|VSTACK);
8488 shellexec(argv, path, cmdentry.u.index);
8489 /* NOTREACHED */
8490
8491 case CMDBUILTIN:
8492 cmdenviron = varlist.list;
8493 if (cmdenviron) {
8494 struct strlist *list = cmdenviron;
8495 int i = VNOSET;
8496 if (spclbltin > 0 || argc == 0) {
8497 i = 0;
8498 if (cmd_is_exec && argc > 1)
8499 i = VEXPORT;
8500 }
8501 listsetvar(list, i);
8502 }
8503 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8504 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008505 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008506 if (i == EXEXIT)
8507 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008508 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008509 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008510 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008511 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008512 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008513 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008514 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008515 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008516 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008517 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008518 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008519 }
8520 break;
8521
8522 case CMDFUNCTION:
8523 listsetvar(varlist.list, 0);
8524 if (evalfun(cmdentry.u.func, argc, argv, flags))
8525 goto raise;
8526 break;
8527 }
8528
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008529 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008530 popredir(cmd_is_exec);
8531 if (lastarg)
8532 /* dsl: I think this is intended to be used to support
8533 * '_' in 'vi' command mode during line editing...
8534 * However I implemented that within libedit itself.
8535 */
8536 setvar("_", lastarg, 0);
8537 popstackmark(&smark);
8538}
8539
8540static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008541evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8542{
Eric Andersenc470f442003-07-28 09:56:35 +00008543 char *volatile savecmdname;
8544 struct jmploc *volatile savehandler;
8545 struct jmploc jmploc;
8546 int i;
8547
8548 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008549 i = setjmp(jmploc.loc);
8550 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008551 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008552 savehandler = exception_handler;
8553 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008554 commandname = argv[0];
8555 argptr = argv + 1;
8556 optptr = NULL; /* initialize nextopt */
8557 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008558 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008559 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008560 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008561 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008562 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008563// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008564 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008565
8566 return i;
8567}
8568
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008569static int
8570goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008571{
8572 return !*endofname(p);
8573}
8574
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008575
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008576/*
8577 * Search for a command. This is called before we fork so that the
8578 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008579 * the child. The check for "goodname" is an overly conservative
8580 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008581 */
Eric Andersenc470f442003-07-28 09:56:35 +00008582static void
8583prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008584{
8585 struct cmdentry entry;
8586
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008587 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8588 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008589}
8590
Eric Andersencb57d552001-06-28 07:25:16 +00008591
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008592/* ============ Builtin commands
8593 *
8594 * Builtin commands whose functions are closely tied to evaluation
8595 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008596 */
8597
8598/*
Eric Andersencb57d552001-06-28 07:25:16 +00008599 * Handle break and continue commands. Break, continue, and return are
8600 * all handled by setting the evalskip flag. The evaluation routines
8601 * above all check this flag, and if it is set they start skipping
8602 * commands rather than executing them. The variable skipcount is
8603 * the number of loops to break/continue, or the number of function
8604 * levels to return. (The latter is always 1.) It should probably
8605 * be an error to break out of more loops than exist, but it isn't
8606 * in the standard shell so we don't make it one here.
8607 */
Eric Andersenc470f442003-07-28 09:56:35 +00008608static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008609breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008610{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008611 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008612
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008613 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008614 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008615 if (n > loopnest)
8616 n = loopnest;
8617 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008618 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008619 skipcount = n;
8620 }
8621 return 0;
8622}
8623
Eric Andersenc470f442003-07-28 09:56:35 +00008624
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008625/* ============ input.c
8626 *
Eric Andersen90898442003-08-06 11:20:52 +00008627 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008628 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008629
Eric Andersenc470f442003-07-28 09:56:35 +00008630#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008631
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008632enum {
8633 INPUT_PUSH_FILE = 1,
8634 INPUT_NOFILE_OK = 2,
8635};
Eric Andersencb57d552001-06-28 07:25:16 +00008636
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008637static int plinno = 1; /* input line number */
8638/* number of characters left in input buffer */
8639static int parsenleft; /* copy of parsefile->nleft */
8640static int parselleft; /* copy of parsefile->lleft */
8641/* next character in input buffer */
8642static char *parsenextc; /* copy of parsefile->nextc */
8643
8644static int checkkwd;
8645/* values of checkkwd variable */
8646#define CHKALIAS 0x1
8647#define CHKKWD 0x2
8648#define CHKNL 0x4
8649
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008650static void
8651popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008652{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008653 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008654
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008655 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008656#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008657 if (sp->ap) {
8658 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8659 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008660 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008661 if (sp->string != sp->ap->val) {
8662 free(sp->string);
8663 }
8664 sp->ap->flag &= ~ALIASINUSE;
8665 if (sp->ap->flag & ALIASDEAD) {
8666 unalias(sp->ap->name);
8667 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008668 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008669#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008670 parsenextc = sp->prevstring;
8671 parsenleft = sp->prevnleft;
8672/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8673 parsefile->strpush = sp->prev;
8674 if (sp != &(parsefile->basestrpush))
8675 free(sp);
8676 INT_ON;
8677}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008678
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008679static int
8680preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008681{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008682 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008683 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008684 parsenextc = buf;
8685
Denis Vlasenko38f63192007-01-22 09:03:07 +00008686#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008687 retry:
Eric Andersenc470f442003-07-28 09:56:35 +00008688 if (!iflag || parsefile->fd)
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008689 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008690 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008691#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008692 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008693#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008694 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8695 if (nr == 0) {
8696 /* Ctrl+C pressed */
8697 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008698 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008699 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008700 raise(SIGINT);
8701 return 1;
8702 }
Eric Andersenc470f442003-07-28 09:56:35 +00008703 goto retry;
8704 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008705 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008706 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008707 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008708 }
Eric Andersencb57d552001-06-28 07:25:16 +00008709 }
8710#else
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008711 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008712#endif
8713
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008714#if 0
8715/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008716 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008717 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008718 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008719 if (flags >= 0 && (flags & O_NONBLOCK)) {
8720 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008721 if (fcntl(0, F_SETFL, flags) >= 0) {
8722 out2str("sh: turning off NDELAY mode\n");
8723 goto retry;
8724 }
8725 }
8726 }
8727 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008728#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008729 return nr;
8730}
8731
8732/*
8733 * Refill the input buffer and return the next input character:
8734 *
8735 * 1) If a string was pushed back on the input, pop it;
8736 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8737 * from a string so we can't refill the buffer, return EOF.
8738 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8739 * 4) Process input up to the next newline, deleting nul characters.
8740 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008741static int
Eric Andersenc470f442003-07-28 09:56:35 +00008742preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008743{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008744 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008745 int more;
8746 char savec;
8747
8748 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008749#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008750 if (parsenleft == -1 && parsefile->strpush->ap &&
8751 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008752 return PEOA;
8753 }
Eric Andersen2870d962001-07-02 17:27:21 +00008754#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008755 popstring();
8756 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008757 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008758 }
8759 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8760 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008761 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008762
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008763 more = parselleft;
8764 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008765 again:
8766 more = preadfd();
8767 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008768 parselleft = parsenleft = EOF_NLEFT;
8769 return PEOF;
8770 }
8771 }
8772
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008773 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008774
8775 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008776 for (;;) {
8777 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008778
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008779 more--;
8780 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008781
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008782 if (!c)
8783 memmove(q, q + 1, more);
8784 else {
8785 q++;
8786 if (c == '\n') {
8787 parsenleft = q - parsenextc - 1;
8788 break;
8789 }
Eric Andersencb57d552001-06-28 07:25:16 +00008790 }
8791
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008792 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008793 parsenleft = q - parsenextc - 1;
8794 if (parsenleft < 0)
8795 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008796 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008797 }
8798 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008799 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008800
8801 savec = *q;
8802 *q = '\0';
8803
8804 if (vflag) {
8805 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008806 }
8807
8808 *q = savec;
8809
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008810 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008811}
8812
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008813#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008814static int
8815pgetc(void)
8816{
8817 return pgetc_as_macro();
8818}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008819
8820#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8821#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008822#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008823#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008824#endif
8825
8826/*
8827 * Same as pgetc(), but ignores PEOA.
8828 */
8829#if ENABLE_ASH_ALIAS
8830static int
8831pgetc2(void)
8832{
8833 int c;
8834
8835 do {
8836 c = pgetc_macro();
8837 } while (c == PEOA);
8838 return c;
8839}
8840#else
8841static int
8842pgetc2(void)
8843{
8844 return pgetc_macro();
8845}
8846#endif
8847
8848/*
8849 * Read a line from the script.
8850 */
8851static char *
8852pfgets(char *line, int len)
8853{
8854 char *p = line;
8855 int nleft = len;
8856 int c;
8857
8858 while (--nleft > 0) {
8859 c = pgetc2();
8860 if (c == PEOF) {
8861 if (p == line)
8862 return NULL;
8863 break;
8864 }
8865 *p++ = c;
8866 if (c == '\n')
8867 break;
8868 }
8869 *p = '\0';
8870 return line;
8871}
8872
Eric Andersenc470f442003-07-28 09:56:35 +00008873/*
8874 * Undo the last call to pgetc. Only one character may be pushed back.
8875 * PEOF may be pushed back.
8876 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008877static void
Eric Andersenc470f442003-07-28 09:56:35 +00008878pungetc(void)
8879{
8880 parsenleft++;
8881 parsenextc--;
8882}
Eric Andersencb57d552001-06-28 07:25:16 +00008883
8884/*
8885 * Push a string back onto the input at this current parsefile level.
8886 * We handle aliases this way.
8887 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00008888#if !ENABLE_ASH_ALIAS
8889#define pushstring(s, ap) pushstring(s)
8890#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008891static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00008892pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008893{
Eric Andersencb57d552001-06-28 07:25:16 +00008894 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008895 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008896
Eric Andersenc470f442003-07-28 09:56:35 +00008897 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008898 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008899/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8900 if (parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00008901 sp = ckzalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008902 sp->prev = parsefile->strpush;
8903 parsefile->strpush = sp;
8904 } else
8905 sp = parsefile->strpush = &(parsefile->basestrpush);
8906 sp->prevstring = parsenextc;
8907 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008908#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00008909 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008910 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00008911 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008912 sp->string = s;
8913 }
Eric Andersen2870d962001-07-02 17:27:21 +00008914#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008915 parsenextc = s;
8916 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008917 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008918}
8919
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008920/*
8921 * To handle the "." command, a stack of input files is used. Pushfile
8922 * adds a new entry to the stack and popfile restores the previous level.
8923 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008924static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008925pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008926{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008927 struct parsefile *pf;
8928
8929 parsefile->nleft = parsenleft;
8930 parsefile->lleft = parselleft;
8931 parsefile->nextc = parsenextc;
8932 parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00008933 pf = ckzalloc(sizeof(*pf));
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008934 pf->prev = parsefile;
8935 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00008936 /*pf->strpush = NULL; - ckzalloc did it */
8937 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008938 parsefile = pf;
8939}
8940
8941static void
8942popfile(void)
8943{
8944 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008945
Denis Vlasenkob012b102007-02-19 22:43:01 +00008946 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008947 if (pf->fd >= 0)
8948 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008949 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008950 while (pf->strpush)
8951 popstring();
8952 parsefile = pf->prev;
8953 free(pf);
8954 parsenleft = parsefile->nleft;
8955 parselleft = parsefile->lleft;
8956 parsenextc = parsefile->nextc;
8957 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008958 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008959}
8960
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008961/*
8962 * Return to top level.
8963 */
8964static void
8965popallfiles(void)
8966{
8967 while (parsefile != &basepf)
8968 popfile();
8969}
8970
8971/*
8972 * Close the file(s) that the shell is reading commands from. Called
8973 * after a fork is done.
8974 */
8975static void
8976closescript(void)
8977{
8978 popallfiles();
8979 if (parsefile->fd > 0) {
8980 close(parsefile->fd);
8981 parsefile->fd = 0;
8982 }
8983}
8984
8985/*
8986 * Like setinputfile, but takes an open file descriptor. Call this with
8987 * interrupts off.
8988 */
8989static void
8990setinputfd(int fd, int push)
8991{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008992 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008993 if (push) {
8994 pushfile();
8995 parsefile->buf = 0;
8996 }
8997 parsefile->fd = fd;
8998 if (parsefile->buf == NULL)
8999 parsefile->buf = ckmalloc(IBUFSIZ);
9000 parselleft = parsenleft = 0;
9001 plinno = 1;
9002}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009003
Eric Andersenc470f442003-07-28 09:56:35 +00009004/*
9005 * Set the input to take input from a file. If push is set, push the
9006 * old input onto the stack first.
9007 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009008static int
9009setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009010{
9011 int fd;
9012 int fd2;
9013
Denis Vlasenkob012b102007-02-19 22:43:01 +00009014 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009015 fd = open(fname, O_RDONLY);
9016 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009017 if (flags & INPUT_NOFILE_OK)
9018 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009019 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009020 }
Eric Andersenc470f442003-07-28 09:56:35 +00009021 if (fd < 10) {
9022 fd2 = copyfd(fd, 10);
9023 close(fd);
9024 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009025 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009026 fd = fd2;
9027 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009028 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009029 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009030 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009031 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009032}
9033
Eric Andersencb57d552001-06-28 07:25:16 +00009034/*
9035 * Like setinputfile, but takes input from a string.
9036 */
Eric Andersenc470f442003-07-28 09:56:35 +00009037static void
9038setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009039{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009040 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009041 pushfile();
9042 parsenextc = string;
9043 parsenleft = strlen(string);
9044 parsefile->buf = NULL;
9045 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009046 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009047}
9048
9049
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009050/* ============ mail.c
9051 *
9052 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009053 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009054
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009055#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009056
Eric Andersencb57d552001-06-28 07:25:16 +00009057#define MAXMBOXES 10
9058
Eric Andersenc470f442003-07-28 09:56:35 +00009059/* times of mailboxes */
9060static time_t mailtime[MAXMBOXES];
9061/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009062static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009063
Eric Andersencb57d552001-06-28 07:25:16 +00009064/*
Eric Andersenc470f442003-07-28 09:56:35 +00009065 * Print appropriate message(s) if mail has arrived.
9066 * If mail_var_path_changed is set,
9067 * then the value of MAIL has mail_var_path_changed,
9068 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009069 */
Eric Andersenc470f442003-07-28 09:56:35 +00009070static void
9071chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009072{
Eric Andersencb57d552001-06-28 07:25:16 +00009073 const char *mpath;
9074 char *p;
9075 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009076 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009077 struct stackmark smark;
9078 struct stat statb;
9079
Eric Andersencb57d552001-06-28 07:25:16 +00009080 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009081 mpath = mpathset() ? mpathval() : mailval();
9082 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009083 p = padvance(&mpath, nullstr);
9084 if (p == NULL)
9085 break;
9086 if (*p == '\0')
9087 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009088 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009089#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009090 if (q[-1] != '/')
9091 abort();
9092#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009093 q[-1] = '\0'; /* delete trailing '/' */
9094 if (stat(p, &statb) < 0) {
9095 *mtp = 0;
9096 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009097 }
Eric Andersenc470f442003-07-28 09:56:35 +00009098 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9099 fprintf(
9100 stderr, snlfmt,
9101 pathopt ? pathopt : "you have mail"
9102 );
9103 }
9104 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009105 }
Eric Andersenc470f442003-07-28 09:56:35 +00009106 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009107 popstackmark(&smark);
9108}
Eric Andersencb57d552001-06-28 07:25:16 +00009109
Eric Andersenc470f442003-07-28 09:56:35 +00009110static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009111changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009112{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009113 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009114}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009115
Denis Vlasenko131ae172007-02-18 13:00:19 +00009116#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009117
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009118
9119/* ============ ??? */
9120
Eric Andersencb57d552001-06-28 07:25:16 +00009121/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009122 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009123 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009124static void
9125setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009126{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009127 char **newparam;
9128 char **ap;
9129 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009130
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009131 for (nparam = 0; argv[nparam]; nparam++);
9132 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9133 while (*argv) {
9134 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009135 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009136 *ap = NULL;
9137 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009138 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009139 shellparam.nparam = nparam;
9140 shellparam.p = newparam;
9141#if ENABLE_ASH_GETOPTS
9142 shellparam.optind = 1;
9143 shellparam.optoff = -1;
9144#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009145}
9146
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009147/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009148 * Process shell options. The global variable argptr contains a pointer
9149 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009150 *
9151 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9152 * For a non-interactive shell, an error condition encountered
9153 * by a special built-in ... shall cause the shell to write a diagnostic message
9154 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009155 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009156 * ...
9157 * Utility syntax error (option or operand error) Shall exit
9158 * ...
9159 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9160 * we see that bash does not do that (set "finishes" with error code 1 instead,
9161 * and shell continues), and people rely on this behavior!
9162 * Testcase:
9163 * set -o barfoo 2>/dev/null
9164 * echo $?
9165 *
9166 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009167 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009168static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009169minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009170{
9171 int i;
9172
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009173 if (name) {
9174 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009175 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009176 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009177 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009178 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009179 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009180 ash_msg("illegal option -o %s", name);
9181 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009182 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009183 out1str("Current option settings\n");
9184 for (i = 0; i < NOPTS; i++)
9185 out1fmt("%-16s%s\n", optnames(i),
9186 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009187 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009188}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009189static void
9190setoption(int flag, int val)
9191{
9192 int i;
9193
9194 for (i = 0; i < NOPTS; i++) {
9195 if (optletters(i) == flag) {
9196 optlist[i] = val;
9197 return;
9198 }
9199 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009200 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009201 /* NOTREACHED */
9202}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009203static int
Eric Andersenc470f442003-07-28 09:56:35 +00009204options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009205{
9206 char *p;
9207 int val;
9208 int c;
9209
9210 if (cmdline)
9211 minusc = NULL;
9212 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009213 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009214 if (c != '-' && c != '+')
9215 break;
9216 argptr++;
9217 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009218 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009219 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009220 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009221 if (!cmdline) {
9222 /* "-" means turn off -x and -v */
9223 if (p[0] == '\0')
9224 xflag = vflag = 0;
9225 /* "--" means reset params */
9226 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009227 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009228 }
Eric Andersenc470f442003-07-28 09:56:35 +00009229 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009230 }
Eric Andersencb57d552001-06-28 07:25:16 +00009231 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009232 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009233 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009234 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009235 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009236 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009237 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009238 if (minus_o(*argptr, val)) {
9239 /* it already printed err message */
9240 return 1; /* error */
9241 }
Eric Andersencb57d552001-06-28 07:25:16 +00009242 if (*argptr)
9243 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009244 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9245 isloginsh = 1;
9246 /* bash does not accept +-login, we also won't */
9247 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009248 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009249 isloginsh = 1;
9250 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009251 } else {
9252 setoption(c, val);
9253 }
9254 }
9255 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009256 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009257}
9258
Eric Andersencb57d552001-06-28 07:25:16 +00009259/*
Eric Andersencb57d552001-06-28 07:25:16 +00009260 * The shift builtin command.
9261 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009262static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009263shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009264{
9265 int n;
9266 char **ap1, **ap2;
9267
9268 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009269 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009270 n = number(argv[1]);
9271 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009272 ash_msg_and_raise_error("can't shift that many");
9273 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009274 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009275 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009276 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009277 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009278 }
9279 ap2 = shellparam.p;
9280 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009281#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009282 shellparam.optind = 1;
9283 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009284#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009285 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009286 return 0;
9287}
9288
Eric Andersencb57d552001-06-28 07:25:16 +00009289/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009290 * POSIX requires that 'set' (but not export or readonly) output the
9291 * variables in lexicographic order - by the locale's collating order (sigh).
9292 * Maybe we could keep them in an ordered balanced binary tree
9293 * instead of hashed lists.
9294 * For now just roll 'em through qsort for printing...
9295 */
9296static int
9297showvars(const char *sep_prefix, int on, int off)
9298{
9299 const char *sep;
9300 char **ep, **epend;
9301
9302 ep = listvars(on, off, &epend);
9303 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9304
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009305 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009306
9307 for (; ep < epend; ep++) {
9308 const char *p;
9309 const char *q;
9310
9311 p = strchrnul(*ep, '=');
9312 q = nullstr;
9313 if (*p)
9314 q = single_quote(++p);
9315 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9316 }
9317 return 0;
9318}
9319
9320/*
Eric Andersencb57d552001-06-28 07:25:16 +00009321 * The set command builtin.
9322 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009323static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009324setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009325{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009326 int retval;
9327
Denis Vlasenko68404f12008-03-17 09:00:54 +00009328 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009329 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009330 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009331 retval = 1;
9332 if (!options(0)) { /* if no parse error... */
9333 retval = 0;
9334 optschanged();
9335 if (*argptr != NULL) {
9336 setparam(argptr);
9337 }
Eric Andersencb57d552001-06-28 07:25:16 +00009338 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009339 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009340 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009341}
9342
Denis Vlasenko131ae172007-02-18 13:00:19 +00009343#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009344/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009345static void
9346change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009347{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009348 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009349 /* "get", generate */
9350 char buf[16];
9351
9352 rseed = rseed * 1103515245 + 12345;
9353 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9354 /* set without recursion */
9355 setvar(vrandom.text, buf, VNOFUNC);
9356 vrandom.flags &= ~VNOFUNC;
9357 } else {
9358 /* set/reset */
9359 rseed = strtoul(value, (char **)NULL, 10);
9360 }
Eric Andersenef02f822004-03-11 13:34:24 +00009361}
Eric Andersen16767e22004-03-16 05:14:10 +00009362#endif
9363
Denis Vlasenko131ae172007-02-18 13:00:19 +00009364#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009365static int
Eric Andersenc470f442003-07-28 09:56:35 +00009366getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009367{
9368 char *p, *q;
9369 char c = '?';
9370 int done = 0;
9371 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009372 char s[12];
9373 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009374
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009375 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009376 return 1;
9377 optnext = optfirst + *param_optind - 1;
9378
9379 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009380 p = NULL;
9381 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009382 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009383 if (p == NULL || *p == '\0') {
9384 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009385 p = *optnext;
9386 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009387 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009388 p = NULL;
9389 done = 1;
9390 goto out;
9391 }
9392 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009393 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009394 goto atend;
9395 }
9396
9397 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009398 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009399 if (*q == '\0') {
9400 if (optstr[0] == ':') {
9401 s[0] = c;
9402 s[1] = '\0';
9403 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009404 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009405 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009406 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009407 }
9408 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009409 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009410 }
9411 if (*++q == ':')
9412 q++;
9413 }
9414
9415 if (*++q == ':') {
9416 if (*p == '\0' && (p = *optnext) == NULL) {
9417 if (optstr[0] == ':') {
9418 s[0] = c;
9419 s[1] = '\0';
9420 err |= setvarsafe("OPTARG", s, 0);
9421 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009422 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009423 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009424 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009425 c = '?';
9426 }
Eric Andersenc470f442003-07-28 09:56:35 +00009427 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009428 }
9429
9430 if (p == *optnext)
9431 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009432 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009433 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009434 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009435 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009436 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009437 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009438 *param_optind = optnext - optfirst + 1;
9439 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009440 err |= setvarsafe("OPTIND", s, VNOFUNC);
9441 s[0] = c;
9442 s[1] = '\0';
9443 err |= setvarsafe(optvar, s, 0);
9444 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009445 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009446 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009447 flush_stdout_stderr();
9448 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009449 }
9450 return done;
9451}
Eric Andersenc470f442003-07-28 09:56:35 +00009452
9453/*
9454 * The getopts builtin. Shellparam.optnext points to the next argument
9455 * to be processed. Shellparam.optptr points to the next character to
9456 * be processed in the current argument. If shellparam.optnext is NULL,
9457 * then it's the first time getopts has been called.
9458 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009459static int
Eric Andersenc470f442003-07-28 09:56:35 +00009460getoptscmd(int argc, char **argv)
9461{
9462 char **optbase;
9463
9464 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009465 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009466 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009467 optbase = shellparam.p;
9468 if (shellparam.optind > shellparam.nparam + 1) {
9469 shellparam.optind = 1;
9470 shellparam.optoff = -1;
9471 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009472 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009473 optbase = &argv[3];
9474 if (shellparam.optind > argc - 2) {
9475 shellparam.optind = 1;
9476 shellparam.optoff = -1;
9477 }
9478 }
9479
9480 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009481 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009482}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009483#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009484
Eric Andersencb57d552001-06-28 07:25:16 +00009485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009486/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009487
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009488/*
9489 * NEOF is returned by parsecmd when it encounters an end of file. It
9490 * must be distinct from NULL, so we use the address of a variable that
9491 * happens to be handy.
9492 */
9493static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009494#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009495static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009496static int lasttoken; /* last token read */
9497static char *wordtext; /* text of last word returned by readtoken */
9498static struct nodelist *backquotelist;
9499static union node *redirnode;
9500static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009501static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009502
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009503static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9504static void
9505raise_error_syntax(const char *msg)
9506{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009507 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009508 /* NOTREACHED */
9509}
9510
9511/*
9512 * Called when an unexpected token is read during the parse. The argument
9513 * is the token that is expected, or -1 if more than one type of token can
9514 * occur at this point.
9515 */
9516static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9517static void
9518raise_error_unexpected_syntax(int token)
9519{
9520 char msg[64];
9521 int l;
9522
9523 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9524 if (token >= 0)
9525 sprintf(msg + l, " (expecting %s)", tokname(token));
9526 raise_error_syntax(msg);
9527 /* NOTREACHED */
9528}
Eric Andersencb57d552001-06-28 07:25:16 +00009529
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009530#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009531
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009532struct heredoc {
9533 struct heredoc *next; /* next here document in list */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009534 union node *here; /* redirection node */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009535 char *eofmark; /* string indicating end of input */
9536 int striptabs; /* if set, strip leading tabs */
9537};
Eric Andersencb57d552001-06-28 07:25:16 +00009538
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009539static struct heredoc *heredoclist; /* list of here documents to read */
9540
9541/* parsing is heavily cross-recursive, need these forward decls */
9542static union node *andor(void);
9543static union node *pipeline(void);
9544static union node *parse_command(void);
9545static void parseheredoc(void);
9546static char peektoken(void);
9547static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009548
Eric Andersenc470f442003-07-28 09:56:35 +00009549static union node *
9550list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009551{
9552 union node *n1, *n2, *n3;
9553 int tok;
9554
Eric Andersenc470f442003-07-28 09:56:35 +00009555 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9556 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009557 return NULL;
9558 n1 = NULL;
9559 for (;;) {
9560 n2 = andor();
9561 tok = readtoken();
9562 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009563 if (n2->type == NPIPE) {
9564 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009565 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009566 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009567 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009568 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009569 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009570 n2 = n3;
9571 }
9572 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009573 }
9574 }
9575 if (n1 == NULL) {
9576 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009577 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009578 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009579 n3->type = NSEMI;
9580 n3->nbinary.ch1 = n1;
9581 n3->nbinary.ch2 = n2;
9582 n1 = n3;
9583 }
9584 switch (tok) {
9585 case TBACKGND:
9586 case TSEMI:
9587 tok = readtoken();
9588 /* fall through */
9589 case TNL:
9590 if (tok == TNL) {
9591 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009592 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009593 return n1;
9594 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009595 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009596 }
Eric Andersenc470f442003-07-28 09:56:35 +00009597 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009598 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009599 return n1;
9600 break;
9601 case TEOF:
9602 if (heredoclist)
9603 parseheredoc();
9604 else
Eric Andersenc470f442003-07-28 09:56:35 +00009605 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009606 return n1;
9607 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009608 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009609 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009610 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009611 return n1;
9612 }
9613 }
9614}
9615
Eric Andersenc470f442003-07-28 09:56:35 +00009616static union node *
9617andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009618{
Eric Andersencb57d552001-06-28 07:25:16 +00009619 union node *n1, *n2, *n3;
9620 int t;
9621
Eric Andersencb57d552001-06-28 07:25:16 +00009622 n1 = pipeline();
9623 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009624 t = readtoken();
9625 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009626 t = NAND;
9627 } else if (t == TOR) {
9628 t = NOR;
9629 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009630 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009631 return n1;
9632 }
Eric Andersenc470f442003-07-28 09:56:35 +00009633 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009634 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009635 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009636 n3->type = t;
9637 n3->nbinary.ch1 = n1;
9638 n3->nbinary.ch2 = n2;
9639 n1 = n3;
9640 }
9641}
9642
Eric Andersenc470f442003-07-28 09:56:35 +00009643static union node *
9644pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009645{
Eric Andersencb57d552001-06-28 07:25:16 +00009646 union node *n1, *n2, *pipenode;
9647 struct nodelist *lp, *prev;
9648 int negate;
9649
9650 negate = 0;
9651 TRACE(("pipeline: entered\n"));
9652 if (readtoken() == TNOT) {
9653 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009654 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009655 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009656 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009657 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009658 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009659 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009660 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009661 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009662 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009663 pipenode->npipe.cmdlist = lp;
9664 lp->n = n1;
9665 do {
9666 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009667 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009668 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009669 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009670 prev->next = lp;
9671 } while (readtoken() == TPIPE);
9672 lp->next = NULL;
9673 n1 = pipenode;
9674 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009675 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009676 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009677 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009678 n2->type = NNOT;
9679 n2->nnot.com = n1;
9680 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009681 }
9682 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009683}
9684
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009685static union node *
9686makename(void)
9687{
9688 union node *n;
9689
Denis Vlasenko597906c2008-02-20 16:38:54 +00009690 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009691 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009692 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009693 n->narg.text = wordtext;
9694 n->narg.backquote = backquotelist;
9695 return n;
9696}
9697
9698static void
9699fixredir(union node *n, const char *text, int err)
9700{
9701 TRACE(("Fix redir %s %d\n", text, err));
9702 if (!err)
9703 n->ndup.vname = NULL;
9704
9705 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009706 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009707 else if (LONE_DASH(text))
9708 n->ndup.dupfd = -1;
9709 else {
9710 if (err)
9711 raise_error_syntax("Bad fd number");
9712 n->ndup.vname = makename();
9713 }
9714}
9715
9716/*
9717 * Returns true if the text contains nothing to expand (no dollar signs
9718 * or backquotes).
9719 */
9720static int
9721noexpand(char *text)
9722{
9723 char *p;
9724 char c;
9725
9726 p = text;
9727 while ((c = *p++) != '\0') {
9728 if (c == CTLQUOTEMARK)
9729 continue;
9730 if (c == CTLESC)
9731 p++;
9732 else if (SIT(c, BASESYNTAX) == CCTL)
9733 return 0;
9734 }
9735 return 1;
9736}
9737
9738static void
9739parsefname(void)
9740{
9741 union node *n = redirnode;
9742
9743 if (readtoken() != TWORD)
9744 raise_error_unexpected_syntax(-1);
9745 if (n->type == NHERE) {
9746 struct heredoc *here = heredoc;
9747 struct heredoc *p;
9748 int i;
9749
9750 if (quoteflag == 0)
9751 n->type = NXHERE;
9752 TRACE(("Here document %d\n", n->type));
9753 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9754 raise_error_syntax("Illegal eof marker for << redirection");
9755 rmescapes(wordtext);
9756 here->eofmark = wordtext;
9757 here->next = NULL;
9758 if (heredoclist == NULL)
9759 heredoclist = here;
9760 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009761 for (p = heredoclist; p->next; p = p->next)
9762 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009763 p->next = here;
9764 }
9765 } else if (n->type == NTOFD || n->type == NFROMFD) {
9766 fixredir(n, wordtext, 0);
9767 } else {
9768 n->nfile.fname = makename();
9769 }
9770}
Eric Andersencb57d552001-06-28 07:25:16 +00009771
Eric Andersenc470f442003-07-28 09:56:35 +00009772static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009773simplecmd(void)
9774{
9775 union node *args, **app;
9776 union node *n = NULL;
9777 union node *vars, **vpp;
9778 union node **rpp, *redir;
9779 int savecheckkwd;
9780
9781 args = NULL;
9782 app = &args;
9783 vars = NULL;
9784 vpp = &vars;
9785 redir = NULL;
9786 rpp = &redir;
9787
9788 savecheckkwd = CHKALIAS;
9789 for (;;) {
9790 checkkwd = savecheckkwd;
9791 switch (readtoken()) {
9792 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +00009793 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009794 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009795 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009796 n->narg.text = wordtext;
9797 n->narg.backquote = backquotelist;
9798 if (savecheckkwd && isassignment(wordtext)) {
9799 *vpp = n;
9800 vpp = &n->narg.next;
9801 } else {
9802 *app = n;
9803 app = &n->narg.next;
9804 savecheckkwd = 0;
9805 }
9806 break;
9807 case TREDIR:
9808 *rpp = n = redirnode;
9809 rpp = &n->nfile.next;
9810 parsefname(); /* read name of redirection file */
9811 break;
9812 case TLP:
9813 if (args && app == &args->narg.next
9814 && !vars && !redir
9815 ) {
9816 struct builtincmd *bcmd;
9817 const char *name;
9818
9819 /* We have a function */
9820 if (readtoken() != TRP)
9821 raise_error_unexpected_syntax(TRP);
9822 name = n->narg.text;
9823 if (!goodname(name)
9824 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9825 ) {
9826 raise_error_syntax("Bad function name");
9827 }
9828 n->type = NDEFUN;
9829 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9830 n->narg.next = parse_command();
9831 return n;
9832 }
9833 /* fall through */
9834 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009835 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009836 goto out;
9837 }
9838 }
9839 out:
9840 *app = NULL;
9841 *vpp = NULL;
9842 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009843 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009844 n->type = NCMD;
9845 n->ncmd.args = args;
9846 n->ncmd.assign = vars;
9847 n->ncmd.redirect = redir;
9848 return n;
9849}
9850
9851static union node *
9852parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009853{
Eric Andersencb57d552001-06-28 07:25:16 +00009854 union node *n1, *n2;
9855 union node *ap, **app;
9856 union node *cp, **cpp;
9857 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009858 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009859 int t;
9860
9861 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009862 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009863
Eric Andersencb57d552001-06-28 07:25:16 +00009864 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009865 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009866 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009867 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009868 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009869 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009870 n1->type = NIF;
9871 n1->nif.test = list(0);
9872 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009873 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009874 n1->nif.ifpart = list(0);
9875 n2 = n1;
9876 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009877 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009878 n2 = n2->nif.elsepart;
9879 n2->type = NIF;
9880 n2->nif.test = list(0);
9881 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009882 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009883 n2->nif.ifpart = list(0);
9884 }
9885 if (lasttoken == TELSE)
9886 n2->nif.elsepart = list(0);
9887 else {
9888 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009889 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009890 }
Eric Andersenc470f442003-07-28 09:56:35 +00009891 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009892 break;
9893 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009894 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009895 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009896 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009897 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009898 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009899 got = readtoken();
9900 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009901 TRACE(("expecting DO got %s %s\n", tokname(got),
9902 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009903 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009904 }
9905 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009906 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 break;
9908 }
9909 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009910 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009911 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009912 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009913 n1->type = NFOR;
9914 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009915 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009916 if (readtoken() == TIN) {
9917 app = &ap;
9918 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009919 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009920 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009921 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009922 n2->narg.text = wordtext;
9923 n2->narg.backquote = backquotelist;
9924 *app = n2;
9925 app = &n2->narg.next;
9926 }
9927 *app = NULL;
9928 n1->nfor.args = ap;
9929 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009930 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009931 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009932 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009933 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009934 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009935 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009936 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00009937 n1->nfor.args = n2;
9938 /*
9939 * Newline or semicolon here is optional (but note
9940 * that the original Bourne shell only allowed NL).
9941 */
9942 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009943 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009944 }
Eric Andersenc470f442003-07-28 09:56:35 +00009945 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009946 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009947 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009948 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009949 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009950 break;
9951 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009952 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009953 n1->type = NCASE;
9954 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009955 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +00009956 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009957 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009958 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009959 n2->narg.text = wordtext;
9960 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +00009961 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009962 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009963 } while (readtoken() == TNL);
9964 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009965 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009966 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009967 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009968 checkkwd = CHKNL | CHKKWD;
9969 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009970 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009971 if (lasttoken == TLP)
9972 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009973 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009974 cp->type = NCLIST;
9975 app = &cp->nclist.pattern;
9976 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009977 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009978 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009979 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009980 ap->narg.text = wordtext;
9981 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009982 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009983 break;
9984 app = &ap->narg.next;
9985 readtoken();
9986 }
Denis Vlasenko597906c2008-02-20 16:38:54 +00009987 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009988 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009989 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009990 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009991
Eric Andersenc470f442003-07-28 09:56:35 +00009992 cpp = &cp->nclist.next;
9993
9994 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009995 t = readtoken();
9996 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009997 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009998 raise_error_unexpected_syntax(TENDCASE);
9999 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010000 }
Eric Andersenc470f442003-07-28 09:56:35 +000010001 }
Eric Andersencb57d552001-06-28 07:25:16 +000010002 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010003 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010004 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010005 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010006 n1->type = NSUBSHELL;
10007 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010008 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010009 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010010 break;
10011 case TBEGIN:
10012 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010013 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010014 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010015 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010016 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010017 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010018 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010019 }
10020
Eric Andersenc470f442003-07-28 09:56:35 +000010021 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010022 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010023
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010024 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010025 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010026 checkkwd = CHKKWD | CHKALIAS;
10027 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010028 while (readtoken() == TREDIR) {
10029 *rpp = n2 = redirnode;
10030 rpp = &n2->nfile.next;
10031 parsefname();
10032 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010033 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010034 *rpp = NULL;
10035 if (redir) {
10036 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010037 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010038 n2->type = NREDIR;
10039 n2->nredir.n = n1;
10040 n1 = n2;
10041 }
10042 n1->nredir.redirect = redir;
10043 }
Eric Andersencb57d552001-06-28 07:25:16 +000010044 return n1;
10045}
10046
Eric Andersencb57d552001-06-28 07:25:16 +000010047/*
10048 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10049 * is not NULL, read a here document. In the latter case, eofmark is the
10050 * word which marks the end of the document and striptabs is true if
10051 * leading tabs should be stripped from the document. The argument firstc
10052 * is the first character of the input token or document.
10053 *
10054 * Because C does not have internal subroutines, I have simulated them
10055 * using goto's to implement the subroutine linkage. The following macros
10056 * will run code that appears at the end of readtoken1.
10057 */
10058
Eric Andersen2870d962001-07-02 17:27:21 +000010059#define CHECKEND() {goto checkend; checkend_return:;}
10060#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10061#define PARSESUB() {goto parsesub; parsesub_return:;}
10062#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10063#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10064#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010065
10066static int
Eric Andersenc470f442003-07-28 09:56:35 +000010067readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010068{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010069 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010070 int c = firstc;
10071 char *out;
10072 int len;
10073 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010074 struct nodelist *bqlist;
10075 smallint quotef;
10076 smallint dblquote;
10077 smallint oldstyle;
10078 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010079#if ENABLE_ASH_EXPAND_PRMT
10080 smallint pssyntax; /* we are expanding a prompt string */
10081#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010082 int varnest; /* levels of variables expansion */
10083 int arinest; /* levels of arithmetic expansion */
10084 int parenlevel; /* levels of parens in arithmetic */
10085 int dqvarnest; /* levels of variables expansion within double quotes */
10086
Eric Andersencb57d552001-06-28 07:25:16 +000010087#if __GNUC__
10088 /* Avoid longjmp clobbering */
10089 (void) &out;
10090 (void) &quotef;
10091 (void) &dblquote;
10092 (void) &varnest;
10093 (void) &arinest;
10094 (void) &parenlevel;
10095 (void) &dqvarnest;
10096 (void) &oldstyle;
10097 (void) &prevsyntax;
10098 (void) &syntax;
10099#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010100 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010101 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010102 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010103 oldstyle = 0;
10104 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010105#if ENABLE_ASH_EXPAND_PRMT
10106 pssyntax = (syntax == PSSYNTAX);
10107 if (pssyntax)
10108 syntax = DQSYNTAX;
10109#endif
10110 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010111 varnest = 0;
10112 arinest = 0;
10113 parenlevel = 0;
10114 dqvarnest = 0;
10115
10116 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010117 loop: { /* for each line, until end of word */
10118 CHECKEND(); /* set c to PEOF if at end of here document */
10119 for (;;) { /* until end of line or end of word */
10120 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010121 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010122 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010123 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010124 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010125 USTPUTC(c, out);
10126 plinno++;
10127 if (doprompt)
10128 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010129 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010130 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010131 case CWORD:
10132 USTPUTC(c, out);
10133 break;
10134 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010135 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010136 USTPUTC(CTLESC, out);
10137 USTPUTC(c, out);
10138 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010139 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010140 c = pgetc2();
10141 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010142 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010143 USTPUTC('\\', out);
10144 pungetc();
10145 } else if (c == '\n') {
10146 if (doprompt)
10147 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010148 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010149#if ENABLE_ASH_EXPAND_PRMT
10150 if (c == '$' && pssyntax) {
10151 USTPUTC(CTLESC, out);
10152 USTPUTC('\\', out);
10153 }
10154#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010155 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010156 c != '\\' && c != '`' &&
10157 c != '$' && (
10158 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010159 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010160 ) {
10161 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010162 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010163 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010164 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010165 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010166 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010167 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010168 }
10169 break;
10170 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010171 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010172 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010173 if (eofmark == NULL) {
10174 USTPUTC(CTLQUOTEMARK, out);
10175 }
Eric Andersencb57d552001-06-28 07:25:16 +000010176 break;
10177 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010178 syntax = DQSYNTAX;
10179 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010180 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010181 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010182 if (eofmark != NULL && arinest == 0
10183 && varnest == 0
10184 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010185 USTPUTC(c, out);
10186 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010187 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010188 syntax = BASESYNTAX;
10189 dblquote = 0;
10190 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010191 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010192 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010193 }
10194 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010195 case CVAR: /* '$' */
10196 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010197 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010198 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010199 if (varnest > 0) {
10200 varnest--;
10201 if (dqvarnest > 0) {
10202 dqvarnest--;
10203 }
10204 USTPUTC(CTLENDVAR, out);
10205 } else {
10206 USTPUTC(c, out);
10207 }
10208 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010209#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010210 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010211 parenlevel++;
10212 USTPUTC(c, out);
10213 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010214 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010215 if (parenlevel > 0) {
10216 USTPUTC(c, out);
10217 --parenlevel;
10218 } else {
10219 if (pgetc() == ')') {
10220 if (--arinest == 0) {
10221 USTPUTC(CTLENDARI, out);
10222 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010223 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010224 } else
10225 USTPUTC(')', out);
10226 } else {
10227 /*
10228 * unbalanced parens
10229 * (don't 2nd guess - no error)
10230 */
10231 pungetc();
10232 USTPUTC(')', out);
10233 }
10234 }
10235 break;
10236#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010237 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010238 PARSEBACKQOLD();
10239 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010240 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010241 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010242 case CIGN:
10243 break;
10244 default:
10245 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010246 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010247#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010248 if (c != PEOA)
10249#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010250 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010251
Eric Andersencb57d552001-06-28 07:25:16 +000010252 }
10253 c = pgetc_macro();
10254 }
10255 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010256 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010257#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010258 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010259 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010260#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010261 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010262 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010263 if (varnest != 0) {
10264 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010265 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010266 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010267 }
10268 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010269 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010270 out = stackblock();
10271 if (eofmark == NULL) {
10272 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010273 && quotef == 0
10274 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010275 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010276 PARSEREDIR();
10277 return lasttoken = TREDIR;
10278 } else {
10279 pungetc();
10280 }
10281 }
10282 quoteflag = quotef;
10283 backquotelist = bqlist;
10284 grabstackblock(len);
10285 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010286 lasttoken = TWORD;
10287 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010288/* end of readtoken routine */
10289
Eric Andersencb57d552001-06-28 07:25:16 +000010290/*
10291 * Check to see whether we are at the end of the here document. When this
10292 * is called, c is set to the first character of the next input line. If
10293 * we are at the end of the here document, this routine sets the c to PEOF.
10294 */
Eric Andersenc470f442003-07-28 09:56:35 +000010295checkend: {
10296 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010297#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010298 if (c == PEOA) {
10299 c = pgetc2();
10300 }
10301#endif
10302 if (striptabs) {
10303 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010304 c = pgetc2();
10305 }
Eric Andersenc470f442003-07-28 09:56:35 +000010306 }
10307 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010308 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010309 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010310
Eric Andersenc470f442003-07-28 09:56:35 +000010311 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010312 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010313 if (*p == '\n' && *q == '\0') {
10314 c = PEOF;
10315 plinno++;
10316 needprompt = doprompt;
10317 } else {
10318 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010319 }
10320 }
10321 }
10322 }
Eric Andersenc470f442003-07-28 09:56:35 +000010323 goto checkend_return;
10324}
Eric Andersencb57d552001-06-28 07:25:16 +000010325
Eric Andersencb57d552001-06-28 07:25:16 +000010326/*
10327 * Parse a redirection operator. The variable "out" points to a string
10328 * specifying the fd to be redirected. The variable "c" contains the
10329 * first character of the redirection operator.
10330 */
Eric Andersenc470f442003-07-28 09:56:35 +000010331parseredir: {
10332 char fd = *out;
10333 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010334
Denis Vlasenko597906c2008-02-20 16:38:54 +000010335 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010336 if (c == '>') {
10337 np->nfile.fd = 1;
10338 c = pgetc();
10339 if (c == '>')
10340 np->type = NAPPEND;
10341 else if (c == '|')
10342 np->type = NCLOBBER;
10343 else if (c == '&')
10344 np->type = NTOFD;
10345 else {
10346 np->type = NTO;
10347 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010348 }
Eric Andersenc470f442003-07-28 09:56:35 +000010349 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010350 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010351 c = pgetc();
10352 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010353 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010354 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010355 np = stzalloc(sizeof(struct nhere));
10356 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010357 }
10358 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010359 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010360 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010361 c = pgetc();
10362 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010363 heredoc->striptabs = 1;
10364 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010365 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010366 pungetc();
10367 }
10368 break;
10369
10370 case '&':
10371 np->type = NFROMFD;
10372 break;
10373
10374 case '>':
10375 np->type = NFROMTO;
10376 break;
10377
10378 default:
10379 np->type = NFROM;
10380 pungetc();
10381 break;
10382 }
Eric Andersencb57d552001-06-28 07:25:16 +000010383 }
Eric Andersenc470f442003-07-28 09:56:35 +000010384 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010385 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010386 redirnode = np;
10387 goto parseredir_return;
10388}
Eric Andersencb57d552001-06-28 07:25:16 +000010389
Eric Andersencb57d552001-06-28 07:25:16 +000010390/*
10391 * Parse a substitution. At this point, we have read the dollar sign
10392 * and nothing else.
10393 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010394
10395/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10396 * (assuming ascii char codes, as the original implementation did) */
10397#define is_special(c) \
10398 ((((unsigned int)c) - 33 < 32) \
10399 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010400parsesub: {
10401 int subtype;
10402 int typeloc;
10403 int flags;
10404 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010405 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010406
Eric Andersenc470f442003-07-28 09:56:35 +000010407 c = pgetc();
10408 if (
10409 c <= PEOA_OR_PEOF ||
10410 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10411 ) {
10412 USTPUTC('$', out);
10413 pungetc();
10414 } else if (c == '(') { /* $(command) or $((arith)) */
10415 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010416#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010417 PARSEARITH();
10418#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010419 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010420#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010421 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010422 pungetc();
10423 PARSEBACKQNEW();
10424 }
10425 } else {
10426 USTPUTC(CTLVAR, out);
10427 typeloc = out - (char *)stackblock();
10428 USTPUTC(VSNORMAL, out);
10429 subtype = VSNORMAL;
10430 if (c == '{') {
10431 c = pgetc();
10432 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010433 c = pgetc();
10434 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010435 c = '#';
10436 else
10437 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010438 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010439 subtype = 0;
10440 }
10441 if (c > PEOA_OR_PEOF && is_name(c)) {
10442 do {
10443 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010444 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010445 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010446 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010447 do {
10448 STPUTC(c, out);
10449 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010450 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010451 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010452 USTPUTC(c, out);
10453 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010454 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010455 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010456
Eric Andersenc470f442003-07-28 09:56:35 +000010457 STPUTC('=', out);
10458 flags = 0;
10459 if (subtype == 0) {
10460 switch (c) {
10461 case ':':
10462 flags = VSNUL;
10463 c = pgetc();
10464 /*FALLTHROUGH*/
10465 default:
10466 p = strchr(types, c);
10467 if (p == NULL)
10468 goto badsub;
10469 subtype = p - types + VSNORMAL;
10470 break;
10471 case '%':
10472 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010473 {
10474 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010475 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010476 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010477 c = pgetc();
10478 if (c == cc)
10479 subtype++;
10480 else
10481 pungetc();
10482 break;
10483 }
10484 }
Eric Andersenc470f442003-07-28 09:56:35 +000010485 } else {
10486 pungetc();
10487 }
10488 if (dblquote || arinest)
10489 flags |= VSQUOTE;
10490 *((char *)stackblock() + typeloc) = subtype | flags;
10491 if (subtype != VSNORMAL) {
10492 varnest++;
10493 if (dblquote || arinest) {
10494 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010495 }
10496 }
10497 }
Eric Andersenc470f442003-07-28 09:56:35 +000010498 goto parsesub_return;
10499}
Eric Andersencb57d552001-06-28 07:25:16 +000010500
Eric Andersencb57d552001-06-28 07:25:16 +000010501/*
10502 * Called to parse command substitutions. Newstyle is set if the command
10503 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10504 * list of commands (passed by reference), and savelen is the number of
10505 * characters on the top of the stack which must be preserved.
10506 */
Eric Andersenc470f442003-07-28 09:56:35 +000010507parsebackq: {
10508 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010509 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010510 union node *n;
10511 char *volatile str;
10512 struct jmploc jmploc;
10513 struct jmploc *volatile savehandler;
10514 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010515 smallint saveprompt = 0;
10516
Eric Andersencb57d552001-06-28 07:25:16 +000010517#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010518 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010519#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010520 savepbq = parsebackquote;
10521 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010522 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010523 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010524 exception_handler = savehandler;
10525 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010526 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010527 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010528 str = NULL;
10529 savelen = out - (char *)stackblock();
10530 if (savelen > 0) {
10531 str = ckmalloc(savelen);
10532 memcpy(str, stackblock(), savelen);
10533 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010534 savehandler = exception_handler;
10535 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010536 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010537 if (oldstyle) {
10538 /* We must read until the closing backquote, giving special
10539 treatment to some slashes, and then push the string and
10540 reread it as input, interpreting it normally. */
10541 char *pout;
10542 int pc;
10543 size_t psavelen;
10544 char *pstr;
10545
10546
10547 STARTSTACKSTR(pout);
10548 for (;;) {
10549 if (needprompt) {
10550 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010551 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010552 pc = pgetc();
10553 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010554 case '`':
10555 goto done;
10556
10557 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010558 pc = pgetc();
10559 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010560 plinno++;
10561 if (doprompt)
10562 setprompt(2);
10563 /*
10564 * If eating a newline, avoid putting
10565 * the newline into the new character
10566 * stream (via the STPUTC after the
10567 * switch).
10568 */
10569 continue;
10570 }
10571 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010572 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010573 STPUTC('\\', pout);
10574 if (pc > PEOA_OR_PEOF) {
10575 break;
10576 }
10577 /* fall through */
10578
10579 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010580#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010581 case PEOA:
10582#endif
10583 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010584 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010585
10586 case '\n':
10587 plinno++;
10588 needprompt = doprompt;
10589 break;
10590
10591 default:
10592 break;
10593 }
10594 STPUTC(pc, pout);
10595 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010596 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010597 STPUTC('\0', pout);
10598 psavelen = pout - (char *)stackblock();
10599 if (psavelen > 0) {
10600 pstr = grabstackstr(pout);
10601 setinputstring(pstr);
10602 }
10603 }
10604 nlpp = &bqlist;
10605 while (*nlpp)
10606 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010607 *nlpp = stzalloc(sizeof(**nlpp));
10608 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010609 parsebackquote = oldstyle;
10610
10611 if (oldstyle) {
10612 saveprompt = doprompt;
10613 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010614 }
10615
Eric Andersenc470f442003-07-28 09:56:35 +000010616 n = list(2);
10617
10618 if (oldstyle)
10619 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010620 else if (readtoken() != TRP)
10621 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010622
10623 (*nlpp)->n = n;
10624 if (oldstyle) {
10625 /*
10626 * Start reading from old file again, ignoring any pushed back
10627 * tokens left from the backquote parsing
10628 */
10629 popfile();
10630 tokpushback = 0;
10631 }
10632 while (stackblocksize() <= savelen)
10633 growstackblock();
10634 STARTSTACKSTR(out);
10635 if (str) {
10636 memcpy(out, str, savelen);
10637 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010638 INT_OFF;
10639 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010640 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010641 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010642 }
10643 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010644 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010645 if (arinest || dblquote)
10646 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10647 else
10648 USTPUTC(CTLBACKQ, out);
10649 if (oldstyle)
10650 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010651 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010652}
10653
Denis Vlasenko131ae172007-02-18 13:00:19 +000010654#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010655/*
10656 * Parse an arithmetic expansion (indicate start of one and set state)
10657 */
Eric Andersenc470f442003-07-28 09:56:35 +000010658parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010659 if (++arinest == 1) {
10660 prevsyntax = syntax;
10661 syntax = ARISYNTAX;
10662 USTPUTC(CTLARI, out);
10663 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010664 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010665 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010666 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010667 } else {
10668 /*
10669 * we collapse embedded arithmetic expansion to
10670 * parenthesis, which should be equivalent
10671 */
10672 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010673 }
Eric Andersenc470f442003-07-28 09:56:35 +000010674 goto parsearith_return;
10675}
10676#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010677
Eric Andersenc470f442003-07-28 09:56:35 +000010678} /* end of readtoken */
10679
Eric Andersencb57d552001-06-28 07:25:16 +000010680/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010681 * Read the next input token.
10682 * If the token is a word, we set backquotelist to the list of cmds in
10683 * backquotes. We set quoteflag to true if any part of the word was
10684 * quoted.
10685 * If the token is TREDIR, then we set redirnode to a structure containing
10686 * the redirection.
10687 * In all cases, the variable startlinno is set to the number of the line
10688 * on which the token starts.
10689 *
10690 * [Change comment: here documents and internal procedures]
10691 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10692 * word parsing code into a separate routine. In this case, readtoken
10693 * doesn't need to have any internal procedures, but parseword does.
10694 * We could also make parseoperator in essence the main routine, and
10695 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010696 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010697#define NEW_xxreadtoken
10698#ifdef NEW_xxreadtoken
10699/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010700static const char xxreadtoken_chars[7] ALIGN1 = {
10701 '\n', '(', ')', '&', '|', ';', 0
10702};
Eric Andersencb57d552001-06-28 07:25:16 +000010703
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010704static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010705 TNL, TLP, TRP, /* only single occurrence allowed */
10706 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10707 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010708 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010709};
10710
10711#define xxreadtoken_doubles \
10712 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10713#define xxreadtoken_singles \
10714 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10715
10716static int
10717xxreadtoken(void)
10718{
10719 int c;
10720
10721 if (tokpushback) {
10722 tokpushback = 0;
10723 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010724 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010725 if (needprompt) {
10726 setprompt(2);
10727 }
10728 startlinno = plinno;
10729 for (;;) { /* until token or start of word found */
10730 c = pgetc_macro();
10731
10732 if ((c != ' ') && (c != '\t')
10733#if ENABLE_ASH_ALIAS
10734 && (c != PEOA)
10735#endif
10736 ) {
10737 if (c == '#') {
10738 while ((c = pgetc()) != '\n' && c != PEOF);
10739 pungetc();
10740 } else if (c == '\\') {
10741 if (pgetc() != '\n') {
10742 pungetc();
10743 goto READTOKEN1;
10744 }
10745 startlinno = ++plinno;
10746 if (doprompt)
10747 setprompt(2);
10748 } else {
10749 const char *p
10750 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10751
10752 if (c != PEOF) {
10753 if (c == '\n') {
10754 plinno++;
10755 needprompt = doprompt;
10756 }
10757
10758 p = strchr(xxreadtoken_chars, c);
10759 if (p == NULL) {
10760 READTOKEN1:
10761 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10762 }
10763
10764 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10765 if (pgetc() == *p) { /* double occurrence? */
10766 p += xxreadtoken_doubles + 1;
10767 } else {
10768 pungetc();
10769 }
10770 }
10771 }
10772 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10773 }
10774 }
10775 } /* for */
10776}
10777#else
10778#define RETURN(token) return lasttoken = token
10779static int
10780xxreadtoken(void)
10781{
10782 int c;
10783
10784 if (tokpushback) {
10785 tokpushback = 0;
10786 return lasttoken;
10787 }
10788 if (needprompt) {
10789 setprompt(2);
10790 }
10791 startlinno = plinno;
10792 for (;;) { /* until token or start of word found */
10793 c = pgetc_macro();
10794 switch (c) {
10795 case ' ': case '\t':
10796#if ENABLE_ASH_ALIAS
10797 case PEOA:
10798#endif
10799 continue;
10800 case '#':
10801 while ((c = pgetc()) != '\n' && c != PEOF);
10802 pungetc();
10803 continue;
10804 case '\\':
10805 if (pgetc() == '\n') {
10806 startlinno = ++plinno;
10807 if (doprompt)
10808 setprompt(2);
10809 continue;
10810 }
10811 pungetc();
10812 goto breakloop;
10813 case '\n':
10814 plinno++;
10815 needprompt = doprompt;
10816 RETURN(TNL);
10817 case PEOF:
10818 RETURN(TEOF);
10819 case '&':
10820 if (pgetc() == '&')
10821 RETURN(TAND);
10822 pungetc();
10823 RETURN(TBACKGND);
10824 case '|':
10825 if (pgetc() == '|')
10826 RETURN(TOR);
10827 pungetc();
10828 RETURN(TPIPE);
10829 case ';':
10830 if (pgetc() == ';')
10831 RETURN(TENDCASE);
10832 pungetc();
10833 RETURN(TSEMI);
10834 case '(':
10835 RETURN(TLP);
10836 case ')':
10837 RETURN(TRP);
10838 default:
10839 goto breakloop;
10840 }
10841 }
10842 breakloop:
10843 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10844#undef RETURN
10845}
10846#endif /* NEW_xxreadtoken */
10847
10848static int
10849readtoken(void)
10850{
10851 int t;
10852#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010853 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010854#endif
10855
10856#if ENABLE_ASH_ALIAS
10857 top:
10858#endif
10859
10860 t = xxreadtoken();
10861
10862 /*
10863 * eat newlines
10864 */
10865 if (checkkwd & CHKNL) {
10866 while (t == TNL) {
10867 parseheredoc();
10868 t = xxreadtoken();
10869 }
10870 }
10871
10872 if (t != TWORD || quoteflag) {
10873 goto out;
10874 }
10875
10876 /*
10877 * check for keywords
10878 */
10879 if (checkkwd & CHKKWD) {
10880 const char *const *pp;
10881
10882 pp = findkwd(wordtext);
10883 if (pp) {
10884 lasttoken = t = pp - tokname_array;
10885 TRACE(("keyword %s recognized\n", tokname(t)));
10886 goto out;
10887 }
10888 }
10889
10890 if (checkkwd & CHKALIAS) {
10891#if ENABLE_ASH_ALIAS
10892 struct alias *ap;
10893 ap = lookupalias(wordtext, 1);
10894 if (ap != NULL) {
10895 if (*ap->val) {
10896 pushstring(ap->val, ap);
10897 }
10898 goto top;
10899 }
10900#endif
10901 }
10902 out:
10903 checkkwd = 0;
10904#if DEBUG
10905 if (!alreadyseen)
10906 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10907 else
10908 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10909#endif
10910 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010911}
10912
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010913static char
10914peektoken(void)
10915{
10916 int t;
10917
10918 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010919 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010920 return tokname_array[t][0];
10921}
Eric Andersencb57d552001-06-28 07:25:16 +000010922
10923/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010924 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10925 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010926 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010927static union node *
10928parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010929{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010930 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010931
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010932 tokpushback = 0;
10933 doprompt = interact;
10934 if (doprompt)
10935 setprompt(doprompt);
10936 needprompt = 0;
10937 t = readtoken();
10938 if (t == TEOF)
10939 return NEOF;
10940 if (t == TNL)
10941 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010942 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010943 return list(1);
10944}
10945
10946/*
10947 * Input any here documents.
10948 */
10949static void
10950parseheredoc(void)
10951{
10952 struct heredoc *here;
10953 union node *n;
10954
10955 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010956 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010957
10958 while (here) {
10959 if (needprompt) {
10960 setprompt(2);
10961 }
10962 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10963 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010964 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010965 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010966 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010967 n->narg.text = wordtext;
10968 n->narg.backquote = backquotelist;
10969 here->here->nhere.doc = n;
10970 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010971 }
Eric Andersencb57d552001-06-28 07:25:16 +000010972}
10973
10974
10975/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010976 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010977 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010978#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010979static const char *
10980expandstr(const char *ps)
10981{
10982 union node n;
10983
10984 /* XXX Fix (char *) cast. */
10985 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010986 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010987 popfile();
10988
10989 n.narg.type = NARG;
10990 n.narg.next = NULL;
10991 n.narg.text = wordtext;
10992 n.narg.backquote = backquotelist;
10993
10994 expandarg(&n, NULL, 0);
10995 return stackblock();
10996}
10997#endif
10998
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010999/*
11000 * Execute a command or commands contained in a string.
11001 */
11002static int
11003evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011004{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011005 union node *n;
11006 struct stackmark smark;
11007 int skip;
11008
11009 setinputstring(s);
11010 setstackmark(&smark);
11011
11012 skip = 0;
11013 while ((n = parsecmd(0)) != NEOF) {
11014 evaltree(n, 0);
11015 popstackmark(&smark);
11016 skip = evalskip;
11017 if (skip)
11018 break;
11019 }
11020 popfile();
11021
11022 skip &= mask;
11023 evalskip = skip;
11024 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011025}
11026
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011027/*
11028 * The eval command.
11029 */
11030static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011031evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011032{
11033 char *p;
11034 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011035
Denis Vlasenko68404f12008-03-17 09:00:54 +000011036 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011037 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011038 argv += 2;
11039 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011040 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011041 for (;;) {
11042 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011043 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011044 if (p == NULL)
11045 break;
11046 STPUTC(' ', concat);
11047 }
11048 STPUTC('\0', concat);
11049 p = grabstackstr(concat);
11050 }
11051 evalstring(p, ~SKIPEVAL);
11052
11053 }
11054 return exitstatus;
11055}
11056
11057/*
11058 * Read and execute commands. "Top" is nonzero for the top level command
11059 * loop; it turns on prompting if the shell is interactive.
11060 */
11061static int
11062cmdloop(int top)
11063{
11064 union node *n;
11065 struct stackmark smark;
11066 int inter;
11067 int numeof = 0;
11068
11069 TRACE(("cmdloop(%d) called\n", top));
11070 for (;;) {
11071 int skip;
11072
11073 setstackmark(&smark);
11074#if JOBS
11075 if (jobctl)
11076 showjobs(stderr, SHOW_CHANGED);
11077#endif
11078 inter = 0;
11079 if (iflag && top) {
11080 inter++;
11081#if ENABLE_ASH_MAIL
11082 chkmail();
11083#endif
11084 }
11085 n = parsecmd(inter);
11086 /* showtree(n); DEBUG */
11087 if (n == NEOF) {
11088 if (!top || numeof >= 50)
11089 break;
11090 if (!stoppedjobs()) {
11091 if (!Iflag)
11092 break;
11093 out2str("\nUse \"exit\" to leave shell.\n");
11094 }
11095 numeof++;
11096 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011097 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11098 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011099 numeof = 0;
11100 evaltree(n, 0);
11101 }
11102 popstackmark(&smark);
11103 skip = evalskip;
11104
11105 if (skip) {
11106 evalskip = 0;
11107 return skip & SKIPEVAL;
11108 }
11109 }
11110 return 0;
11111}
11112
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011113/*
11114 * Take commands from a file. To be compatible we should do a path
11115 * search for the file, which is necessary to find sub-commands.
11116 */
11117static char *
11118find_dot_file(char *name)
11119{
11120 char *fullname;
11121 const char *path = pathval();
11122 struct stat statb;
11123
11124 /* don't try this for absolute or relative paths */
11125 if (strchr(name, '/'))
11126 return name;
11127
11128 while ((fullname = padvance(&path, name)) != NULL) {
11129 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11130 /*
11131 * Don't bother freeing here, since it will
11132 * be freed by the caller.
11133 */
11134 return fullname;
11135 }
11136 stunalloc(fullname);
11137 }
11138
11139 /* not found in the PATH */
11140 ash_msg_and_raise_error("%s: not found", name);
11141 /* NOTREACHED */
11142}
11143
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011144static int
11145dotcmd(int argc, char **argv)
11146{
11147 struct strlist *sp;
11148 volatile struct shparam saveparam;
11149 int status = 0;
11150
11151 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011152 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011153
Denis Vlasenko68404f12008-03-17 09:00:54 +000011154 if (argv[1]) { /* That's what SVR2 does */
11155 char *fullname = find_dot_file(argv[1]);
11156 argv += 2;
11157 argc -= 2;
11158 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011159 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011160 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011161 shellparam.nparam = argc;
11162 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011163 };
11164
11165 setinputfile(fullname, INPUT_PUSH_FILE);
11166 commandname = fullname;
11167 cmdloop(0);
11168 popfile();
11169
Denis Vlasenko68404f12008-03-17 09:00:54 +000011170 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011171 freeparam(&shellparam);
11172 shellparam = saveparam;
11173 };
11174 status = exitstatus;
11175 }
11176 return status;
11177}
11178
11179static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011180exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011181{
11182 if (stoppedjobs())
11183 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011184 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011185 exitstatus = number(argv[1]);
11186 raise_exception(EXEXIT);
11187 /* NOTREACHED */
11188}
11189
11190#if ENABLE_ASH_BUILTIN_ECHO
11191static int
11192echocmd(int argc, char **argv)
11193{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011194 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011195}
11196#endif
11197
11198#if ENABLE_ASH_BUILTIN_TEST
11199static int
11200testcmd(int argc, char **argv)
11201{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011202 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011203}
11204#endif
11205
11206/*
11207 * Read a file containing shell functions.
11208 */
11209static void
11210readcmdfile(char *name)
11211{
11212 setinputfile(name, INPUT_PUSH_FILE);
11213 cmdloop(0);
11214 popfile();
11215}
11216
11217
Denis Vlasenkocc571512007-02-23 21:10:35 +000011218/* ============ find_command inplementation */
11219
11220/*
11221 * Resolve a command name. If you change this routine, you may have to
11222 * change the shellexec routine as well.
11223 */
11224static void
11225find_command(char *name, struct cmdentry *entry, int act, const char *path)
11226{
11227 struct tblentry *cmdp;
11228 int idx;
11229 int prev;
11230 char *fullname;
11231 struct stat statb;
11232 int e;
11233 int updatetbl;
11234 struct builtincmd *bcmd;
11235
11236 /* If name contains a slash, don't use PATH or hash table */
11237 if (strchr(name, '/') != NULL) {
11238 entry->u.index = -1;
11239 if (act & DO_ABS) {
11240 while (stat(name, &statb) < 0) {
11241#ifdef SYSV
11242 if (errno == EINTR)
11243 continue;
11244#endif
11245 entry->cmdtype = CMDUNKNOWN;
11246 return;
11247 }
11248 }
11249 entry->cmdtype = CMDNORMAL;
11250 return;
11251 }
11252
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011253/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011254
11255 updatetbl = (path == pathval());
11256 if (!updatetbl) {
11257 act |= DO_ALTPATH;
11258 if (strstr(path, "%builtin") != NULL)
11259 act |= DO_ALTBLTIN;
11260 }
11261
11262 /* If name is in the table, check answer will be ok */
11263 cmdp = cmdlookup(name, 0);
11264 if (cmdp != NULL) {
11265 int bit;
11266
11267 switch (cmdp->cmdtype) {
11268 default:
11269#if DEBUG
11270 abort();
11271#endif
11272 case CMDNORMAL:
11273 bit = DO_ALTPATH;
11274 break;
11275 case CMDFUNCTION:
11276 bit = DO_NOFUNC;
11277 break;
11278 case CMDBUILTIN:
11279 bit = DO_ALTBLTIN;
11280 break;
11281 }
11282 if (act & bit) {
11283 updatetbl = 0;
11284 cmdp = NULL;
11285 } else if (cmdp->rehash == 0)
11286 /* if not invalidated by cd, we're done */
11287 goto success;
11288 }
11289
11290 /* If %builtin not in path, check for builtin next */
11291 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011292 if (bcmd) {
11293 if (IS_BUILTIN_REGULAR(bcmd))
11294 goto builtin_success;
11295 if (act & DO_ALTPATH) {
11296 if (!(act & DO_ALTBLTIN))
11297 goto builtin_success;
11298 } else if (builtinloc <= 0) {
11299 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011300 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011301 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011302
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011303#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011304 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011305 entry->cmdtype = CMDNORMAL;
11306 entry->u.index = -1;
11307 return;
11308 }
11309#endif
11310
Denis Vlasenkocc571512007-02-23 21:10:35 +000011311 /* We have to search path. */
11312 prev = -1; /* where to start */
11313 if (cmdp && cmdp->rehash) { /* doing a rehash */
11314 if (cmdp->cmdtype == CMDBUILTIN)
11315 prev = builtinloc;
11316 else
11317 prev = cmdp->param.index;
11318 }
11319
11320 e = ENOENT;
11321 idx = -1;
11322 loop:
11323 while ((fullname = padvance(&path, name)) != NULL) {
11324 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011325 /* NB: code below will still use fullname
11326 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011327 idx++;
11328 if (pathopt) {
11329 if (prefix(pathopt, "builtin")) {
11330 if (bcmd)
11331 goto builtin_success;
11332 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011333 } else if (!(act & DO_NOFUNC)
11334 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011335 /* handled below */
11336 } else {
11337 /* ignore unimplemented options */
11338 continue;
11339 }
11340 }
11341 /* if rehash, don't redo absolute path names */
11342 if (fullname[0] == '/' && idx <= prev) {
11343 if (idx < prev)
11344 continue;
11345 TRACE(("searchexec \"%s\": no change\n", name));
11346 goto success;
11347 }
11348 while (stat(fullname, &statb) < 0) {
11349#ifdef SYSV
11350 if (errno == EINTR)
11351 continue;
11352#endif
11353 if (errno != ENOENT && errno != ENOTDIR)
11354 e = errno;
11355 goto loop;
11356 }
11357 e = EACCES; /* if we fail, this will be the error */
11358 if (!S_ISREG(statb.st_mode))
11359 continue;
11360 if (pathopt) { /* this is a %func directory */
11361 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011362 /* NB: stalloc will return space pointed by fullname
11363 * (because we don't have any intervening allocations
11364 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011365 readcmdfile(fullname);
11366 cmdp = cmdlookup(name, 0);
11367 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11368 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11369 stunalloc(fullname);
11370 goto success;
11371 }
11372 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11373 if (!updatetbl) {
11374 entry->cmdtype = CMDNORMAL;
11375 entry->u.index = idx;
11376 return;
11377 }
11378 INT_OFF;
11379 cmdp = cmdlookup(name, 1);
11380 cmdp->cmdtype = CMDNORMAL;
11381 cmdp->param.index = idx;
11382 INT_ON;
11383 goto success;
11384 }
11385
11386 /* We failed. If there was an entry for this command, delete it */
11387 if (cmdp && updatetbl)
11388 delete_cmd_entry();
11389 if (act & DO_ERR)
11390 ash_msg("%s: %s", name, errmsg(e, "not found"));
11391 entry->cmdtype = CMDUNKNOWN;
11392 return;
11393
11394 builtin_success:
11395 if (!updatetbl) {
11396 entry->cmdtype = CMDBUILTIN;
11397 entry->u.cmd = bcmd;
11398 return;
11399 }
11400 INT_OFF;
11401 cmdp = cmdlookup(name, 1);
11402 cmdp->cmdtype = CMDBUILTIN;
11403 cmdp->param.cmd = bcmd;
11404 INT_ON;
11405 success:
11406 cmdp->rehash = 0;
11407 entry->cmdtype = cmdp->cmdtype;
11408 entry->u = cmdp->param;
11409}
11410
11411
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011412/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011413
Eric Andersencb57d552001-06-28 07:25:16 +000011414/*
Eric Andersencb57d552001-06-28 07:25:16 +000011415 * The trap builtin.
11416 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011417static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011418trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011419{
11420 char *action;
11421 char **ap;
11422 int signo;
11423
Eric Andersenc470f442003-07-28 09:56:35 +000011424 nextopt(nullstr);
11425 ap = argptr;
11426 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011427 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011428 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011429 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011430
Rob Landleyc9c1a412006-07-12 19:17:55 +000011431 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011432 out1fmt("trap -- %s %s\n",
11433 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011434 }
11435 }
11436 return 0;
11437 }
Eric Andersenc470f442003-07-28 09:56:35 +000011438 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011439 action = NULL;
11440 else
11441 action = *ap++;
11442 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011443 signo = get_signum(*ap);
11444 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011445 ash_msg_and_raise_error("%s: bad trap", *ap);
11446 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011447 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011448 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011449 action = NULL;
11450 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011451 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011452 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011453 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011454 trap[signo] = action;
11455 if (signo != 0)
11456 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011457 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011458 ap++;
11459 }
11460 return 0;
11461}
11462
Eric Andersenc470f442003-07-28 09:56:35 +000011463
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011464/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011465
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011466#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011467/*
11468 * Lists available builtins
11469 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011470static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011471helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011472{
11473 int col, i;
11474
11475 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011476 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011477 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011478 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011479 if (col > 60) {
11480 out1fmt("\n");
11481 col = 0;
11482 }
11483 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011484#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011485 {
11486 const char *a = applet_names;
11487 while (*a) {
11488 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11489 if (col > 60) {
11490 out1fmt("\n");
11491 col = 0;
11492 }
11493 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011494 }
11495 }
11496#endif
11497 out1fmt("\n\n");
11498 return EXIT_SUCCESS;
11499}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011500#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011501
Eric Andersencb57d552001-06-28 07:25:16 +000011502/*
Eric Andersencb57d552001-06-28 07:25:16 +000011503 * The export and readonly commands.
11504 */
Eric Andersenc470f442003-07-28 09:56:35 +000011505static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011506exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011507{
11508 struct var *vp;
11509 char *name;
11510 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011511 char **aptr;
11512 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011513
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011514 if (nextopt("p") != 'p') {
11515 aptr = argptr;
11516 name = *aptr;
11517 if (name) {
11518 do {
11519 p = strchr(name, '=');
11520 if (p != NULL) {
11521 p++;
11522 } else {
11523 vp = *findvar(hashvar(name), name);
11524 if (vp) {
11525 vp->flags |= flag;
11526 continue;
11527 }
Eric Andersencb57d552001-06-28 07:25:16 +000011528 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011529 setvar(name, p, flag);
11530 } while ((name = *++aptr) != NULL);
11531 return 0;
11532 }
Eric Andersencb57d552001-06-28 07:25:16 +000011533 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011534 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011535 return 0;
11536}
11537
Eric Andersencb57d552001-06-28 07:25:16 +000011538/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011539 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011540 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011541static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011542unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011543{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011544 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011545
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011546 cmdp = cmdlookup(name, 0);
11547 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11548 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011549}
11550
Eric Andersencb57d552001-06-28 07:25:16 +000011551/*
Eric Andersencb57d552001-06-28 07:25:16 +000011552 * The unset builtin command. We unset the function before we unset the
11553 * variable to allow a function to be unset when there is a readonly variable
11554 * with the same name.
11555 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011556static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011557unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011558{
11559 char **ap;
11560 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011561 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011562 int ret = 0;
11563
11564 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011565 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011566 }
Eric Andersencb57d552001-06-28 07:25:16 +000011567
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011568 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011569 if (flag != 'f') {
11570 i = unsetvar(*ap);
11571 ret |= i;
11572 if (!(i & 2))
11573 continue;
11574 }
11575 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011576 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011577 }
Eric Andersenc470f442003-07-28 09:56:35 +000011578 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011579}
11580
11581
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011582/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011583
Eric Andersenc470f442003-07-28 09:56:35 +000011584#include <sys/times.h>
11585
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011586static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011587 ' ', offsetof(struct tms, tms_utime),
11588 '\n', offsetof(struct tms, tms_stime),
11589 ' ', offsetof(struct tms, tms_cutime),
11590 '\n', offsetof(struct tms, tms_cstime),
11591 0
11592};
Eric Andersencb57d552001-06-28 07:25:16 +000011593
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011594static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011595timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011596{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011597 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011598 const unsigned char *p;
11599 struct tms buf;
11600
11601 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011602 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011603
11604 p = timescmd_str;
11605 do {
11606 t = *(clock_t *)(((char *) &buf) + p[1]);
11607 s = t / clk_tck;
11608 out1fmt("%ldm%ld.%.3lds%c",
11609 s/60, s%60,
11610 ((t - s * clk_tck) * 1000) / clk_tck,
11611 p[0]);
11612 } while (*(p += 2));
11613
Eric Andersencb57d552001-06-28 07:25:16 +000011614 return 0;
11615}
11616
Denis Vlasenko131ae172007-02-18 13:00:19 +000011617#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011618static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011619dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011620{
Eric Andersened9ecf72004-06-22 08:29:45 +000011621 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011622 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011623
Denis Vlasenkob012b102007-02-19 22:43:01 +000011624 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011625 result = arith(s, &errcode);
11626 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011627 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011628 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011629 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011630 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011631 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011632 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011633 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011634 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011635 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011636
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011637 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011638}
Eric Andersenc470f442003-07-28 09:56:35 +000011639
Eric Andersenc470f442003-07-28 09:56:35 +000011640/*
Eric Andersen90898442003-08-06 11:20:52 +000011641 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11642 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11643 *
11644 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011645 */
11646static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011647letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011648{
Denis Vlasenko68404f12008-03-17 09:00:54 +000011649 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000011650
Denis Vlasenko68404f12008-03-17 09:00:54 +000011651 argv++;
11652 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011653 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000011654 do {
11655 i = dash_arith(*argv);
11656 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000011657
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011658 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011659}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011660#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011661
Eric Andersenc470f442003-07-28 09:56:35 +000011662
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011663/* ============ miscbltin.c
11664 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011665 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011666 */
11667
11668#undef rflag
11669
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011670#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011671typedef enum __rlimit_resource rlim_t;
11672#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011673
Eric Andersenc470f442003-07-28 09:56:35 +000011674/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011675 * The read builtin. Options:
11676 * -r Do not interpret '\' specially
11677 * -s Turn off echo (tty only)
11678 * -n NCHARS Read NCHARS max
11679 * -p PROMPT Display PROMPT on stderr (if input is from tty)
11680 * -t SECONDS Timeout after SECONDS (tty or pipe only)
11681 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000011682 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011683 * TODO: bash also has:
11684 * -a ARRAY Read into array[0],[1],etc
11685 * -d DELIM End on DELIM char, not newline
11686 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000011687 */
Eric Andersenc470f442003-07-28 09:56:35 +000011688static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011689readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011690{
11691 char **ap;
11692 int backslash;
11693 char c;
11694 int rflag;
11695 char *prompt;
11696 const char *ifs;
11697 char *p;
11698 int startword;
11699 int status;
11700 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011701 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011702#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011703 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000011704 int silent = 0;
11705 struct termios tty, old_tty;
11706#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011707#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011708 unsigned end_ms = 0;
11709 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011710#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011711
11712 rflag = 0;
11713 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011714 while ((i = nextopt("p:u:r"
11715 USE_ASH_READ_TIMEOUT("t:")
11716 USE_ASH_READ_NCHARS("n:s")
11717 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011718 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011719 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011720 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011721 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011722#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011723 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011724 nchars = bb_strtou(optionarg, NULL, 10);
11725 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011726 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011727 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000011728 break;
11729 case 's':
11730 silent = 1;
11731 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011732#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011733#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011734 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011735 timeout = bb_strtou(optionarg, NULL, 10);
11736 if (errno || timeout > UINT_MAX / 2048)
11737 ash_msg_and_raise_error("invalid timeout");
11738 timeout *= 1000;
11739#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000011740 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011741 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011742 /* EINVAL means number is ok, but not terminated by NUL */
11743 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011744 char *p2;
11745 if (*++p) {
11746 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011747 ts.tv_usec = bb_strtou(p, &p2, 10);
11748 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011749 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011750 scale = p2 - p;
11751 /* normalize to usec */
11752 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011753 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011754 while (scale++ < 6)
11755 ts.tv_usec *= 10;
11756 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011757 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011758 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011759 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011760 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011761 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011762 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011763#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000011764 break;
11765#endif
11766 case 'r':
11767 rflag = 1;
11768 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011769 case 'u':
11770 fd = bb_strtou(optionarg, NULL, 10);
11771 if (fd < 0 || errno)
11772 ash_msg_and_raise_error("invalid file descriptor");
11773 break;
Paul Fox02eb9342005-09-07 16:56:02 +000011774 default:
11775 break;
11776 }
Eric Andersenc470f442003-07-28 09:56:35 +000011777 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011778 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011779 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011780 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011781 ap = argptr;
11782 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011783 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011784 ifs = bltinlookup("IFS");
11785 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011786 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011787#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011788 tcgetattr(fd, &tty);
11789 old_tty = tty;
11790 if (nchars || silent) {
11791 if (nchars) {
11792 tty.c_lflag &= ~ICANON;
11793 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000011794 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011795 if (silent) {
11796 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11797 }
11798 /* if tcgetattr failed, tcsetattr will fail too.
11799 * Ignoring, it's harmless. */
11800 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011801 }
11802#endif
Paul Fox02eb9342005-09-07 16:56:02 +000011803
Eric Andersenc470f442003-07-28 09:56:35 +000011804 status = 0;
11805 startword = 1;
11806 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011807#if ENABLE_ASH_READ_TIMEOUT
11808 if (timeout) /* NB: ensuring end_ms is nonzero */
11809 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
11810#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011811 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011812 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011813#if ENABLE_ASH_READ_TIMEOUT
11814 if (end_ms) {
11815 struct pollfd pfd[1];
11816 pfd[0].fd = fd;
11817 pfd[0].events = POLLIN;
11818 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
11819 if ((int)timeout <= 0 /* already late? */
11820 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
11821 ) { /* timed out! */
11822#if ENABLE_ASH_READ_NCHARS
11823 tcsetattr(fd, TCSANOW, &old_tty);
11824#endif
11825 return 1;
11826 }
11827 }
11828#endif
11829 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011830 status = 1;
11831 break;
11832 }
11833 if (c == '\0')
11834 continue;
11835 if (backslash) {
11836 backslash = 0;
11837 if (c != '\n')
11838 goto put;
11839 continue;
11840 }
11841 if (!rflag && c == '\\') {
11842 backslash++;
11843 continue;
11844 }
11845 if (c == '\n')
11846 break;
11847 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11848 continue;
11849 }
11850 startword = 0;
11851 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11852 STACKSTRNUL(p);
11853 setvar(*ap, stackblock(), 0);
11854 ap++;
11855 startword = 1;
11856 STARTSTACKSTR(p);
11857 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011858 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011859 STPUTC(c, p);
11860 }
11861 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011862/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011863#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011864 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011865#else
11866 while (1);
11867#endif
11868
11869#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011870 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011871#endif
11872
Eric Andersenc470f442003-07-28 09:56:35 +000011873 STACKSTRNUL(p);
11874 /* Remove trailing blanks */
11875 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11876 *p = '\0';
11877 setvar(*ap, stackblock(), 0);
11878 while (*++ap != NULL)
11879 setvar(*ap, nullstr, 0);
11880 return status;
11881}
11882
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011883static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011884umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011885{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011886 static const char permuser[3] ALIGN1 = "ugo";
11887 static const char permmode[3] ALIGN1 = "rwx";
11888 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011889 S_IRUSR, S_IWUSR, S_IXUSR,
11890 S_IRGRP, S_IWGRP, S_IXGRP,
11891 S_IROTH, S_IWOTH, S_IXOTH
11892 };
11893
11894 char *ap;
11895 mode_t mask;
11896 int i;
11897 int symbolic_mode = 0;
11898
11899 while (nextopt("S") != '\0') {
11900 symbolic_mode = 1;
11901 }
11902
Denis Vlasenkob012b102007-02-19 22:43:01 +000011903 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011904 mask = umask(0);
11905 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011906 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011907
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011908 ap = *argptr;
11909 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011910 if (symbolic_mode) {
11911 char buf[18];
11912 char *p = buf;
11913
11914 for (i = 0; i < 3; i++) {
11915 int j;
11916
11917 *p++ = permuser[i];
11918 *p++ = '=';
11919 for (j = 0; j < 3; j++) {
11920 if ((mask & permmask[3 * i + j]) == 0) {
11921 *p++ = permmode[j];
11922 }
11923 }
11924 *p++ = ',';
11925 }
11926 *--p = 0;
11927 puts(buf);
11928 } else {
11929 out1fmt("%.4o\n", mask);
11930 }
11931 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011932 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011933 mask = 0;
11934 do {
11935 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011936 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011937 mask = (mask << 3) + (*ap - '0');
11938 } while (*++ap != '\0');
11939 umask(mask);
11940 } else {
11941 mask = ~mask & 0777;
11942 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011943 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011944 }
11945 umask(~mask & 0777);
11946 }
11947 }
11948 return 0;
11949}
11950
11951/*
11952 * ulimit builtin
11953 *
11954 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11955 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11956 * ash by J.T. Conklin.
11957 *
11958 * Public domain.
11959 */
11960
11961struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011962 uint8_t cmd; /* RLIMIT_xxx fit into it */
11963 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011964 char option;
11965};
11966
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011967static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011968#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011969 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011970#endif
11971#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011972 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011973#endif
11974#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011975 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011976#endif
11977#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011978 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011979#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011980#ifdef RLIMIT_CORE
11981 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011982#endif
11983#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011984 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011985#endif
11986#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011987 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011988#endif
11989#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011990 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011991#endif
11992#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011993 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011994#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011995#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011996 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011997#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011998#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011999 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012000#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012001};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012002static const char limits_name[] =
12003#ifdef RLIMIT_CPU
12004 "time(seconds)" "\0"
12005#endif
12006#ifdef RLIMIT_FSIZE
12007 "file(blocks)" "\0"
12008#endif
12009#ifdef RLIMIT_DATA
12010 "data(kb)" "\0"
12011#endif
12012#ifdef RLIMIT_STACK
12013 "stack(kb)" "\0"
12014#endif
12015#ifdef RLIMIT_CORE
12016 "coredump(blocks)" "\0"
12017#endif
12018#ifdef RLIMIT_RSS
12019 "memory(kb)" "\0"
12020#endif
12021#ifdef RLIMIT_MEMLOCK
12022 "locked memory(kb)" "\0"
12023#endif
12024#ifdef RLIMIT_NPROC
12025 "process" "\0"
12026#endif
12027#ifdef RLIMIT_NOFILE
12028 "nofiles" "\0"
12029#endif
12030#ifdef RLIMIT_AS
12031 "vmemory(kb)" "\0"
12032#endif
12033#ifdef RLIMIT_LOCKS
12034 "locks" "\0"
12035#endif
12036;
Eric Andersenc470f442003-07-28 09:56:35 +000012037
Glenn L McGrath76620622004-01-13 10:19:37 +000012038enum limtype { SOFT = 0x1, HARD = 0x2 };
12039
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012040static void
12041printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012042 const struct limits *l)
12043{
12044 rlim_t val;
12045
12046 val = limit->rlim_max;
12047 if (how & SOFT)
12048 val = limit->rlim_cur;
12049
12050 if (val == RLIM_INFINITY)
12051 out1fmt("unlimited\n");
12052 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012053 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012054 out1fmt("%lld\n", (long long) val);
12055 }
12056}
12057
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012058static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012059ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012060{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012061 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012062 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012063 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012064 const struct limits *l;
12065 int set, all = 0;
12066 int optc, what;
12067 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012068
12069 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012070 while ((optc = nextopt("HSa"
12071#ifdef RLIMIT_CPU
12072 "t"
12073#endif
12074#ifdef RLIMIT_FSIZE
12075 "f"
12076#endif
12077#ifdef RLIMIT_DATA
12078 "d"
12079#endif
12080#ifdef RLIMIT_STACK
12081 "s"
12082#endif
12083#ifdef RLIMIT_CORE
12084 "c"
12085#endif
12086#ifdef RLIMIT_RSS
12087 "m"
12088#endif
12089#ifdef RLIMIT_MEMLOCK
12090 "l"
12091#endif
12092#ifdef RLIMIT_NPROC
12093 "p"
12094#endif
12095#ifdef RLIMIT_NOFILE
12096 "n"
12097#endif
12098#ifdef RLIMIT_AS
12099 "v"
12100#endif
12101#ifdef RLIMIT_LOCKS
12102 "w"
12103#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012104 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012105 switch (optc) {
12106 case 'H':
12107 how = HARD;
12108 break;
12109 case 'S':
12110 how = SOFT;
12111 break;
12112 case 'a':
12113 all = 1;
12114 break;
12115 default:
12116 what = optc;
12117 }
12118
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012119 for (l = limits_tbl; l->option != what; l++)
12120 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012121
12122 set = *argptr ? 1 : 0;
12123 if (set) {
12124 char *p = *argptr;
12125
12126 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012127 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012128 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012129 val = RLIM_INFINITY;
12130 else {
12131 val = (rlim_t) 0;
12132
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012133 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012134 val = (val * 10) + (long)(c - '0');
12135 if (val < (rlim_t) 0)
12136 break;
12137 }
12138 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012139 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012140 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012141 }
12142 }
12143 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012144 const char *lname = limits_name;
12145 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012146 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012147 out1fmt("%-20s ", lname);
12148 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012149 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012150 }
12151 return 0;
12152 }
12153
12154 getrlimit(l->cmd, &limit);
12155 if (set) {
12156 if (how & HARD)
12157 limit.rlim_max = val;
12158 if (how & SOFT)
12159 limit.rlim_cur = val;
12160 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012161 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012162 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012163 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012164 }
12165 return 0;
12166}
12167
Eric Andersen90898442003-08-06 11:20:52 +000012168
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012169/* ============ Math support */
12170
Denis Vlasenko131ae172007-02-18 13:00:19 +000012171#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012172
12173/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12174
12175 Permission is hereby granted, free of charge, to any person obtaining
12176 a copy of this software and associated documentation files (the
12177 "Software"), to deal in the Software without restriction, including
12178 without limitation the rights to use, copy, modify, merge, publish,
12179 distribute, sublicense, and/or sell copies of the Software, and to
12180 permit persons to whom the Software is furnished to do so, subject to
12181 the following conditions:
12182
12183 The above copyright notice and this permission notice shall be
12184 included in all copies or substantial portions of the Software.
12185
12186 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12187 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12188 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12189 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12190 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12191 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12192 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12193*/
12194
12195/* This is my infix parser/evaluator. It is optimized for size, intended
12196 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012197 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012198 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012199 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012200 * be that which POSIX specifies for shells. */
12201
12202/* The code uses a simple two-stack algorithm. See
12203 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012204 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012205 * this is based (this code differs in that it applies operators immediately
12206 * to the stack instead of adding them to a queue to end up with an
12207 * expression). */
12208
12209/* To use the routine, call it with an expression string and error return
12210 * pointer */
12211
12212/*
12213 * Aug 24, 2001 Manuel Novoa III
12214 *
12215 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12216 *
12217 * 1) In arith_apply():
12218 * a) Cached values of *numptr and &(numptr[-1]).
12219 * b) Removed redundant test for zero denominator.
12220 *
12221 * 2) In arith():
12222 * a) Eliminated redundant code for processing operator tokens by moving
12223 * to a table-based implementation. Also folded handling of parens
12224 * into the table.
12225 * b) Combined all 3 loops which called arith_apply to reduce generated
12226 * code size at the cost of speed.
12227 *
12228 * 3) The following expressions were treated as valid by the original code:
12229 * 1() , 0! , 1 ( *3 ) .
12230 * These bugs have been fixed by internally enclosing the expression in
12231 * parens and then checking that all binary ops and right parens are
12232 * preceded by a valid expression (NUM_TOKEN).
12233 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012234 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012235 * ctype's isspace() if it is used by another busybox applet or if additional
12236 * whitespace chars should be considered. Look below the "#include"s for a
12237 * precompiler test.
12238 */
12239
12240/*
12241 * Aug 26, 2001 Manuel Novoa III
12242 *
12243 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12244 *
12245 * Merge in Aaron's comments previously posted to the busybox list,
12246 * modified slightly to take account of my changes to the code.
12247 *
12248 */
12249
12250/*
12251 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12252 *
12253 * - allow access to variable,
12254 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12255 * - realize assign syntax (VAR=expr, +=, *= etc)
12256 * - realize exponentiation (** operator)
12257 * - realize comma separated - expr, expr
12258 * - realise ++expr --expr expr++ expr--
12259 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012260 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012261 * - was restored loses XOR operator
12262 * - remove one goto label, added three ;-)
12263 * - protect $((num num)) as true zero expr (Manuel`s error)
12264 * - always use special isspace(), see comment from bash ;-)
12265 */
12266
Eric Andersen90898442003-08-06 11:20:52 +000012267#define arith_isspace(arithval) \
12268 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12269
Eric Andersen90898442003-08-06 11:20:52 +000012270typedef unsigned char operator;
12271
12272/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012273 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012274 * precedence. The ID portion is so that multiple operators can have the
12275 * same precedence, ensuring that the leftmost one is evaluated first.
12276 * Consider * and /. */
12277
12278#define tok_decl(prec,id) (((id)<<5)|(prec))
12279#define PREC(op) ((op) & 0x1F)
12280
12281#define TOK_LPAREN tok_decl(0,0)
12282
12283#define TOK_COMMA tok_decl(1,0)
12284
12285#define TOK_ASSIGN tok_decl(2,0)
12286#define TOK_AND_ASSIGN tok_decl(2,1)
12287#define TOK_OR_ASSIGN tok_decl(2,2)
12288#define TOK_XOR_ASSIGN tok_decl(2,3)
12289#define TOK_PLUS_ASSIGN tok_decl(2,4)
12290#define TOK_MINUS_ASSIGN tok_decl(2,5)
12291#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12292#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12293
12294#define TOK_MUL_ASSIGN tok_decl(3,0)
12295#define TOK_DIV_ASSIGN tok_decl(3,1)
12296#define TOK_REM_ASSIGN tok_decl(3,2)
12297
12298/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012299#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012300
12301/* conditional is right associativity too */
12302#define TOK_CONDITIONAL tok_decl(4,0)
12303#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12304
12305#define TOK_OR tok_decl(5,0)
12306
12307#define TOK_AND tok_decl(6,0)
12308
12309#define TOK_BOR tok_decl(7,0)
12310
12311#define TOK_BXOR tok_decl(8,0)
12312
12313#define TOK_BAND tok_decl(9,0)
12314
12315#define TOK_EQ tok_decl(10,0)
12316#define TOK_NE tok_decl(10,1)
12317
12318#define TOK_LT tok_decl(11,0)
12319#define TOK_GT tok_decl(11,1)
12320#define TOK_GE tok_decl(11,2)
12321#define TOK_LE tok_decl(11,3)
12322
12323#define TOK_LSHIFT tok_decl(12,0)
12324#define TOK_RSHIFT tok_decl(12,1)
12325
12326#define TOK_ADD tok_decl(13,0)
12327#define TOK_SUB tok_decl(13,1)
12328
12329#define TOK_MUL tok_decl(14,0)
12330#define TOK_DIV tok_decl(14,1)
12331#define TOK_REM tok_decl(14,2)
12332
12333/* exponent is right associativity */
12334#define TOK_EXPONENT tok_decl(15,1)
12335
12336/* For now unary operators. */
12337#define UNARYPREC 16
12338#define TOK_BNOT tok_decl(UNARYPREC,0)
12339#define TOK_NOT tok_decl(UNARYPREC,1)
12340
12341#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12342#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12343
12344#define PREC_PRE (UNARYPREC+2)
12345
12346#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12347#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12348
12349#define PREC_POST (UNARYPREC+3)
12350
12351#define TOK_POST_INC tok_decl(PREC_POST, 0)
12352#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12353
12354#define SPEC_PREC (UNARYPREC+4)
12355
12356#define TOK_NUM tok_decl(SPEC_PREC, 0)
12357#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12358
12359#define NUMPTR (*numstackptr)
12360
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012361static int
12362tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012363{
12364 operator prec = PREC(op);
12365
12366 convert_prec_is_assing(prec);
12367 return (prec == PREC(TOK_ASSIGN) ||
12368 prec == PREC_PRE || prec == PREC_POST);
12369}
12370
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012371static int
12372is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012373{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012374 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12375 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012376}
12377
Eric Andersen90898442003-08-06 11:20:52 +000012378typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012379 arith_t val;
12380 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012381 char contidional_second_val_initialized;
12382 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012383 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012384} v_n_t;
12385
Eric Andersen90898442003-08-06 11:20:52 +000012386typedef struct CHK_VAR_RECURSIVE_LOOPED {
12387 const char *var;
12388 struct CHK_VAR_RECURSIVE_LOOPED *next;
12389} chk_var_recursive_looped_t;
12390
12391static chk_var_recursive_looped_t *prev_chk_var_recursive;
12392
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012393static int
12394arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012395{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 if (t->var) {
12397 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012398
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012399 if (p) {
12400 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012401
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 /* recursive try as expression */
12403 chk_var_recursive_looped_t *cur;
12404 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012405
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12407 if (strcmp(cur->var, t->var) == 0) {
12408 /* expression recursion loop detected */
12409 return -5;
12410 }
12411 }
12412 /* save current lookuped var name */
12413 cur = prev_chk_var_recursive;
12414 cur_save.var = t->var;
12415 cur_save.next = cur;
12416 prev_chk_var_recursive = &cur_save;
12417
12418 t->val = arith (p, &errcode);
12419 /* restore previous ptr after recursiving */
12420 prev_chk_var_recursive = cur;
12421 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012422 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012423 /* allow undefined var as 0 */
12424 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012425 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012426 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012427}
12428
12429/* "applying" a token means performing it on the top elements on the integer
12430 * stack. For a unary operator it will only change the top element, but a
12431 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012432static int
12433arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012434{
Eric Andersen90898442003-08-06 11:20:52 +000012435 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012436 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012437 int ret_arith_lookup_val;
12438
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 /* There is no operator that can work without arguments */
12440 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012441 numptr_m1 = NUMPTR - 1;
12442
12443 /* check operand is var with noninteger value */
12444 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012446 return ret_arith_lookup_val;
12447
12448 rez = numptr_m1->val;
12449 if (op == TOK_UMINUS)
12450 rez *= -1;
12451 else if (op == TOK_NOT)
12452 rez = !rez;
12453 else if (op == TOK_BNOT)
12454 rez = ~rez;
12455 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12456 rez++;
12457 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12458 rez--;
12459 else if (op != TOK_UPLUS) {
12460 /* Binary operators */
12461
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012462 /* check and binary operators need two arguments */
12463 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012464
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012465 /* ... and they pop one */
12466 --NUMPTR;
12467 numptr_val = rez;
12468 if (op == TOK_CONDITIONAL) {
12469 if (! numptr_m1->contidional_second_val_initialized) {
12470 /* protect $((expr1 ? expr2)) without ": expr" */
12471 goto err;
12472 }
12473 rez = numptr_m1->contidional_second_val;
12474 } else if (numptr_m1->contidional_second_val_initialized) {
12475 /* protect $((expr1 : expr2)) without "expr ? " */
12476 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012477 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012478 numptr_m1 = NUMPTR - 1;
12479 if (op != TOK_ASSIGN) {
12480 /* check operand is var with noninteger value for not '=' */
12481 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12482 if (ret_arith_lookup_val)
12483 return ret_arith_lookup_val;
12484 }
12485 if (op == TOK_CONDITIONAL) {
12486 numptr_m1->contidional_second_val = rez;
12487 }
12488 rez = numptr_m1->val;
12489 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012490 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012491 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012492 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012493 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012494 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012495 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012496 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012497 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012498 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012499 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012500 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012501 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012502 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012503 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012504 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012505 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012506 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012507 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012508 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012509 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012510 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012511 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012512 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012513 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012514 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012515 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012516 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012517 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012518 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012519 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012520 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012521 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012522 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012524 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 /* protect $((expr : expr)) without "expr ? " */
12526 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012527 }
12528 numptr_m1->contidional_second_val_initialized = op;
12529 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012530 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012531 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012532 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012533 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012534 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012535 return -3; /* exponent less than 0 */
12536 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012537 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012538
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012539 if (numptr_val)
12540 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012541 c *= rez;
12542 rez = c;
12543 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012544 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012545 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012546 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012547 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012548 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012549 rez %= numptr_val;
12550 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012551 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012552 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012553
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012554 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012555 /* Hmm, 1=2 ? */
12556 goto err;
12557 }
12558 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012559#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012560 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012561#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012562 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012563#endif
Eric Andersen90898442003-08-06 11:20:52 +000012564 setvar(numptr_m1->var, buf, 0);
12565 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012566 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012567 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012568 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012569 rez++;
12570 }
12571 numptr_m1->val = rez;
12572 /* protect geting var value, is number now */
12573 numptr_m1->var = NULL;
12574 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012575 err:
12576 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012577}
12578
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012579/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012580static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012581 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12582 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12583 '<','<', 0, TOK_LSHIFT,
12584 '>','>', 0, TOK_RSHIFT,
12585 '|','|', 0, TOK_OR,
12586 '&','&', 0, TOK_AND,
12587 '!','=', 0, TOK_NE,
12588 '<','=', 0, TOK_LE,
12589 '>','=', 0, TOK_GE,
12590 '=','=', 0, TOK_EQ,
12591 '|','=', 0, TOK_OR_ASSIGN,
12592 '&','=', 0, TOK_AND_ASSIGN,
12593 '*','=', 0, TOK_MUL_ASSIGN,
12594 '/','=', 0, TOK_DIV_ASSIGN,
12595 '%','=', 0, TOK_REM_ASSIGN,
12596 '+','=', 0, TOK_PLUS_ASSIGN,
12597 '-','=', 0, TOK_MINUS_ASSIGN,
12598 '-','-', 0, TOK_POST_DEC,
12599 '^','=', 0, TOK_XOR_ASSIGN,
12600 '+','+', 0, TOK_POST_INC,
12601 '*','*', 0, TOK_EXPONENT,
12602 '!', 0, TOK_NOT,
12603 '<', 0, TOK_LT,
12604 '>', 0, TOK_GT,
12605 '=', 0, TOK_ASSIGN,
12606 '|', 0, TOK_BOR,
12607 '&', 0, TOK_BAND,
12608 '*', 0, TOK_MUL,
12609 '/', 0, TOK_DIV,
12610 '%', 0, TOK_REM,
12611 '+', 0, TOK_ADD,
12612 '-', 0, TOK_SUB,
12613 '^', 0, TOK_BXOR,
12614 /* uniq */
12615 '~', 0, TOK_BNOT,
12616 ',', 0, TOK_COMMA,
12617 '?', 0, TOK_CONDITIONAL,
12618 ':', 0, TOK_CONDITIONAL_SEP,
12619 ')', 0, TOK_RPAREN,
12620 '(', 0, TOK_LPAREN,
12621 0
12622};
12623/* ptr to ")" */
12624#define endexpression &op_tokens[sizeof(op_tokens)-7]
12625
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012626static arith_t
12627arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012628{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012629 char arithval; /* Current character under analysis */
12630 operator lasttok, op;
12631 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012632
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012633 const char *p = endexpression;
12634 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012635
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012636 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012637
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012638 /* Stack of integers */
12639 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12640 * in any given correct or incorrect expression is left as an exercise to
12641 * the reader. */
12642 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12643 *numstackptr = numstack;
12644 /* Stack of operator tokens */
12645 operator *stack = alloca((datasizes) * sizeof(operator)),
12646 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012647
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012648 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12649 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012650
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012651 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012652 arithval = *expr;
12653 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012654 if (p == endexpression) {
12655 /* Null expression. */
12656 return 0;
12657 }
12658
12659 /* This is only reached after all tokens have been extracted from the
12660 * input stream. If there are still tokens on the operator stack, they
12661 * are to be applied in order. At the end, there should be a final
12662 * result on the integer stack */
12663
12664 if (expr != endexpression + 1) {
12665 /* If we haven't done so already, */
12666 /* append a closing right paren */
12667 expr = endexpression;
12668 /* and let the loop process it. */
12669 continue;
12670 }
12671 /* At this point, we're done with the expression. */
12672 if (numstackptr != numstack+1) {
12673 /* ... but if there isn't, it's bad */
12674 err:
12675 return (*perrcode = -1);
12676 }
12677 if (numstack->var) {
12678 /* expression is $((var)) only, lookup now */
12679 errcode = arith_lookup_val(numstack);
12680 }
12681 ret:
12682 *perrcode = errcode;
12683 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012684 }
12685
Eric Andersen90898442003-08-06 11:20:52 +000012686 /* Continue processing the expression. */
12687 if (arith_isspace(arithval)) {
12688 /* Skip whitespace */
12689 goto prologue;
12690 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012691 p = endofname(expr);
12692 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012693 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012694
12695 numstackptr->var = alloca(var_name_size);
12696 safe_strncpy(numstackptr->var, expr, var_name_size);
12697 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012698 num:
Eric Andersen90898442003-08-06 11:20:52 +000012699 numstackptr->contidional_second_val_initialized = 0;
12700 numstackptr++;
12701 lasttok = TOK_NUM;
12702 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012703 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012704 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012705 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012706#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012707 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012708#else
12709 numstackptr->val = strtol(expr, (char **) &expr, 0);
12710#endif
Eric Andersen90898442003-08-06 11:20:52 +000012711 goto num;
12712 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012713 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012714 const char *o;
12715
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012716 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012717 /* strange operator not found */
12718 goto err;
12719 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012720 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012721 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012722 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012723 /* found */
12724 expr = o - 1;
12725 break;
12726 }
12727 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012728 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012729 p++;
12730 /* skip zero delim */
12731 p++;
12732 }
12733 op = p[1];
12734
12735 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012736 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12737 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012738
12739 /* Plus and minus are binary (not unary) _only_ if the last
12740 * token was as number, or a right paren (which pretends to be
12741 * a number, since it evaluates to one). Think about it.
12742 * It makes sense. */
12743 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012744 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012745 case TOK_ADD:
12746 op = TOK_UPLUS;
12747 break;
12748 case TOK_SUB:
12749 op = TOK_UMINUS;
12750 break;
12751 case TOK_POST_INC:
12752 op = TOK_PRE_INC;
12753 break;
12754 case TOK_POST_DEC:
12755 op = TOK_PRE_DEC;
12756 break;
Eric Andersen90898442003-08-06 11:20:52 +000012757 }
12758 }
12759 /* We don't want a unary operator to cause recursive descent on the
12760 * stack, because there can be many in a row and it could cause an
12761 * operator to be evaluated before its argument is pushed onto the
12762 * integer stack. */
12763 /* But for binary operators, "apply" everything on the operator
12764 * stack until we find an operator with a lesser priority than the
12765 * one we have just extracted. */
12766 /* Left paren is given the lowest priority so it will never be
12767 * "applied" in this way.
12768 * if associativity is right and priority eq, applied also skip
12769 */
12770 prec = PREC(op);
12771 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12772 /* not left paren or unary */
12773 if (lasttok != TOK_NUM) {
12774 /* binary op must be preceded by a num */
12775 goto err;
12776 }
12777 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012778 if (op == TOK_RPAREN) {
12779 /* The algorithm employed here is simple: while we don't
12780 * hit an open paren nor the bottom of the stack, pop
12781 * tokens and apply them */
12782 if (stackptr[-1] == TOK_LPAREN) {
12783 --stackptr;
12784 /* Any operator directly after a */
12785 lasttok = TOK_NUM;
12786 /* close paren should consider itself binary */
12787 goto prologue;
12788 }
12789 } else {
12790 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012791
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012792 convert_prec_is_assing(prec);
12793 convert_prec_is_assing(prev_prec);
12794 if (prev_prec < prec)
12795 break;
12796 /* check right assoc */
12797 if (prev_prec == prec && is_right_associativity(prec))
12798 break;
12799 }
12800 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12801 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012802 }
12803 if (op == TOK_RPAREN) {
12804 goto err;
12805 }
12806 }
12807
12808 /* Push this operator to the stack and remember it. */
12809 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012810 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012811 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012812 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012813}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012814#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012815
12816
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012817/* ============ main() and helpers */
12818
12819/*
12820 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012821 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012822static void exitshell(void) ATTRIBUTE_NORETURN;
12823static void
12824exitshell(void)
12825{
12826 struct jmploc loc;
12827 char *p;
12828 int status;
12829
12830 status = exitstatus;
12831 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12832 if (setjmp(loc.loc)) {
12833 if (exception == EXEXIT)
12834/* dash bug: it just does _exit(exitstatus) here
12835 * but we have to do setjobctl(0) first!
12836 * (bug is still not fixed in dash-0.5.3 - if you run dash
12837 * under Midnight Commander, on exit from dash MC is backgrounded) */
12838 status = exitstatus;
12839 goto out;
12840 }
12841 exception_handler = &loc;
12842 p = trap[0];
12843 if (p) {
12844 trap[0] = NULL;
12845 evalstring(p, 0);
12846 }
12847 flush_stdout_stderr();
12848 out:
12849 setjobctl(0);
12850 _exit(status);
12851 /* NOTREACHED */
12852}
12853
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012854static void
12855init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012856{
12857 /* from input.c: */
12858 basepf.nextc = basepf.buf = basebuf;
12859
12860 /* from trap.c: */
12861 signal(SIGCHLD, SIG_DFL);
12862
12863 /* from var.c: */
12864 {
12865 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012866 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012867 const char *p;
12868 struct stat st1, st2;
12869
12870 initvar();
12871 for (envp = environ; envp && *envp; envp++) {
12872 if (strchr(*envp, '=')) {
12873 setvareq(*envp, VEXPORT|VTEXTFIXED);
12874 }
12875 }
12876
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012877 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012878 setvar("PPID", ppid, 0);
12879
12880 p = lookupvar("PWD");
12881 if (p)
12882 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12883 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12884 p = '\0';
12885 setpwd(p, 0);
12886 }
12887}
12888
12889/*
12890 * Process the shell command line arguments.
12891 */
12892static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012893procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012894{
12895 int i;
12896 const char *xminusc;
12897 char **xargv;
12898
12899 xargv = argv;
12900 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012901 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012902 xargv++;
12903 for (i = 0; i < NOPTS; i++)
12904 optlist[i] = 2;
12905 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012906 if (options(1)) {
12907 /* it already printed err message */
12908 raise_exception(EXERROR);
12909 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012910 xargv = argptr;
12911 xminusc = minusc;
12912 if (*xargv == NULL) {
12913 if (xminusc)
12914 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12915 sflag = 1;
12916 }
12917 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12918 iflag = 1;
12919 if (mflag == 2)
12920 mflag = iflag;
12921 for (i = 0; i < NOPTS; i++)
12922 if (optlist[i] == 2)
12923 optlist[i] = 0;
12924#if DEBUG == 2
12925 debug = 1;
12926#endif
12927 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12928 if (xminusc) {
12929 minusc = *xargv++;
12930 if (*xargv)
12931 goto setarg0;
12932 } else if (!sflag) {
12933 setinputfile(*xargv, 0);
12934 setarg0:
12935 arg0 = *xargv++;
12936 commandname = arg0;
12937 }
12938
12939 shellparam.p = xargv;
12940#if ENABLE_ASH_GETOPTS
12941 shellparam.optind = 1;
12942 shellparam.optoff = -1;
12943#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012944 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012945 while (*xargv) {
12946 shellparam.nparam++;
12947 xargv++;
12948 }
12949 optschanged();
12950}
12951
12952/*
12953 * Read /etc/profile or .profile.
12954 */
12955static void
12956read_profile(const char *name)
12957{
12958 int skip;
12959
12960 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12961 return;
12962 skip = cmdloop(0);
12963 popfile();
12964 if (skip)
12965 exitshell();
12966}
12967
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012968/*
12969 * This routine is called when an error or an interrupt occurs in an
12970 * interactive shell and control is returned to the main command loop.
12971 */
12972static void
12973reset(void)
12974{
12975 /* from eval.c: */
12976 evalskip = 0;
12977 loopnest = 0;
12978 /* from input.c: */
12979 parselleft = parsenleft = 0; /* clear input buffer */
12980 popallfiles();
12981 /* from parser.c: */
12982 tokpushback = 0;
12983 checkkwd = 0;
12984 /* from redir.c: */
12985 clearredir(0);
12986}
12987
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012988#if PROFILE
12989static short profile_buf[16384];
12990extern int etext();
12991#endif
12992
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012993/*
12994 * Main routine. We initialize things, parse the arguments, execute
12995 * profiles if we're a login shell, and then call cmdloop to execute
12996 * commands. The setjmp call sets up the location to jump to when an
12997 * exception occurs. When an exception occurs the variable "state"
12998 * is used to figure out how far we had gotten.
12999 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013000int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013001int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013002{
13003 char *shinit;
13004 volatile int state;
13005 struct jmploc jmploc;
13006 struct stackmark smark;
13007
Denis Vlasenko01631112007-12-16 17:20:38 +000013008 /* Initialize global data */
13009 INIT_G_misc();
13010 INIT_G_memstack();
13011 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013012#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013013 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013014#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013015 INIT_G_cmdtable();
13016
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013017#if PROFILE
13018 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13019#endif
13020
13021#if ENABLE_FEATURE_EDITING
13022 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13023#endif
13024 state = 0;
13025 if (setjmp(jmploc.loc)) {
13026 int e;
13027 int s;
13028
13029 reset();
13030
13031 e = exception;
13032 if (e == EXERROR)
13033 exitstatus = 2;
13034 s = state;
13035 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13036 exitshell();
13037
13038 if (e == EXINT) {
13039 outcslow('\n', stderr);
13040 }
13041 popstackmark(&smark);
13042 FORCE_INT_ON; /* enable interrupts */
13043 if (s == 1)
13044 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013045 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013046 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013047 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013048 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013049 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013050 }
13051 exception_handler = &jmploc;
13052#if DEBUG
13053 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013054 trace_puts("Shell args: ");
13055 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013056#endif
13057 rootpid = getpid();
13058
13059#if ENABLE_ASH_RANDOM_SUPPORT
13060 rseed = rootpid + time(NULL);
13061#endif
13062 init();
13063 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013064 procargs(argv);
13065
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013066#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13067 if (iflag) {
13068 const char *hp = lookupvar("HISTFILE");
13069
13070 if (hp == NULL) {
13071 hp = lookupvar("HOME");
13072 if (hp != NULL) {
13073 char *defhp = concat_path_file(hp, ".ash_history");
13074 setvar("HISTFILE", defhp, 0);
13075 free(defhp);
13076 }
13077 }
13078 }
13079#endif
13080 if (argv[0] && argv[0][0] == '-')
13081 isloginsh = 1;
13082 if (isloginsh) {
13083 state = 1;
13084 read_profile("/etc/profile");
13085 state1:
13086 state = 2;
13087 read_profile(".profile");
13088 }
13089 state2:
13090 state = 3;
13091 if (
13092#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013093 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013094#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013095 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013096 ) {
13097 shinit = lookupvar("ENV");
13098 if (shinit != NULL && *shinit != '\0') {
13099 read_profile(shinit);
13100 }
13101 }
13102 state3:
13103 state = 4;
13104 if (minusc)
13105 evalstring(minusc, 0);
13106
13107 if (sflag || minusc == NULL) {
13108#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13109 if ( iflag ) {
13110 const char *hp = lookupvar("HISTFILE");
13111
13112 if (hp != NULL)
13113 line_input_state->hist_file = hp;
13114 }
13115#endif
13116 state4: /* XXX ??? - why isn't this before the "if" statement */
13117 cmdloop(1);
13118 }
13119#if PROFILE
13120 monitor(0);
13121#endif
13122#ifdef GPROF
13123 {
13124 extern void _mcleanup(void);
13125 _mcleanup();
13126 }
13127#endif
13128 exitshell();
13129 /* NOTREACHED */
13130}
13131
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013132#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013133const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013134int main(int argc, char **argv)
13135{
13136 return ash_main(argc, argv);
13137}
13138#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013139
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013140
Eric Andersendf82f612001-06-28 07:46:40 +000013141/*-
13142 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013143 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013144 *
13145 * This code is derived from software contributed to Berkeley by
13146 * Kenneth Almquist.
13147 *
13148 * Redistribution and use in source and binary forms, with or without
13149 * modification, are permitted provided that the following conditions
13150 * are met:
13151 * 1. Redistributions of source code must retain the above copyright
13152 * notice, this list of conditions and the following disclaimer.
13153 * 2. Redistributions in binary form must reproduce the above copyright
13154 * notice, this list of conditions and the following disclaimer in the
13155 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013156 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013157 * may be used to endorse or promote products derived from this software
13158 * without specific prior written permission.
13159 *
13160 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13161 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13162 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13163 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13164 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13165 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13166 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13167 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13168 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13169 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13170 * SUCH DAMAGE.
13171 */