blob: 62380b3ab6a31b0b6ccf95346d6e4d3997bc0150 [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 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000469#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#if ENABLE_ASH_BASH_COMPAT
480#define VSSUBSTR 0xc /* ${var:position:length} */
481#define VSREPLACE 0xd /* ${var/pattern/replacement} */
482#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
483#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000484
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000485static const char dolatstr[] ALIGN1 = {
486 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
487};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000488
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000489#define NCMD 0
490#define NPIPE 1
491#define NREDIR 2
492#define NBACKGND 3
493#define NSUBSHELL 4
494#define NAND 5
495#define NOR 6
496#define NSEMI 7
497#define NIF 8
498#define NWHILE 9
499#define NUNTIL 10
500#define NFOR 11
501#define NCASE 12
502#define NCLIST 13
503#define NDEFUN 14
504#define NARG 15
505#define NTO 16
506#define NCLOBBER 17
507#define NFROM 18
508#define NFROMTO 19
509#define NAPPEND 20
510#define NTOFD 21
511#define NFROMFD 22
512#define NHERE 23
513#define NXHERE 24
514#define NNOT 25
515
516union node;
517
518struct ncmd {
519 int type;
520 union node *assign;
521 union node *args;
522 union node *redirect;
523};
524
525struct npipe {
526 int type;
527 int backgnd;
528 struct nodelist *cmdlist;
529};
530
531struct nredir {
532 int type;
533 union node *n;
534 union node *redirect;
535};
536
537struct nbinary {
538 int type;
539 union node *ch1;
540 union node *ch2;
541};
542
543struct nif {
544 int type;
545 union node *test;
546 union node *ifpart;
547 union node *elsepart;
548};
549
550struct nfor {
551 int type;
552 union node *args;
553 union node *body;
554 char *var;
555};
556
557struct ncase {
558 int type;
559 union node *expr;
560 union node *cases;
561};
562
563struct nclist {
564 int type;
565 union node *next;
566 union node *pattern;
567 union node *body;
568};
569
570struct narg {
571 int type;
572 union node *next;
573 char *text;
574 struct nodelist *backquote;
575};
576
577struct nfile {
578 int type;
579 union node *next;
580 int fd;
581 union node *fname;
582 char *expfname;
583};
584
585struct ndup {
586 int type;
587 union node *next;
588 int fd;
589 int dupfd;
590 union node *vname;
591};
592
593struct nhere {
594 int type;
595 union node *next;
596 int fd;
597 union node *doc;
598};
599
600struct nnot {
601 int type;
602 union node *com;
603};
604
605union node {
606 int type;
607 struct ncmd ncmd;
608 struct npipe npipe;
609 struct nredir nredir;
610 struct nbinary nbinary;
611 struct nif nif;
612 struct nfor nfor;
613 struct ncase ncase;
614 struct nclist nclist;
615 struct narg narg;
616 struct nfile nfile;
617 struct ndup ndup;
618 struct nhere nhere;
619 struct nnot nnot;
620};
621
622struct nodelist {
623 struct nodelist *next;
624 union node *n;
625};
626
627struct funcnode {
628 int count;
629 union node n;
630};
631
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000632/*
633 * Free a parse tree.
634 */
635static void
636freefunc(struct funcnode *f)
637{
638 if (f && --f->count < 0)
639 free(f);
640}
641
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000642
643/* ============ Debugging output */
644
645#if DEBUG
646
647static FILE *tracefile;
648
649static void
650trace_printf(const char *fmt, ...)
651{
652 va_list va;
653
654 if (debug != 1)
655 return;
656 va_start(va, fmt);
657 vfprintf(tracefile, fmt, va);
658 va_end(va);
659}
660
661static void
662trace_vprintf(const char *fmt, va_list va)
663{
664 if (debug != 1)
665 return;
666 vfprintf(tracefile, fmt, va);
667}
668
669static void
670trace_puts(const char *s)
671{
672 if (debug != 1)
673 return;
674 fputs(s, tracefile);
675}
676
677static void
678trace_puts_quoted(char *s)
679{
680 char *p;
681 char c;
682
683 if (debug != 1)
684 return;
685 putc('"', tracefile);
686 for (p = s; *p; p++) {
687 switch (*p) {
688 case '\n': c = 'n'; goto backslash;
689 case '\t': c = 't'; goto backslash;
690 case '\r': c = 'r'; goto backslash;
691 case '"': c = '"'; goto backslash;
692 case '\\': c = '\\'; goto backslash;
693 case CTLESC: c = 'e'; goto backslash;
694 case CTLVAR: c = 'v'; goto backslash;
695 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
696 case CTLBACKQ: c = 'q'; goto backslash;
697 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
698 backslash:
699 putc('\\', tracefile);
700 putc(c, tracefile);
701 break;
702 default:
703 if (*p >= ' ' && *p <= '~')
704 putc(*p, tracefile);
705 else {
706 putc('\\', tracefile);
707 putc(*p >> 6 & 03, tracefile);
708 putc(*p >> 3 & 07, tracefile);
709 putc(*p & 07, tracefile);
710 }
711 break;
712 }
713 }
714 putc('"', tracefile);
715}
716
717static void
718trace_puts_args(char **ap)
719{
720 if (debug != 1)
721 return;
722 if (!*ap)
723 return;
724 while (1) {
725 trace_puts_quoted(*ap);
726 if (!*++ap) {
727 putc('\n', tracefile);
728 break;
729 }
730 putc(' ', tracefile);
731 }
732}
733
734static void
735opentrace(void)
736{
737 char s[100];
738#ifdef O_APPEND
739 int flags;
740#endif
741
742 if (debug != 1) {
743 if (tracefile)
744 fflush(tracefile);
745 /* leave open because libedit might be using it */
746 return;
747 }
748 strcpy(s, "./trace");
749 if (tracefile) {
750 if (!freopen(s, "a", tracefile)) {
751 fprintf(stderr, "Can't re-open %s\n", s);
752 debug = 0;
753 return;
754 }
755 } else {
756 tracefile = fopen(s, "a");
757 if (tracefile == NULL) {
758 fprintf(stderr, "Can't open %s\n", s);
759 debug = 0;
760 return;
761 }
762 }
763#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000764 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000765 if (flags >= 0)
766 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
767#endif
768 setlinebuf(tracefile);
769 fputs("\nTracing started.\n", tracefile);
770}
771
772static void
773indent(int amount, char *pfx, FILE *fp)
774{
775 int i;
776
777 for (i = 0; i < amount; i++) {
778 if (pfx && i == amount - 1)
779 fputs(pfx, fp);
780 putc('\t', fp);
781 }
782}
783
784/* little circular references here... */
785static void shtree(union node *n, int ind, char *pfx, FILE *fp);
786
787static void
788sharg(union node *arg, FILE *fp)
789{
790 char *p;
791 struct nodelist *bqlist;
792 int subtype;
793
794 if (arg->type != NARG) {
795 out1fmt("<node type %d>\n", arg->type);
796 abort();
797 }
798 bqlist = arg->narg.backquote;
799 for (p = arg->narg.text; *p; p++) {
800 switch (*p) {
801 case CTLESC:
802 putc(*++p, fp);
803 break;
804 case CTLVAR:
805 putc('$', fp);
806 putc('{', fp);
807 subtype = *++p;
808 if (subtype == VSLENGTH)
809 putc('#', fp);
810
811 while (*p != '=')
812 putc(*p++, fp);
813
814 if (subtype & VSNUL)
815 putc(':', fp);
816
817 switch (subtype & VSTYPE) {
818 case VSNORMAL:
819 putc('}', fp);
820 break;
821 case VSMINUS:
822 putc('-', fp);
823 break;
824 case VSPLUS:
825 putc('+', fp);
826 break;
827 case VSQUESTION:
828 putc('?', fp);
829 break;
830 case VSASSIGN:
831 putc('=', fp);
832 break;
833 case VSTRIMLEFT:
834 putc('#', fp);
835 break;
836 case VSTRIMLEFTMAX:
837 putc('#', fp);
838 putc('#', fp);
839 break;
840 case VSTRIMRIGHT:
841 putc('%', fp);
842 break;
843 case VSTRIMRIGHTMAX:
844 putc('%', fp);
845 putc('%', fp);
846 break;
847 case VSLENGTH:
848 break;
849 default:
850 out1fmt("<subtype %d>", subtype);
851 }
852 break;
853 case CTLENDVAR:
854 putc('}', fp);
855 break;
856 case CTLBACKQ:
857 case CTLBACKQ|CTLQUOTE:
858 putc('$', fp);
859 putc('(', fp);
860 shtree(bqlist->n, -1, NULL, fp);
861 putc(')', fp);
862 break;
863 default:
864 putc(*p, fp);
865 break;
866 }
867 }
868}
869
870static void
871shcmd(union node *cmd, FILE *fp)
872{
873 union node *np;
874 int first;
875 const char *s;
876 int dftfd;
877
878 first = 1;
879 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000880 if (!first)
881 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000882 sharg(np, fp);
883 first = 0;
884 }
885 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000886 if (!first)
887 putc(' ', fp);
888 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000889 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000890 case NTO: s = ">>"+1; dftfd = 1; break;
891 case NCLOBBER: s = ">|"; dftfd = 1; break;
892 case NAPPEND: s = ">>"; dftfd = 1; break;
893 case NTOFD: s = ">&"; dftfd = 1; break;
894 case NFROM: s = "<"; break;
895 case NFROMFD: s = "<&"; break;
896 case NFROMTO: s = "<>"; break;
897 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000898 }
899 if (np->nfile.fd != dftfd)
900 fprintf(fp, "%d", np->nfile.fd);
901 fputs(s, fp);
902 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
903 fprintf(fp, "%d", np->ndup.dupfd);
904 } else {
905 sharg(np->nfile.fname, fp);
906 }
907 first = 0;
908 }
909}
910
911static void
912shtree(union node *n, int ind, char *pfx, FILE *fp)
913{
914 struct nodelist *lp;
915 const char *s;
916
917 if (n == NULL)
918 return;
919
920 indent(ind, pfx, fp);
921 switch (n->type) {
922 case NSEMI:
923 s = "; ";
924 goto binop;
925 case NAND:
926 s = " && ";
927 goto binop;
928 case NOR:
929 s = " || ";
930 binop:
931 shtree(n->nbinary.ch1, ind, NULL, fp);
932 /* if (ind < 0) */
933 fputs(s, fp);
934 shtree(n->nbinary.ch2, ind, NULL, fp);
935 break;
936 case NCMD:
937 shcmd(n, fp);
938 if (ind >= 0)
939 putc('\n', fp);
940 break;
941 case NPIPE:
942 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
943 shcmd(lp->n, fp);
944 if (lp->next)
945 fputs(" | ", fp);
946 }
947 if (n->npipe.backgnd)
948 fputs(" &", fp);
949 if (ind >= 0)
950 putc('\n', fp);
951 break;
952 default:
953 fprintf(fp, "<node type %d>", n->type);
954 if (ind >= 0)
955 putc('\n', fp);
956 break;
957 }
958}
959
960static void
961showtree(union node *n)
962{
963 trace_puts("showtree called\n");
964 shtree(n, 1, NULL, stdout);
965}
966
967#define TRACE(param) trace_printf param
968#define TRACEV(param) trace_vprintf param
969
970#else
971
972#define TRACE(param)
973#define TRACEV(param)
974
975#endif /* DEBUG */
976
977
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000978/* ============ Parser data */
979
980/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000981 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
982 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000983struct strlist {
984 struct strlist *next;
985 char *text;
986};
987
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000988#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000989struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000990#endif
991
Denis Vlasenkob012b102007-02-19 22:43:01 +0000992struct strpush {
993 struct strpush *prev; /* preceding string on stack */
994 char *prevstring;
995 int prevnleft;
996#if ENABLE_ASH_ALIAS
997 struct alias *ap; /* if push was associated with an alias */
998#endif
999 char *string; /* remember the string since it may change */
1000};
1001
1002struct parsefile {
1003 struct parsefile *prev; /* preceding file on stack */
1004 int linno; /* current line */
1005 int fd; /* file descriptor (or -1 if string) */
1006 int nleft; /* number of chars left in this line */
1007 int lleft; /* number of chars left in this buffer */
1008 char *nextc; /* next char in buffer */
1009 char *buf; /* input buffer */
1010 struct strpush *strpush; /* for pushing strings at this level */
1011 struct strpush basestrpush; /* so pushing one is fast */
1012};
1013
1014static struct parsefile basepf; /* top level input file */
1015static struct parsefile *parsefile = &basepf; /* current input file */
1016static int startlinno; /* line # where last token started */
1017static char *commandname; /* currently executing command */
1018static struct strlist *cmdenviron; /* environment for builtin command */
1019static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001020
1021
1022/* ============ Message printing */
1023
1024static void
1025ash_vmsg(const char *msg, va_list ap)
1026{
1027 fprintf(stderr, "%s: ", arg0);
1028 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001029 if (strcmp(arg0, commandname))
1030 fprintf(stderr, "%s: ", commandname);
1031 if (!iflag || parsefile->fd)
1032 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001033 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034 vfprintf(stderr, msg, ap);
1035 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001036}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001037
1038/*
1039 * Exverror is called to raise the error exception. If the second argument
1040 * is not NULL then error prints an error message using printf style
1041 * formatting. It then raises the error exception.
1042 */
1043static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1044static void
1045ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001046{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001047#if DEBUG
1048 if (msg) {
1049 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1050 TRACEV((msg, ap));
1051 TRACE(("\") pid=%d\n", getpid()));
1052 } else
1053 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1054 if (msg)
1055#endif
1056 ash_vmsg(msg, ap);
1057
1058 flush_stdout_stderr();
1059 raise_exception(cond);
1060 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001061}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001062
1063static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1064static void
1065ash_msg_and_raise_error(const char *msg, ...)
1066{
1067 va_list ap;
1068
1069 va_start(ap, msg);
1070 ash_vmsg_and_raise(EXERROR, msg, ap);
1071 /* NOTREACHED */
1072 va_end(ap);
1073}
1074
1075static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1076static void
1077ash_msg_and_raise(int cond, const char *msg, ...)
1078{
1079 va_list ap;
1080
1081 va_start(ap, msg);
1082 ash_vmsg_and_raise(cond, msg, ap);
1083 /* NOTREACHED */
1084 va_end(ap);
1085}
1086
1087/*
1088 * error/warning routines for external builtins
1089 */
1090static void
1091ash_msg(const char *fmt, ...)
1092{
1093 va_list ap;
1094
1095 va_start(ap, fmt);
1096 ash_vmsg(fmt, ap);
1097 va_end(ap);
1098}
1099
1100/*
1101 * Return a string describing an error. The returned string may be a
1102 * pointer to a static buffer that will be overwritten on the next call.
1103 * Action describes the operation that got the error.
1104 */
1105static const char *
1106errmsg(int e, const char *em)
1107{
1108 if (e == ENOENT || e == ENOTDIR) {
1109 return em;
1110 }
1111 return strerror(e);
1112}
1113
1114
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001115/* ============ Memory allocation */
1116
1117/*
1118 * It appears that grabstackstr() will barf with such alignments
1119 * because stalloc() will return a string allocated in a new stackblock.
1120 */
1121#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1122enum {
1123 /* Most machines require the value returned from malloc to be aligned
1124 * in some way. The following macro will get this right
1125 * on many machines. */
1126 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1127 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001128 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001129};
1130
1131struct stack_block {
1132 struct stack_block *prev;
1133 char space[MINSIZE];
1134};
1135
1136struct stackmark {
1137 struct stack_block *stackp;
1138 char *stacknxt;
1139 size_t stacknleft;
1140 struct stackmark *marknext;
1141};
1142
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001143
Denis Vlasenko01631112007-12-16 17:20:38 +00001144struct globals_memstack {
1145 struct stack_block *g_stackp; // = &stackbase;
1146 struct stackmark *markp;
1147 char *g_stacknxt; // = stackbase.space;
1148 char *sstrend; // = stackbase.space + MINSIZE;
1149 size_t g_stacknleft; // = MINSIZE;
1150 int herefd; // = -1;
1151 struct stack_block stackbase;
1152};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001153extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1154#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001155#define g_stackp (G_memstack.g_stackp )
1156#define markp (G_memstack.markp )
1157#define g_stacknxt (G_memstack.g_stacknxt )
1158#define sstrend (G_memstack.sstrend )
1159#define g_stacknleft (G_memstack.g_stacknleft)
1160#define herefd (G_memstack.herefd )
1161#define stackbase (G_memstack.stackbase )
1162#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001163 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1164 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001165 g_stackp = &stackbase; \
1166 g_stacknxt = stackbase.space; \
1167 g_stacknleft = MINSIZE; \
1168 sstrend = stackbase.space + MINSIZE; \
1169 herefd = -1; \
1170} while (0)
1171
1172#define stackblock() ((void *)g_stacknxt)
1173#define stackblocksize() g_stacknleft
1174
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001175
1176static void *
1177ckrealloc(void * p, size_t nbytes)
1178{
1179 p = realloc(p, nbytes);
1180 if (!p)
1181 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1182 return p;
1183}
1184
1185static void *
1186ckmalloc(size_t nbytes)
1187{
1188 return ckrealloc(NULL, nbytes);
1189}
1190
Denis Vlasenko597906c2008-02-20 16:38:54 +00001191static void *
1192ckzalloc(size_t nbytes)
1193{
1194 return memset(ckmalloc(nbytes), 0, nbytes);
1195}
1196
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001197/*
1198 * Make a copy of a string in safe storage.
1199 */
1200static char *
1201ckstrdup(const char *s)
1202{
1203 char *p = strdup(s);
1204 if (!p)
1205 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1206 return p;
1207}
1208
1209/*
1210 * Parse trees for commands are allocated in lifo order, so we use a stack
1211 * to make this more efficient, and also to avoid all sorts of exception
1212 * handling code to handle interrupts in the middle of a parse.
1213 *
1214 * The size 504 was chosen because the Ultrix malloc handles that size
1215 * well.
1216 */
1217static void *
1218stalloc(size_t nbytes)
1219{
1220 char *p;
1221 size_t aligned;
1222
1223 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001224 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001225 size_t len;
1226 size_t blocksize;
1227 struct stack_block *sp;
1228
1229 blocksize = aligned;
1230 if (blocksize < MINSIZE)
1231 blocksize = MINSIZE;
1232 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1233 if (len < blocksize)
1234 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1235 INT_OFF;
1236 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001237 sp->prev = g_stackp;
1238 g_stacknxt = sp->space;
1239 g_stacknleft = blocksize;
1240 sstrend = g_stacknxt + blocksize;
1241 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001242 INT_ON;
1243 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001244 p = g_stacknxt;
1245 g_stacknxt += aligned;
1246 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001247 return p;
1248}
1249
Denis Vlasenko597906c2008-02-20 16:38:54 +00001250static void *
1251stzalloc(size_t nbytes)
1252{
1253 return memset(stalloc(nbytes), 0, nbytes);
1254}
1255
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256static void
1257stunalloc(void *p)
1258{
1259#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001260 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001261 write(2, "stunalloc\n", 10);
1262 abort();
1263 }
1264#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001265 g_stacknleft += g_stacknxt - (char *)p;
1266 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001267}
1268
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001269/*
1270 * Like strdup but works with the ash stack.
1271 */
1272static char *
1273ststrdup(const char *p)
1274{
1275 size_t len = strlen(p) + 1;
1276 return memcpy(stalloc(len), p, len);
1277}
1278
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001279static void
1280setstackmark(struct stackmark *mark)
1281{
Denis Vlasenko01631112007-12-16 17:20:38 +00001282 mark->stackp = g_stackp;
1283 mark->stacknxt = g_stacknxt;
1284 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001285 mark->marknext = markp;
1286 markp = mark;
1287}
1288
1289static void
1290popstackmark(struct stackmark *mark)
1291{
1292 struct stack_block *sp;
1293
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001294 if (!mark->stackp)
1295 return;
1296
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297 INT_OFF;
1298 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001299 while (g_stackp != mark->stackp) {
1300 sp = g_stackp;
1301 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001302 free(sp);
1303 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001304 g_stacknxt = mark->stacknxt;
1305 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001306 sstrend = mark->stacknxt + mark->stacknleft;
1307 INT_ON;
1308}
1309
1310/*
1311 * When the parser reads in a string, it wants to stick the string on the
1312 * stack and only adjust the stack pointer when it knows how big the
1313 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1314 * of space on top of the stack and stackblocklen returns the length of
1315 * this block. Growstackblock will grow this space by at least one byte,
1316 * possibly moving it (like realloc). Grabstackblock actually allocates the
1317 * part of the block that has been used.
1318 */
1319static void
1320growstackblock(void)
1321{
1322 size_t newlen;
1323
Denis Vlasenko01631112007-12-16 17:20:38 +00001324 newlen = g_stacknleft * 2;
1325 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1327 if (newlen < 128)
1328 newlen += 128;
1329
Denis Vlasenko01631112007-12-16 17:20:38 +00001330 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 struct stack_block *oldstackp;
1332 struct stackmark *xmark;
1333 struct stack_block *sp;
1334 struct stack_block *prevstackp;
1335 size_t grosslen;
1336
1337 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001338 oldstackp = g_stackp;
1339 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001340 prevstackp = sp->prev;
1341 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1342 sp = ckrealloc(sp, grosslen);
1343 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001344 g_stackp = sp;
1345 g_stacknxt = sp->space;
1346 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347 sstrend = sp->space + newlen;
1348
1349 /*
1350 * Stack marks pointing to the start of the old block
1351 * must be relocated to point to the new block
1352 */
1353 xmark = markp;
1354 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001355 xmark->stackp = g_stackp;
1356 xmark->stacknxt = g_stacknxt;
1357 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358 xmark = xmark->marknext;
1359 }
1360 INT_ON;
1361 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 char *oldspace = g_stacknxt;
1363 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001364 char *p = stalloc(newlen);
1365
1366 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001367 g_stacknxt = memcpy(p, oldspace, oldlen);
1368 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001369 }
1370}
1371
1372static void
1373grabstackblock(size_t len)
1374{
1375 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 g_stacknxt += len;
1377 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378}
1379
1380/*
1381 * The following routines are somewhat easier to use than the above.
1382 * The user declares a variable of type STACKSTR, which may be declared
1383 * to be a register. The macro STARTSTACKSTR initializes things. Then
1384 * the user uses the macro STPUTC to add characters to the string. In
1385 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1386 * grown as necessary. When the user is done, she can just leave the
1387 * string there and refer to it using stackblock(). Or she can allocate
1388 * the space for it using grabstackstr(). If it is necessary to allow
1389 * someone else to use the stack temporarily and then continue to grow
1390 * the string, the user should use grabstack to allocate the space, and
1391 * then call ungrabstr(p) to return to the previous mode of operation.
1392 *
1393 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1394 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1395 * is space for at least one character.
1396 */
1397static void *
1398growstackstr(void)
1399{
1400 size_t len = stackblocksize();
1401 if (herefd >= 0 && len >= 1024) {
1402 full_write(herefd, stackblock(), len);
1403 return stackblock();
1404 }
1405 growstackblock();
1406 return stackblock() + len;
1407}
1408
1409/*
1410 * Called from CHECKSTRSPACE.
1411 */
1412static char *
1413makestrspace(size_t newlen, char *p)
1414{
Denis Vlasenko01631112007-12-16 17:20:38 +00001415 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001416 size_t size = stackblocksize();
1417
1418 for (;;) {
1419 size_t nleft;
1420
1421 size = stackblocksize();
1422 nleft = size - len;
1423 if (nleft >= newlen)
1424 break;
1425 growstackblock();
1426 }
1427 return stackblock() + len;
1428}
1429
1430static char *
1431stack_nputstr(const char *s, size_t n, char *p)
1432{
1433 p = makestrspace(n, p);
1434 p = memcpy(p, s, n) + n;
1435 return p;
1436}
1437
1438static char *
1439stack_putstr(const char *s, char *p)
1440{
1441 return stack_nputstr(s, strlen(s), p);
1442}
1443
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001444static char *
1445_STPUTC(int c, char *p)
1446{
1447 if (p == sstrend)
1448 p = growstackstr();
1449 *p++ = c;
1450 return p;
1451}
1452
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001453#define STARTSTACKSTR(p) ((p) = stackblock())
1454#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001455#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001456 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001457 char *q = (p); \
1458 size_t l = (n); \
1459 size_t m = sstrend - q; \
1460 if (l > m) \
1461 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001462 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001463#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001464#define STACKSTRNUL(p) \
1465 do { \
1466 if ((p) == sstrend) \
1467 p = growstackstr(); \
1468 *p = '\0'; \
1469 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001470#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001471#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001472#define STADJUST(amount, p) (p += (amount))
1473
1474#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1475#define ungrabstackstr(s, p) stunalloc((s))
1476#define stackstrend() ((void *)sstrend)
1477
1478
1479/* ============ String helpers */
1480
1481/*
1482 * prefix -- see if pfx is a prefix of string.
1483 */
1484static char *
1485prefix(const char *string, const char *pfx)
1486{
1487 while (*pfx) {
1488 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001489 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001490 }
1491 return (char *) string;
1492}
1493
1494/*
1495 * Check for a valid number. This should be elsewhere.
1496 */
1497static int
1498is_number(const char *p)
1499{
1500 do {
1501 if (!isdigit(*p))
1502 return 0;
1503 } while (*++p != '\0');
1504 return 1;
1505}
1506
1507/*
1508 * Convert a string of digits to an integer, printing an error message on
1509 * failure.
1510 */
1511static int
1512number(const char *s)
1513{
1514 if (!is_number(s))
1515 ash_msg_and_raise_error(illnum, s);
1516 return atoi(s);
1517}
1518
1519/*
1520 * Produce a possibly single quoted string suitable as input to the shell.
1521 * The return string is allocated on the stack.
1522 */
1523static char *
1524single_quote(const char *s)
1525{
1526 char *p;
1527
1528 STARTSTACKSTR(p);
1529
1530 do {
1531 char *q;
1532 size_t len;
1533
1534 len = strchrnul(s, '\'') - s;
1535
1536 q = p = makestrspace(len + 3, p);
1537
1538 *q++ = '\'';
1539 q = memcpy(q, s, len) + len;
1540 *q++ = '\'';
1541 s += len;
1542
1543 STADJUST(q - p, p);
1544
1545 len = strspn(s, "'");
1546 if (!len)
1547 break;
1548
1549 q = p = makestrspace(len + 3, p);
1550
1551 *q++ = '"';
1552 q = memcpy(q, s, len) + len;
1553 *q++ = '"';
1554 s += len;
1555
1556 STADJUST(q - p, p);
1557 } while (*s);
1558
1559 USTPUTC(0, p);
1560
1561 return stackblock();
1562}
1563
1564
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001565/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001566
1567static char **argptr; /* argument list for builtin commands */
1568static char *optionarg; /* set by nextopt (like getopt) */
1569static char *optptr; /* used by nextopt */
1570
1571/*
1572 * XXX - should get rid of. have all builtins use getopt(3). the
1573 * library getopt must have the BSD extension static variable "optreset"
1574 * otherwise it can't be used within the shell safely.
1575 *
1576 * Standard option processing (a la getopt) for builtin routines. The
1577 * only argument that is passed to nextopt is the option string; the
1578 * other arguments are unnecessary. It return the character, or '\0' on
1579 * end of input.
1580 */
1581static int
1582nextopt(const char *optstring)
1583{
1584 char *p;
1585 const char *q;
1586 char c;
1587
1588 p = optptr;
1589 if (p == NULL || *p == '\0') {
1590 p = *argptr;
1591 if (p == NULL || *p != '-' || *++p == '\0')
1592 return '\0';
1593 argptr++;
1594 if (LONE_DASH(p)) /* check for "--" */
1595 return '\0';
1596 }
1597 c = *p++;
1598 for (q = optstring; *q != c; ) {
1599 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001600 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001601 if (*++q == ':')
1602 q++;
1603 }
1604 if (*++q == ':') {
1605 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001606 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001607 optionarg = p;
1608 p = NULL;
1609 }
1610 optptr = p;
1611 return c;
1612}
1613
1614
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001615/* ============ Math support definitions */
1616
1617#if ENABLE_ASH_MATH_SUPPORT_64
1618typedef int64_t arith_t;
1619#define arith_t_type long long
1620#else
1621typedef long arith_t;
1622#define arith_t_type long
1623#endif
1624
1625#if ENABLE_ASH_MATH_SUPPORT
1626static arith_t dash_arith(const char *);
1627static arith_t arith(const char *expr, int *perrcode);
1628#endif
1629
1630#if ENABLE_ASH_RANDOM_SUPPORT
1631static unsigned long rseed;
1632#ifndef DYNAMIC_VAR
1633#define DYNAMIC_VAR
1634#endif
1635#endif
1636
1637
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001638/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001639
Denis Vlasenko01631112007-12-16 17:20:38 +00001640/*
1641 * The parsefile structure pointed to by the global variable parsefile
1642 * contains information about the current file being read.
1643 */
1644struct redirtab {
1645 struct redirtab *next;
1646 int renamed[10];
1647 int nullredirs;
1648};
1649
1650struct shparam {
1651 int nparam; /* # of positional parameters (without $0) */
1652#if ENABLE_ASH_GETOPTS
1653 int optind; /* next parameter to be processed by getopts */
1654 int optoff; /* used by getopts */
1655#endif
1656 unsigned char malloced; /* if parameter list dynamically allocated */
1657 char **p; /* parameter list */
1658};
1659
1660/*
1661 * Free the list of positional parameters.
1662 */
1663static void
1664freeparam(volatile struct shparam *param)
1665{
1666 char **ap;
1667
1668 if (param->malloced) {
1669 for (ap = param->p; *ap; ap++)
1670 free(*ap);
1671 free(param->p);
1672 }
1673}
1674
1675#if ENABLE_ASH_GETOPTS
1676static void getoptsreset(const char *value);
1677#endif
1678
1679struct var {
1680 struct var *next; /* next entry in hash list */
1681 int flags; /* flags are defined above */
1682 const char *text; /* name=value */
1683 void (*func)(const char *); /* function to be called when */
1684 /* the variable gets set/unset */
1685};
1686
1687struct localvar {
1688 struct localvar *next; /* next local variable in list */
1689 struct var *vp; /* the variable that was made local */
1690 int flags; /* saved flags */
1691 const char *text; /* saved text */
1692};
1693
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001694/* flags */
1695#define VEXPORT 0x01 /* variable is exported */
1696#define VREADONLY 0x02 /* variable cannot be modified */
1697#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1698#define VTEXTFIXED 0x08 /* text is statically allocated */
1699#define VSTACK 0x10 /* text is allocated on the stack */
1700#define VUNSET 0x20 /* the variable is not set */
1701#define VNOFUNC 0x40 /* don't call the callback function */
1702#define VNOSET 0x80 /* do not set variable - just readonly test */
1703#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1704#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001705# define VDYNAMIC 0x200 /* dynamic variable */
1706#else
1707# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708#endif
1709
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001710#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001711static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001712#define defifs (defifsvar + 4)
1713#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001714static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715#endif
1716
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001717
Denis Vlasenko01631112007-12-16 17:20:38 +00001718/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001719#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001720static void
1721change_lc_all(const char *value)
1722{
1723 if (value && *value != '\0')
1724 setlocale(LC_ALL, value);
1725}
1726static void
1727change_lc_ctype(const char *value)
1728{
1729 if (value && *value != '\0')
1730 setlocale(LC_CTYPE, value);
1731}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001732#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#if ENABLE_ASH_MAIL
1734static void chkmail(void);
1735static void changemail(const char *);
1736#endif
1737static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#if ENABLE_ASH_RANDOM_SUPPORT
1739static void change_random(const char *);
1740#endif
1741
Denis Vlasenko01631112007-12-16 17:20:38 +00001742static const struct {
1743 int flags;
1744 const char *text;
1745 void (*func)(const char *);
1746} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001748 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1754 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001756 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1757 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1758 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1759 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001761 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
1766#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001767 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1768 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769#endif
1770#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
1773};
1774
Denis Vlasenko01631112007-12-16 17:20:38 +00001775
1776struct globals_var {
1777 struct shparam shellparam; /* $@ current positional parameters */
1778 struct redirtab *redirlist;
1779 int g_nullredirs;
1780 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1781 struct var *vartab[VTABSIZE];
1782 struct var varinit[ARRAY_SIZE(varinit_data)];
1783};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001784extern struct globals_var *const ash_ptr_to_globals_var;
1785#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001786#define shellparam (G_var.shellparam )
1787#define redirlist (G_var.redirlist )
1788#define g_nullredirs (G_var.g_nullredirs )
1789#define preverrout_fd (G_var.preverrout_fd)
1790#define vartab (G_var.vartab )
1791#define varinit (G_var.varinit )
1792#define INIT_G_var() do { \
1793 int i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001794 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1795 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001796 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1797 varinit[i].flags = varinit_data[i].flags; \
1798 varinit[i].text = varinit_data[i].text; \
1799 varinit[i].func = varinit_data[i].func; \
1800 } \
1801} while (0)
1802
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001803#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001804#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001805# define vmail (&vifs)[1]
1806# define vmpath (&vmail)[1]
1807# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001808#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001809# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001810#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001811#define vps1 (&vpath)[1]
1812#define vps2 (&vps1)[1]
1813#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001814#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001815# define voptind (&vps4)[1]
1816# if ENABLE_ASH_RANDOM_SUPPORT
1817# define vrandom (&voptind)[1]
1818# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001820# if ENABLE_ASH_RANDOM_SUPPORT
1821# define vrandom (&vps4)[1]
1822# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001823#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001824
1825/*
1826 * The following macros access the values of the above variables.
1827 * They have to skip over the name. They return the null string
1828 * for unset variables.
1829 */
1830#define ifsval() (vifs.text + 4)
1831#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001832#if ENABLE_ASH_MAIL
1833# define mailval() (vmail.text + 5)
1834# define mpathval() (vmpath.text + 9)
1835# define mpathset() ((vmpath.flags & VUNSET) == 0)
1836#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837#define pathval() (vpath.text + 5)
1838#define ps1val() (vps1.text + 4)
1839#define ps2val() (vps2.text + 4)
1840#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001841#if ENABLE_ASH_GETOPTS
1842# define optindval() (voptind.text + 7)
1843#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001846#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1847#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1848
Denis Vlasenko01631112007-12-16 17:20:38 +00001849#if ENABLE_ASH_GETOPTS
1850static void
1851getoptsreset(const char *value)
1852{
1853 shellparam.optind = number(value);
1854 shellparam.optoff = -1;
1855}
1856#endif
1857
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858/*
1859 * Return of a legal variable name (a letter or underscore followed by zero or
1860 * more letters, underscores, and digits).
1861 */
1862static char *
1863endofname(const char *name)
1864{
1865 char *p;
1866
1867 p = (char *) name;
1868 if (!is_name(*p))
1869 return p;
1870 while (*++p) {
1871 if (!is_in_name(*p))
1872 break;
1873 }
1874 return p;
1875}
1876
1877/*
1878 * Compares two strings up to the first = or '\0'. The first
1879 * string must be terminated by '='; the second may be terminated by
1880 * either '=' or '\0'.
1881 */
1882static int
1883varcmp(const char *p, const char *q)
1884{
1885 int c, d;
1886
1887 while ((c = *p) == (d = *q)) {
1888 if (!c || c == '=')
1889 goto out;
1890 p++;
1891 q++;
1892 }
1893 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001894 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001895 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001896 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001897 out:
1898 return c - d;
1899}
1900
1901static int
1902varequal(const char *a, const char *b)
1903{
1904 return !varcmp(a, b);
1905}
1906
1907/*
1908 * Find the appropriate entry in the hash table from the name.
1909 */
1910static struct var **
1911hashvar(const char *p)
1912{
1913 unsigned hashval;
1914
1915 hashval = ((unsigned char) *p) << 4;
1916 while (*p && *p != '=')
1917 hashval += (unsigned char) *p++;
1918 return &vartab[hashval % VTABSIZE];
1919}
1920
1921static int
1922vpcmp(const void *a, const void *b)
1923{
1924 return varcmp(*(const char **)a, *(const char **)b);
1925}
1926
1927/*
1928 * This routine initializes the builtin variables.
1929 */
1930static void
1931initvar(void)
1932{
1933 struct var *vp;
1934 struct var *end;
1935 struct var **vpp;
1936
1937 /*
1938 * PS1 depends on uid
1939 */
1940#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1941 vps1.text = "PS1=\\w \\$ ";
1942#else
1943 if (!geteuid())
1944 vps1.text = "PS1=# ";
1945#endif
1946 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001947 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001948 do {
1949 vpp = hashvar(vp->text);
1950 vp->next = *vpp;
1951 *vpp = vp;
1952 } while (++vp < end);
1953}
1954
1955static struct var **
1956findvar(struct var **vpp, const char *name)
1957{
1958 for (; *vpp; vpp = &(*vpp)->next) {
1959 if (varequal((*vpp)->text, name)) {
1960 break;
1961 }
1962 }
1963 return vpp;
1964}
1965
1966/*
1967 * Find the value of a variable. Returns NULL if not set.
1968 */
1969static char *
1970lookupvar(const char *name)
1971{
1972 struct var *v;
1973
1974 v = *findvar(hashvar(name), name);
1975 if (v) {
1976#ifdef DYNAMIC_VAR
1977 /*
1978 * Dynamic variables are implemented roughly the same way they are
1979 * in bash. Namely, they're "special" so long as they aren't unset.
1980 * As soon as they're unset, they're no longer dynamic, and dynamic
1981 * lookup will no longer happen at that point. -- PFM.
1982 */
1983 if ((v->flags & VDYNAMIC))
1984 (*v->func)(NULL);
1985#endif
1986 if (!(v->flags & VUNSET))
1987 return strchrnul(v->text, '=') + 1;
1988 }
1989 return NULL;
1990}
1991
1992/*
1993 * Search the environment of a builtin command.
1994 */
1995static char *
1996bltinlookup(const char *name)
1997{
1998 struct strlist *sp;
1999
2000 for (sp = cmdenviron; sp; sp = sp->next) {
2001 if (varequal(sp->text, name))
2002 return strchrnul(sp->text, '=') + 1;
2003 }
2004 return lookupvar(name);
2005}
2006
2007/*
2008 * Same as setvar except that the variable and value are passed in
2009 * the first argument as name=value. Since the first argument will
2010 * be actually stored in the table, it should not be a string that
2011 * will go away.
2012 * Called with interrupts off.
2013 */
2014static void
2015setvareq(char *s, int flags)
2016{
2017 struct var *vp, **vpp;
2018
2019 vpp = hashvar(s);
2020 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2021 vp = *findvar(vpp, s);
2022 if (vp) {
2023 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2024 const char *n;
2025
2026 if (flags & VNOSAVE)
2027 free(s);
2028 n = vp->text;
2029 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2030 }
2031
2032 if (flags & VNOSET)
2033 return;
2034
2035 if (vp->func && (flags & VNOFUNC) == 0)
2036 (*vp->func)(strchrnul(s, '=') + 1);
2037
2038 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2039 free((char*)vp->text);
2040
2041 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2042 } else {
2043 if (flags & VNOSET)
2044 return;
2045 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002046 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002047 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002048 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002049 *vpp = vp;
2050 }
2051 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2052 s = ckstrdup(s);
2053 vp->text = s;
2054 vp->flags = flags;
2055}
2056
2057/*
2058 * Set the value of a variable. The flags argument is ored with the
2059 * flags of the variable. If val is NULL, the variable is unset.
2060 */
2061static void
2062setvar(const char *name, const char *val, int flags)
2063{
2064 char *p, *q;
2065 size_t namelen;
2066 char *nameeq;
2067 size_t vallen;
2068
2069 q = endofname(name);
2070 p = strchrnul(q, '=');
2071 namelen = p - name;
2072 if (!namelen || p != q)
2073 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2074 vallen = 0;
2075 if (val == NULL) {
2076 flags |= VUNSET;
2077 } else {
2078 vallen = strlen(val);
2079 }
2080 INT_OFF;
2081 nameeq = ckmalloc(namelen + vallen + 2);
2082 p = memcpy(nameeq, name, namelen) + namelen;
2083 if (val) {
2084 *p++ = '=';
2085 p = memcpy(p, val, vallen) + vallen;
2086 }
2087 *p = '\0';
2088 setvareq(nameeq, flags | VNOSAVE);
2089 INT_ON;
2090}
2091
2092#if ENABLE_ASH_GETOPTS
2093/*
2094 * Safe version of setvar, returns 1 on success 0 on failure.
2095 */
2096static int
2097setvarsafe(const char *name, const char *val, int flags)
2098{
2099 int err;
2100 volatile int saveint;
2101 struct jmploc *volatile savehandler = exception_handler;
2102 struct jmploc jmploc;
2103
2104 SAVE_INT(saveint);
2105 if (setjmp(jmploc.loc))
2106 err = 1;
2107 else {
2108 exception_handler = &jmploc;
2109 setvar(name, val, flags);
2110 err = 0;
2111 }
2112 exception_handler = savehandler;
2113 RESTORE_INT(saveint);
2114 return err;
2115}
2116#endif
2117
2118/*
2119 * Unset the specified variable.
2120 */
2121static int
2122unsetvar(const char *s)
2123{
2124 struct var **vpp;
2125 struct var *vp;
2126 int retval;
2127
2128 vpp = findvar(hashvar(s), s);
2129 vp = *vpp;
2130 retval = 2;
2131 if (vp) {
2132 int flags = vp->flags;
2133
2134 retval = 1;
2135 if (flags & VREADONLY)
2136 goto out;
2137#ifdef DYNAMIC_VAR
2138 vp->flags &= ~VDYNAMIC;
2139#endif
2140 if (flags & VUNSET)
2141 goto ok;
2142 if ((flags & VSTRFIXED) == 0) {
2143 INT_OFF;
2144 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2145 free((char*)vp->text);
2146 *vpp = vp->next;
2147 free(vp);
2148 INT_ON;
2149 } else {
2150 setvar(s, 0, 0);
2151 vp->flags &= ~VEXPORT;
2152 }
2153 ok:
2154 retval = 0;
2155 }
2156 out:
2157 return retval;
2158}
2159
2160/*
2161 * Process a linked list of variable assignments.
2162 */
2163static void
2164listsetvar(struct strlist *list_set_var, int flags)
2165{
2166 struct strlist *lp = list_set_var;
2167
2168 if (!lp)
2169 return;
2170 INT_OFF;
2171 do {
2172 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002173 lp = lp->next;
2174 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002175 INT_ON;
2176}
2177
2178/*
2179 * Generate a list of variables satisfying the given conditions.
2180 */
2181static char **
2182listvars(int on, int off, char ***end)
2183{
2184 struct var **vpp;
2185 struct var *vp;
2186 char **ep;
2187 int mask;
2188
2189 STARTSTACKSTR(ep);
2190 vpp = vartab;
2191 mask = on | off;
2192 do {
2193 for (vp = *vpp; vp; vp = vp->next) {
2194 if ((vp->flags & mask) == on) {
2195 if (ep == stackstrend())
2196 ep = growstackstr();
2197 *ep++ = (char *) vp->text;
2198 }
2199 }
2200 } while (++vpp < vartab + VTABSIZE);
2201 if (ep == stackstrend())
2202 ep = growstackstr();
2203 if (end)
2204 *end = ep;
2205 *ep++ = NULL;
2206 return grabstackstr(ep);
2207}
2208
2209
2210/* ============ Path search helper
2211 *
2212 * The variable path (passed by reference) should be set to the start
2213 * of the path before the first call; padvance will update
2214 * this value as it proceeds. Successive calls to padvance will return
2215 * the possible path expansions in sequence. If an option (indicated by
2216 * a percent sign) appears in the path entry then the global variable
2217 * pathopt will be set to point to it; otherwise pathopt will be set to
2218 * NULL.
2219 */
2220static const char *pathopt; /* set by padvance */
2221
2222static char *
2223padvance(const char **path, const char *name)
2224{
2225 const char *p;
2226 char *q;
2227 const char *start;
2228 size_t len;
2229
2230 if (*path == NULL)
2231 return NULL;
2232 start = *path;
2233 for (p = start; *p && *p != ':' && *p != '%'; p++);
2234 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2235 while (stackblocksize() < len)
2236 growstackblock();
2237 q = stackblock();
2238 if (p != start) {
2239 memcpy(q, start, p - start);
2240 q += p - start;
2241 *q++ = '/';
2242 }
2243 strcpy(q, name);
2244 pathopt = NULL;
2245 if (*p == '%') {
2246 pathopt = ++p;
2247 while (*p && *p != ':') p++;
2248 }
2249 if (*p == ':')
2250 *path = p + 1;
2251 else
2252 *path = NULL;
2253 return stalloc(len);
2254}
2255
2256
2257/* ============ Prompt */
2258
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002259static smallint doprompt; /* if set, prompt the user */
2260static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002261
2262#if ENABLE_FEATURE_EDITING
2263static line_input_t *line_input_state;
2264static const char *cmdedit_prompt;
2265static void
2266putprompt(const char *s)
2267{
2268 if (ENABLE_ASH_EXPAND_PRMT) {
2269 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002270 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002271 return;
2272 }
2273 cmdedit_prompt = s;
2274}
2275#else
2276static void
2277putprompt(const char *s)
2278{
2279 out2str(s);
2280}
2281#endif
2282
2283#if ENABLE_ASH_EXPAND_PRMT
2284/* expandstr() needs parsing machinery, so it is far away ahead... */
2285static const char *expandstr(const char *ps);
2286#else
2287#define expandstr(s) s
2288#endif
2289
2290static void
2291setprompt(int whichprompt)
2292{
2293 const char *prompt;
2294#if ENABLE_ASH_EXPAND_PRMT
2295 struct stackmark smark;
2296#endif
2297
2298 needprompt = 0;
2299
2300 switch (whichprompt) {
2301 case 1:
2302 prompt = ps1val();
2303 break;
2304 case 2:
2305 prompt = ps2val();
2306 break;
2307 default: /* 0 */
2308 prompt = nullstr;
2309 }
2310#if ENABLE_ASH_EXPAND_PRMT
2311 setstackmark(&smark);
2312 stalloc(stackblocksize());
2313#endif
2314 putprompt(expandstr(prompt));
2315#if ENABLE_ASH_EXPAND_PRMT
2316 popstackmark(&smark);
2317#endif
2318}
2319
2320
2321/* ============ The cd and pwd commands */
2322
2323#define CD_PHYSICAL 1
2324#define CD_PRINT 2
2325
2326static int docd(const char *, int);
2327
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002328static int
2329cdopt(void)
2330{
2331 int flags = 0;
2332 int i, j;
2333
2334 j = 'L';
2335 while ((i = nextopt("LP"))) {
2336 if (i != j) {
2337 flags ^= CD_PHYSICAL;
2338 j = i;
2339 }
2340 }
2341
2342 return flags;
2343}
2344
2345/*
2346 * Update curdir (the name of the current directory) in response to a
2347 * cd command.
2348 */
2349static const char *
2350updatepwd(const char *dir)
2351{
2352 char *new;
2353 char *p;
2354 char *cdcomppath;
2355 const char *lim;
2356
2357 cdcomppath = ststrdup(dir);
2358 STARTSTACKSTR(new);
2359 if (*dir != '/') {
2360 if (curdir == nullstr)
2361 return 0;
2362 new = stack_putstr(curdir, new);
2363 }
2364 new = makestrspace(strlen(dir) + 2, new);
2365 lim = stackblock() + 1;
2366 if (*dir != '/') {
2367 if (new[-1] != '/')
2368 USTPUTC('/', new);
2369 if (new > lim && *lim == '/')
2370 lim++;
2371 } else {
2372 USTPUTC('/', new);
2373 cdcomppath++;
2374 if (dir[1] == '/' && dir[2] != '/') {
2375 USTPUTC('/', new);
2376 cdcomppath++;
2377 lim++;
2378 }
2379 }
2380 p = strtok(cdcomppath, "/");
2381 while (p) {
2382 switch (*p) {
2383 case '.':
2384 if (p[1] == '.' && p[2] == '\0') {
2385 while (new > lim) {
2386 STUNPUTC(new);
2387 if (new[-1] == '/')
2388 break;
2389 }
2390 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002391 }
2392 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002393 break;
2394 /* fall through */
2395 default:
2396 new = stack_putstr(p, new);
2397 USTPUTC('/', new);
2398 }
2399 p = strtok(0, "/");
2400 }
2401 if (new > lim)
2402 STUNPUTC(new);
2403 *new = 0;
2404 return stackblock();
2405}
2406
2407/*
2408 * Find out what the current directory is. If we already know the current
2409 * directory, this routine returns immediately.
2410 */
2411static char *
2412getpwd(void)
2413{
Denis Vlasenko01631112007-12-16 17:20:38 +00002414 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002415 return dir ? dir : nullstr;
2416}
2417
2418static void
2419setpwd(const char *val, int setold)
2420{
2421 char *oldcur, *dir;
2422
2423 oldcur = dir = curdir;
2424
2425 if (setold) {
2426 setvar("OLDPWD", oldcur, VEXPORT);
2427 }
2428 INT_OFF;
2429 if (physdir != nullstr) {
2430 if (physdir != oldcur)
2431 free(physdir);
2432 physdir = nullstr;
2433 }
2434 if (oldcur == val || !val) {
2435 char *s = getpwd();
2436 physdir = s;
2437 if (!val)
2438 dir = s;
2439 } else
2440 dir = ckstrdup(val);
2441 if (oldcur != dir && oldcur != nullstr) {
2442 free(oldcur);
2443 }
2444 curdir = dir;
2445 INT_ON;
2446 setvar("PWD", dir, VEXPORT);
2447}
2448
2449static void hashcd(void);
2450
2451/*
2452 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2453 * know that the current directory has changed.
2454 */
2455static int
2456docd(const char *dest, int flags)
2457{
2458 const char *dir = 0;
2459 int err;
2460
2461 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2462
2463 INT_OFF;
2464 if (!(flags & CD_PHYSICAL)) {
2465 dir = updatepwd(dest);
2466 if (dir)
2467 dest = dir;
2468 }
2469 err = chdir(dest);
2470 if (err)
2471 goto out;
2472 setpwd(dir, 1);
2473 hashcd();
2474 out:
2475 INT_ON;
2476 return err;
2477}
2478
2479static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002480cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002481{
2482 const char *dest;
2483 const char *path;
2484 const char *p;
2485 char c;
2486 struct stat statb;
2487 int flags;
2488
2489 flags = cdopt();
2490 dest = *argptr;
2491 if (!dest)
2492 dest = bltinlookup(homestr);
2493 else if (LONE_DASH(dest)) {
2494 dest = bltinlookup("OLDPWD");
2495 flags |= CD_PRINT;
2496 }
2497 if (!dest)
2498 dest = nullstr;
2499 if (*dest == '/')
2500 goto step7;
2501 if (*dest == '.') {
2502 c = dest[1];
2503 dotdot:
2504 switch (c) {
2505 case '\0':
2506 case '/':
2507 goto step6;
2508 case '.':
2509 c = dest[2];
2510 if (c != '.')
2511 goto dotdot;
2512 }
2513 }
2514 if (!*dest)
2515 dest = ".";
2516 path = bltinlookup("CDPATH");
2517 if (!path) {
2518 step6:
2519 step7:
2520 p = dest;
2521 goto docd;
2522 }
2523 do {
2524 c = *path;
2525 p = padvance(&path, dest);
2526 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2527 if (c && c != ':')
2528 flags |= CD_PRINT;
2529 docd:
2530 if (!docd(p, flags))
2531 goto out;
2532 break;
2533 }
2534 } while (path);
2535 ash_msg_and_raise_error("can't cd to %s", dest);
2536 /* NOTREACHED */
2537 out:
2538 if (flags & CD_PRINT)
2539 out1fmt(snlfmt, curdir);
2540 return 0;
2541}
2542
2543static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002544pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002545{
2546 int flags;
2547 const char *dir = curdir;
2548
2549 flags = cdopt();
2550 if (flags) {
2551 if (physdir == nullstr)
2552 setpwd(dir, 0);
2553 dir = physdir;
2554 }
2555 out1fmt(snlfmt, dir);
2556 return 0;
2557}
2558
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002559
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002560/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002561
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002562#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002563#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002564
Eric Andersenc470f442003-07-28 09:56:35 +00002565/* Syntax classes */
2566#define CWORD 0 /* character is nothing special */
2567#define CNL 1 /* newline character */
2568#define CBACK 2 /* a backslash character */
2569#define CSQUOTE 3 /* single quote */
2570#define CDQUOTE 4 /* double quote */
2571#define CENDQUOTE 5 /* a terminating quote */
2572#define CBQUOTE 6 /* backwards single quote */
2573#define CVAR 7 /* a dollar sign */
2574#define CENDVAR 8 /* a '}' character */
2575#define CLP 9 /* a left paren in arithmetic */
2576#define CRP 10 /* a right paren in arithmetic */
2577#define CENDFILE 11 /* end of file */
2578#define CCTL 12 /* like CWORD, except it must be escaped */
2579#define CSPCL 13 /* these terminate a word */
2580#define CIGN 14 /* character should be ignored */
2581
Denis Vlasenko131ae172007-02-18 13:00:19 +00002582#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002583#define SYNBASE 130
2584#define PEOF -130
2585#define PEOA -129
2586#define PEOA_OR_PEOF PEOA
2587#else
2588#define SYNBASE 129
2589#define PEOF -129
2590#define PEOA_OR_PEOF PEOF
2591#endif
2592
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002593/* number syntax index */
2594#define BASESYNTAX 0 /* not in quotes */
2595#define DQSYNTAX 1 /* in double quotes */
2596#define SQSYNTAX 2 /* in single quotes */
2597#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002598#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002599
Denis Vlasenko131ae172007-02-18 13:00:19 +00002600#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002601#define USE_SIT_FUNCTION
2602#endif
2603
Denis Vlasenko131ae172007-02-18 13:00:19 +00002604#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002605static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002606#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002607 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002608#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2610 { CNL, CNL, CNL, CNL }, /* 2, \n */
2611 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2612 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2613 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2614 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2615 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2616 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2617 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2618 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2619 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002620#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002621 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2622 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2623 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002624#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002625};
Eric Andersenc470f442003-07-28 09:56:35 +00002626#else
2627static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002628#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002629 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002630#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002631 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2632 { CNL, CNL, CNL }, /* 2, \n */
2633 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2634 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2635 { CVAR, CVAR, CWORD }, /* 5, $ */
2636 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2637 { CSPCL, CWORD, CWORD }, /* 7, ( */
2638 { CSPCL, CWORD, CWORD }, /* 8, ) */
2639 { CBACK, CBACK, CCTL }, /* 9, \ */
2640 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2641 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002642#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002643 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2644 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2645 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002646#endif
2647};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002648#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002649
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002650#ifdef USE_SIT_FUNCTION
2651
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002652static int
2653SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002654{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002655 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002656#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002657 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002658 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2659 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2660 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2661 11, 3 /* "}~" */
2662 };
2663#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002664 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002665 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2666 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2667 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2668 10, 2 /* "}~" */
2669 };
2670#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002671 const char *s;
2672 int indx;
2673
Eric Andersenc470f442003-07-28 09:56:35 +00002674 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002676#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002677 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002678 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002679 else
2680#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002681#define U_C(c) ((unsigned char)(c))
2682
2683 if ((unsigned char)c >= (unsigned char)(CTLESC)
2684 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2685 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002686 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002687 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002688 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002689 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002691 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692 }
2693 return S_I_T[indx][syntax];
2694}
2695
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002696#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002697
Denis Vlasenko131ae172007-02-18 13:00:19 +00002698#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002699#define CSPCL_CIGN_CIGN_CIGN 0
2700#define CSPCL_CWORD_CWORD_CWORD 1
2701#define CNL_CNL_CNL_CNL 2
2702#define CWORD_CCTL_CCTL_CWORD 3
2703#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2704#define CVAR_CVAR_CWORD_CVAR 5
2705#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2706#define CSPCL_CWORD_CWORD_CLP 7
2707#define CSPCL_CWORD_CWORD_CRP 8
2708#define CBACK_CBACK_CCTL_CBACK 9
2709#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2710#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2711#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2712#define CWORD_CWORD_CWORD_CWORD 13
2713#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002714#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002715#define CSPCL_CWORD_CWORD_CWORD 0
2716#define CNL_CNL_CNL_CNL 1
2717#define CWORD_CCTL_CCTL_CWORD 2
2718#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2719#define CVAR_CVAR_CWORD_CVAR 4
2720#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2721#define CSPCL_CWORD_CWORD_CLP 6
2722#define CSPCL_CWORD_CWORD_CRP 7
2723#define CBACK_CBACK_CCTL_CBACK 8
2724#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2725#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2726#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2727#define CWORD_CWORD_CWORD_CWORD 12
2728#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002729#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002730
2731static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002732 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002733 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002734#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002735 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2736#endif
2737 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2739 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2740 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2741 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2742 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2743 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2744 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2745 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002746 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2875 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2876 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002899 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002900 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2902 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002904 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002905 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2906 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2907 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2910 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2911 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2912 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2925 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2926 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2927 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2928 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2929 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2957 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2958 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2959 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2962 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2990 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2991 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2992 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002993};
2994
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002995#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2996
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002997#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002998
Eric Andersen2870d962001-07-02 17:27:21 +00002999
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003000/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003001
Denis Vlasenko131ae172007-02-18 13:00:19 +00003002#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003003
3004#define ALIASINUSE 1
3005#define ALIASDEAD 2
3006
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003007struct alias {
3008 struct alias *next;
3009 char *name;
3010 char *val;
3011 int flag;
3012};
3013
Denis Vlasenko01631112007-12-16 17:20:38 +00003014
3015static struct alias **atab; // [ATABSIZE];
3016#define INIT_G_alias() do { \
3017 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3018} while (0)
3019
Eric Andersen2870d962001-07-02 17:27:21 +00003020
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003021static struct alias **
3022__lookupalias(const char *name) {
3023 unsigned int hashval;
3024 struct alias **app;
3025 const char *p;
3026 unsigned int ch;
3027
3028 p = name;
3029
3030 ch = (unsigned char)*p;
3031 hashval = ch << 4;
3032 while (ch) {
3033 hashval += ch;
3034 ch = (unsigned char)*++p;
3035 }
3036 app = &atab[hashval % ATABSIZE];
3037
3038 for (; *app; app = &(*app)->next) {
3039 if (strcmp(name, (*app)->name) == 0) {
3040 break;
3041 }
3042 }
3043
3044 return app;
3045}
3046
3047static struct alias *
3048lookupalias(const char *name, int check)
3049{
3050 struct alias *ap = *__lookupalias(name);
3051
3052 if (check && ap && (ap->flag & ALIASINUSE))
3053 return NULL;
3054 return ap;
3055}
3056
3057static struct alias *
3058freealias(struct alias *ap)
3059{
3060 struct alias *next;
3061
3062 if (ap->flag & ALIASINUSE) {
3063 ap->flag |= ALIASDEAD;
3064 return ap;
3065 }
3066
3067 next = ap->next;
3068 free(ap->name);
3069 free(ap->val);
3070 free(ap);
3071 return next;
3072}
Eric Andersencb57d552001-06-28 07:25:16 +00003073
Eric Andersenc470f442003-07-28 09:56:35 +00003074static void
3075setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003076{
3077 struct alias *ap, **app;
3078
3079 app = __lookupalias(name);
3080 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003081 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003082 if (ap) {
3083 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003084 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003085 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003086 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003087 ap->flag &= ~ALIASDEAD;
3088 } else {
3089 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003090 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003091 ap->name = ckstrdup(name);
3092 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003093 /*ap->flag = 0; - ckzalloc did it */
3094 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003095 *app = ap;
3096 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003097 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003098}
3099
Eric Andersenc470f442003-07-28 09:56:35 +00003100static int
3101unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003102{
Eric Andersencb57d552001-06-28 07:25:16 +00003103 struct alias **app;
3104
3105 app = __lookupalias(name);
3106
3107 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003108 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003109 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003110 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003111 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003112 }
3113
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003114 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003115}
3116
Eric Andersenc470f442003-07-28 09:56:35 +00003117static void
3118rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003119{
Eric Andersencb57d552001-06-28 07:25:16 +00003120 struct alias *ap, **app;
3121 int i;
3122
Denis Vlasenkob012b102007-02-19 22:43:01 +00003123 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003124 for (i = 0; i < ATABSIZE; i++) {
3125 app = &atab[i];
3126 for (ap = *app; ap; ap = *app) {
3127 *app = freealias(*app);
3128 if (ap == *app) {
3129 app = &ap->next;
3130 }
3131 }
3132 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003133 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003134}
3135
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003136static void
3137printalias(const struct alias *ap)
3138{
3139 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3140}
3141
Eric Andersencb57d552001-06-28 07:25:16 +00003142/*
3143 * TODO - sort output
3144 */
Eric Andersenc470f442003-07-28 09:56:35 +00003145static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003146aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003147{
3148 char *n, *v;
3149 int ret = 0;
3150 struct alias *ap;
3151
Denis Vlasenko68404f12008-03-17 09:00:54 +00003152 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003153 int i;
3154
Denis Vlasenko68404f12008-03-17 09:00:54 +00003155 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003156 for (ap = atab[i]; ap; ap = ap->next) {
3157 printalias(ap);
3158 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003159 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003160 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003161 }
3162 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003163 v = strchr(n+1, '=');
3164 if (v == NULL) { /* n+1: funny ksh stuff */
3165 ap = *__lookupalias(n);
3166 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003167 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003168 ret = 1;
3169 } else
3170 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003171 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003172 *v++ = '\0';
3173 setalias(n, v);
3174 }
3175 }
3176
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003177 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003178}
3179
Eric Andersenc470f442003-07-28 09:56:35 +00003180static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003181unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003182{
3183 int i;
3184
3185 while ((i = nextopt("a")) != '\0') {
3186 if (i == 'a') {
3187 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003188 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003189 }
3190 }
3191 for (i = 0; *argptr; argptr++) {
3192 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003193 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003194 i = 1;
3195 }
3196 }
3197
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003198 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003199}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003200
Denis Vlasenko131ae172007-02-18 13:00:19 +00003201#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003202
Eric Andersenc470f442003-07-28 09:56:35 +00003203
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003204/* ============ jobs.c */
3205
3206/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3207#define FORK_FG 0
3208#define FORK_BG 1
3209#define FORK_NOJOB 2
3210
3211/* mode flags for showjob(s) */
3212#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3213#define SHOW_PID 0x04 /* include process pid */
3214#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3215
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003216/*
3217 * A job structure contains information about a job. A job is either a
3218 * single process or a set of processes contained in a pipeline. In the
3219 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3220 * array of pids.
3221 */
3222
3223struct procstat {
3224 pid_t pid; /* process id */
3225 int status; /* last process status from wait() */
3226 char *cmd; /* text of command being run */
3227};
3228
3229struct job {
3230 struct procstat ps0; /* status of process */
3231 struct procstat *ps; /* status or processes when more than one */
3232#if JOBS
3233 int stopstatus; /* status of a stopped job */
3234#endif
3235 uint32_t
3236 nprocs: 16, /* number of processes */
3237 state: 8,
3238#define JOBRUNNING 0 /* at least one proc running */
3239#define JOBSTOPPED 1 /* all procs are stopped */
3240#define JOBDONE 2 /* all procs are completed */
3241#if JOBS
3242 sigint: 1, /* job was killed by SIGINT */
3243 jobctl: 1, /* job running under job control */
3244#endif
3245 waited: 1, /* true if this entry has been waited for */
3246 used: 1, /* true if this entry is in used */
3247 changed: 1; /* true if status has changed */
3248 struct job *prev_job; /* previous job */
3249};
3250
3251static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003252static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003253
Denis Vlasenko68404f12008-03-17 09:00:54 +00003254static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003255#if !JOBS
3256#define forkshell(job, node, mode) forkshell(job, mode)
3257#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003258static int forkshell(struct job *, union node *, int);
3259static int waitforjob(struct job *);
3260
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003261#if !JOBS
3262enum { jobctl = 0 };
3263#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003264#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003265static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003266static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003267#endif
3268
3269/*
3270 * Set the signal handler for the specified signal. The routine figures
3271 * out what it should be set to.
3272 */
3273static void
3274setsignal(int signo)
3275{
3276 int action;
3277 char *t, tsig;
3278 struct sigaction act;
3279
3280 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003281 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003282 if (t == NULL)
3283 action = S_DFL;
3284 else if (*t != '\0')
3285 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003286 if (rootshell && action == S_DFL) {
3287 switch (signo) {
3288 case SIGINT:
3289 if (iflag || minusc || sflag == 0)
3290 action = S_CATCH;
3291 break;
3292 case SIGQUIT:
3293#if DEBUG
3294 if (debug)
3295 break;
3296#endif
3297 /* FALLTHROUGH */
3298 case SIGTERM:
3299 if (iflag)
3300 action = S_IGN;
3301 break;
3302#if JOBS
3303 case SIGTSTP:
3304 case SIGTTOU:
3305 if (mflag)
3306 action = S_IGN;
3307 break;
3308#endif
3309 }
3310 }
3311
3312 t = &sigmode[signo - 1];
3313 tsig = *t;
3314 if (tsig == 0) {
3315 /*
3316 * current setting unknown
3317 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003318 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003319 /*
3320 * Pretend it worked; maybe we should give a warning
3321 * here, but other shells don't. We don't alter
3322 * sigmode, so that we retry every time.
3323 */
3324 return;
3325 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003326 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 if (mflag
3330 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3331 ) {
3332 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003333 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334 }
3335 }
3336 if (tsig == S_HARD_IGN || tsig == action)
3337 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003338 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339 switch (action) {
3340 case S_CATCH:
3341 act.sa_handler = onsig;
3342 break;
3343 case S_IGN:
3344 act.sa_handler = SIG_IGN;
3345 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003346 }
3347 *t = action;
3348 act.sa_flags = 0;
3349 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003350 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003351}
3352
3353/* mode flags for set_curjob */
3354#define CUR_DELETE 2
3355#define CUR_RUNNING 1
3356#define CUR_STOPPED 0
3357
3358/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003359#define DOWAIT_NONBLOCK WNOHANG
3360#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361
3362#if JOBS
3363/* pgrp of shell on invocation */
3364static int initialpgrp;
3365static int ttyfd = -1;
3366#endif
3367/* array of jobs */
3368static struct job *jobtab;
3369/* size of array */
3370static unsigned njobs;
3371/* current job */
3372static struct job *curjob;
3373/* number of presumed living untracked jobs */
3374static int jobless;
3375
3376static void
3377set_curjob(struct job *jp, unsigned mode)
3378{
3379 struct job *jp1;
3380 struct job **jpp, **curp;
3381
3382 /* first remove from list */
3383 jpp = curp = &curjob;
3384 do {
3385 jp1 = *jpp;
3386 if (jp1 == jp)
3387 break;
3388 jpp = &jp1->prev_job;
3389 } while (1);
3390 *jpp = jp1->prev_job;
3391
3392 /* Then re-insert in correct position */
3393 jpp = curp;
3394 switch (mode) {
3395 default:
3396#if DEBUG
3397 abort();
3398#endif
3399 case CUR_DELETE:
3400 /* job being deleted */
3401 break;
3402 case CUR_RUNNING:
3403 /* newly created job or backgrounded job,
3404 put after all stopped jobs. */
3405 do {
3406 jp1 = *jpp;
3407#if JOBS
3408 if (!jp1 || jp1->state != JOBSTOPPED)
3409#endif
3410 break;
3411 jpp = &jp1->prev_job;
3412 } while (1);
3413 /* FALLTHROUGH */
3414#if JOBS
3415 case CUR_STOPPED:
3416#endif
3417 /* newly stopped job - becomes curjob */
3418 jp->prev_job = *jpp;
3419 *jpp = jp;
3420 break;
3421 }
3422}
3423
3424#if JOBS || DEBUG
3425static int
3426jobno(const struct job *jp)
3427{
3428 return jp - jobtab + 1;
3429}
3430#endif
3431
3432/*
3433 * Convert a job name to a job structure.
3434 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003435#if !JOBS
3436#define getjob(name, getctl) getjob(name)
3437#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003438static struct job *
3439getjob(const char *name, int getctl)
3440{
3441 struct job *jp;
3442 struct job *found;
3443 const char *err_msg = "No such job: %s";
3444 unsigned num;
3445 int c;
3446 const char *p;
3447 char *(*match)(const char *, const char *);
3448
3449 jp = curjob;
3450 p = name;
3451 if (!p)
3452 goto currentjob;
3453
3454 if (*p != '%')
3455 goto err;
3456
3457 c = *++p;
3458 if (!c)
3459 goto currentjob;
3460
3461 if (!p[1]) {
3462 if (c == '+' || c == '%') {
3463 currentjob:
3464 err_msg = "No current job";
3465 goto check;
3466 }
3467 if (c == '-') {
3468 if (jp)
3469 jp = jp->prev_job;
3470 err_msg = "No previous job";
3471 check:
3472 if (!jp)
3473 goto err;
3474 goto gotit;
3475 }
3476 }
3477
3478 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003479// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003480 num = atoi(p);
3481 if (num < njobs) {
3482 jp = jobtab + num - 1;
3483 if (jp->used)
3484 goto gotit;
3485 goto err;
3486 }
3487 }
3488
3489 match = prefix;
3490 if (*p == '?') {
3491 match = strstr;
3492 p++;
3493 }
3494
3495 found = 0;
3496 while (1) {
3497 if (!jp)
3498 goto err;
3499 if (match(jp->ps[0].cmd, p)) {
3500 if (found)
3501 goto err;
3502 found = jp;
3503 err_msg = "%s: ambiguous";
3504 }
3505 jp = jp->prev_job;
3506 }
3507
3508 gotit:
3509#if JOBS
3510 err_msg = "job %s not created under job control";
3511 if (getctl && jp->jobctl == 0)
3512 goto err;
3513#endif
3514 return jp;
3515 err:
3516 ash_msg_and_raise_error(err_msg, name);
3517}
3518
3519/*
3520 * Mark a job structure as unused.
3521 */
3522static void
3523freejob(struct job *jp)
3524{
3525 struct procstat *ps;
3526 int i;
3527
3528 INT_OFF;
3529 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3530 if (ps->cmd != nullstr)
3531 free(ps->cmd);
3532 }
3533 if (jp->ps != &jp->ps0)
3534 free(jp->ps);
3535 jp->used = 0;
3536 set_curjob(jp, CUR_DELETE);
3537 INT_ON;
3538}
3539
3540#if JOBS
3541static void
3542xtcsetpgrp(int fd, pid_t pgrp)
3543{
3544 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003545 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003546}
3547
3548/*
3549 * Turn job control on and off.
3550 *
3551 * Note: This code assumes that the third arg to ioctl is a character
3552 * pointer, which is true on Berkeley systems but not System V. Since
3553 * System V doesn't have job control yet, this isn't a problem now.
3554 *
3555 * Called with interrupts off.
3556 */
3557static void
3558setjobctl(int on)
3559{
3560 int fd;
3561 int pgrp;
3562
3563 if (on == jobctl || rootshell == 0)
3564 return;
3565 if (on) {
3566 int ofd;
3567 ofd = fd = open(_PATH_TTY, O_RDWR);
3568 if (fd < 0) {
3569 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3570 * That sometimes helps to acquire controlling tty.
3571 * Obviously, a workaround for bugs when someone
3572 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003573 fd = 2;
3574 while (!isatty(fd))
3575 if (--fd < 0)
3576 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003577 }
3578 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003579 if (ofd >= 0)
3580 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003581 if (fd < 0)
3582 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003583 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003584 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003585 do { /* while we are in the background */
3586 pgrp = tcgetpgrp(fd);
3587 if (pgrp < 0) {
3588 out:
3589 ash_msg("can't access tty; job control turned off");
3590 mflag = on = 0;
3591 goto close;
3592 }
3593 if (pgrp == getpgrp())
3594 break;
3595 killpg(0, SIGTTIN);
3596 } while (1);
3597 initialpgrp = pgrp;
3598
3599 setsignal(SIGTSTP);
3600 setsignal(SIGTTOU);
3601 setsignal(SIGTTIN);
3602 pgrp = rootpid;
3603 setpgid(0, pgrp);
3604 xtcsetpgrp(fd, pgrp);
3605 } else {
3606 /* turning job control off */
3607 fd = ttyfd;
3608 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003609 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003610 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003611 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003612 setpgid(0, pgrp);
3613 setsignal(SIGTSTP);
3614 setsignal(SIGTTOU);
3615 setsignal(SIGTTIN);
3616 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003617 if (fd >= 0)
3618 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003619 fd = -1;
3620 }
3621 ttyfd = fd;
3622 jobctl = on;
3623}
3624
3625static int
3626killcmd(int argc, char **argv)
3627{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003628 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003629 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003630 do {
3631 if (argv[i][0] == '%') {
3632 struct job *jp = getjob(argv[i], 0);
3633 unsigned pid = jp->ps[0].pid;
3634 /* Enough space for ' -NNN<nul>' */
3635 argv[i] = alloca(sizeof(int)*3 + 3);
3636 /* kill_main has matching code to expect
3637 * leading space. Needed to not confuse
3638 * negative pids with "kill -SIGNAL_NO" syntax */
3639 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003640 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003641 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003642 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003643 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003644}
3645
3646static void
3647showpipe(struct job *jp, FILE *out)
3648{
3649 struct procstat *sp;
3650 struct procstat *spend;
3651
3652 spend = jp->ps + jp->nprocs;
3653 for (sp = jp->ps + 1; sp < spend; sp++)
3654 fprintf(out, " | %s", sp->cmd);
3655 outcslow('\n', out);
3656 flush_stdout_stderr();
3657}
3658
3659
3660static int
3661restartjob(struct job *jp, int mode)
3662{
3663 struct procstat *ps;
3664 int i;
3665 int status;
3666 pid_t pgid;
3667
3668 INT_OFF;
3669 if (jp->state == JOBDONE)
3670 goto out;
3671 jp->state = JOBRUNNING;
3672 pgid = jp->ps->pid;
3673 if (mode == FORK_FG)
3674 xtcsetpgrp(ttyfd, pgid);
3675 killpg(pgid, SIGCONT);
3676 ps = jp->ps;
3677 i = jp->nprocs;
3678 do {
3679 if (WIFSTOPPED(ps->status)) {
3680 ps->status = -1;
3681 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003682 ps++;
3683 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003684 out:
3685 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3686 INT_ON;
3687 return status;
3688}
3689
3690static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003691fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003692{
3693 struct job *jp;
3694 FILE *out;
3695 int mode;
3696 int retval;
3697
3698 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3699 nextopt(nullstr);
3700 argv = argptr;
3701 out = stdout;
3702 do {
3703 jp = getjob(*argv, 1);
3704 if (mode == FORK_BG) {
3705 set_curjob(jp, CUR_RUNNING);
3706 fprintf(out, "[%d] ", jobno(jp));
3707 }
3708 outstr(jp->ps->cmd, out);
3709 showpipe(jp, out);
3710 retval = restartjob(jp, mode);
3711 } while (*argv && *++argv);
3712 return retval;
3713}
3714#endif
3715
3716static int
3717sprint_status(char *s, int status, int sigonly)
3718{
3719 int col;
3720 int st;
3721
3722 col = 0;
3723 if (!WIFEXITED(status)) {
3724#if JOBS
3725 if (WIFSTOPPED(status))
3726 st = WSTOPSIG(status);
3727 else
3728#endif
3729 st = WTERMSIG(status);
3730 if (sigonly) {
3731 if (st == SIGINT || st == SIGPIPE)
3732 goto out;
3733#if JOBS
3734 if (WIFSTOPPED(status))
3735 goto out;
3736#endif
3737 }
3738 st &= 0x7f;
3739 col = fmtstr(s, 32, strsignal(st));
3740 if (WCOREDUMP(status)) {
3741 col += fmtstr(s + col, 16, " (core dumped)");
3742 }
3743 } else if (!sigonly) {
3744 st = WEXITSTATUS(status);
3745 if (st)
3746 col = fmtstr(s, 16, "Done(%d)", st);
3747 else
3748 col = fmtstr(s, 16, "Done");
3749 }
3750 out:
3751 return col;
3752}
3753
3754/*
3755 * Do a wait system call. If job control is compiled in, we accept
3756 * stopped processes. If block is zero, we return a value of zero
3757 * rather than blocking.
3758 *
3759 * System V doesn't have a non-blocking wait system call. It does
3760 * have a SIGCLD signal that is sent to a process when one of it's
3761 * children dies. The obvious way to use SIGCLD would be to install
3762 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3763 * was received, and have waitproc bump another counter when it got
3764 * the status of a process. Waitproc would then know that a wait
3765 * system call would not block if the two counters were different.
3766 * This approach doesn't work because if a process has children that
3767 * have not been waited for, System V will send it a SIGCLD when it
3768 * installs a signal handler for SIGCLD. What this means is that when
3769 * a child exits, the shell will be sent SIGCLD signals continuously
3770 * until is runs out of stack space, unless it does a wait call before
3771 * restoring the signal handler. The code below takes advantage of
3772 * this (mis)feature by installing a signal handler for SIGCLD and
3773 * then checking to see whether it was called. If there are any
3774 * children to be waited for, it will be.
3775 *
3776 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3777 * waits at all. In this case, the user will not be informed when
3778 * a background process until the next time she runs a real program
3779 * (as opposed to running a builtin command or just typing return),
3780 * and the jobs command may give out of date information.
3781 */
3782static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003783waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785#if JOBS
3786 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003787 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003789 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3790 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791}
3792
3793/*
3794 * Wait for a process to terminate.
3795 */
3796static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003797dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798{
3799 int pid;
3800 int status;
3801 struct job *jp;
3802 struct job *thisjob;
3803 int state;
3804
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003805 TRACE(("dowait(%d) called\n", wait_flags));
3806 pid = waitproc(wait_flags, &status);
3807 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003808 if (pid <= 0) {
3809 /* If we were doing blocking wait and (probably) got EINTR,
3810 * check for pending sigs received while waiting.
3811 * (NB: can be moved into callers if needed) */
3812 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3813 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003814 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003815 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003816 INT_OFF;
3817 thisjob = NULL;
3818 for (jp = curjob; jp; jp = jp->prev_job) {
3819 struct procstat *sp;
3820 struct procstat *spend;
3821 if (jp->state == JOBDONE)
3822 continue;
3823 state = JOBDONE;
3824 spend = jp->ps + jp->nprocs;
3825 sp = jp->ps;
3826 do {
3827 if (sp->pid == pid) {
3828 TRACE(("Job %d: changing status of proc %d "
3829 "from 0x%x to 0x%x\n",
3830 jobno(jp), pid, sp->status, status));
3831 sp->status = status;
3832 thisjob = jp;
3833 }
3834 if (sp->status == -1)
3835 state = JOBRUNNING;
3836#if JOBS
3837 if (state == JOBRUNNING)
3838 continue;
3839 if (WIFSTOPPED(sp->status)) {
3840 jp->stopstatus = sp->status;
3841 state = JOBSTOPPED;
3842 }
3843#endif
3844 } while (++sp < spend);
3845 if (thisjob)
3846 goto gotjob;
3847 }
3848#if JOBS
3849 if (!WIFSTOPPED(status))
3850#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003851 jobless--;
3852 goto out;
3853
3854 gotjob:
3855 if (state != JOBRUNNING) {
3856 thisjob->changed = 1;
3857
3858 if (thisjob->state != state) {
3859 TRACE(("Job %d: changing state from %d to %d\n",
3860 jobno(thisjob), thisjob->state, state));
3861 thisjob->state = state;
3862#if JOBS
3863 if (state == JOBSTOPPED) {
3864 set_curjob(thisjob, CUR_STOPPED);
3865 }
3866#endif
3867 }
3868 }
3869
3870 out:
3871 INT_ON;
3872
3873 if (thisjob && thisjob == job) {
3874 char s[48 + 1];
3875 int len;
3876
3877 len = sprint_status(s, status, 1);
3878 if (len) {
3879 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003880 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003881 out2str(s);
3882 }
3883 }
3884 return pid;
3885}
3886
3887#if JOBS
3888static void
3889showjob(FILE *out, struct job *jp, int mode)
3890{
3891 struct procstat *ps;
3892 struct procstat *psend;
3893 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003894 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003895 char s[80];
3896
3897 ps = jp->ps;
3898
3899 if (mode & SHOW_PGID) {
3900 /* just output process (group) id of pipeline */
3901 fprintf(out, "%d\n", ps->pid);
3902 return;
3903 }
3904
3905 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003906 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003907
3908 if (jp == curjob)
3909 s[col - 2] = '+';
3910 else if (curjob && jp == curjob->prev_job)
3911 s[col - 2] = '-';
3912
3913 if (mode & SHOW_PID)
3914 col += fmtstr(s + col, 16, "%d ", ps->pid);
3915
3916 psend = ps + jp->nprocs;
3917
3918 if (jp->state == JOBRUNNING) {
3919 strcpy(s + col, "Running");
3920 col += sizeof("Running") - 1;
3921 } else {
3922 int status = psend[-1].status;
3923 if (jp->state == JOBSTOPPED)
3924 status = jp->stopstatus;
3925 col += sprint_status(s + col, status, 0);
3926 }
3927
3928 goto start;
3929
3930 do {
3931 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003932 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003933 start:
3934 fprintf(out, "%s%*c%s",
3935 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3936 );
3937 if (!(mode & SHOW_PID)) {
3938 showpipe(jp, out);
3939 break;
3940 }
3941 if (++ps == psend) {
3942 outcslow('\n', out);
3943 break;
3944 }
3945 } while (1);
3946
3947 jp->changed = 0;
3948
3949 if (jp->state == JOBDONE) {
3950 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3951 freejob(jp);
3952 }
3953}
3954
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003955/*
3956 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3957 * statuses have changed since the last call to showjobs.
3958 */
3959static void
3960showjobs(FILE *out, int mode)
3961{
3962 struct job *jp;
3963
3964 TRACE(("showjobs(%x) called\n", mode));
3965
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003966 /* If not even one job changed, there is nothing to do */
3967 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 continue;
3969
3970 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003971 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003972 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974 }
3975}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003976
3977static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003978jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003979{
3980 int mode, m;
3981
3982 mode = 0;
3983 while ((m = nextopt("lp"))) {
3984 if (m == 'l')
3985 mode = SHOW_PID;
3986 else
3987 mode = SHOW_PGID;
3988 }
3989
3990 argv = argptr;
3991 if (*argv) {
3992 do
3993 showjob(stdout, getjob(*argv,0), mode);
3994 while (*++argv);
3995 } else
3996 showjobs(stdout, mode);
3997
3998 return 0;
3999}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004000#endif /* JOBS */
4001
4002static int
4003getstatus(struct job *job)
4004{
4005 int status;
4006 int retval;
4007
4008 status = job->ps[job->nprocs - 1].status;
4009 retval = WEXITSTATUS(status);
4010 if (!WIFEXITED(status)) {
4011#if JOBS
4012 retval = WSTOPSIG(status);
4013 if (!WIFSTOPPED(status))
4014#endif
4015 {
4016 /* XXX: limits number of signals */
4017 retval = WTERMSIG(status);
4018#if JOBS
4019 if (retval == SIGINT)
4020 job->sigint = 1;
4021#endif
4022 }
4023 retval += 128;
4024 }
4025 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4026 jobno(job), job->nprocs, status, retval));
4027 return retval;
4028}
4029
4030static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004031waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004032{
4033 struct job *job;
4034 int retval;
4035 struct job *jp;
4036
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004037// exsig++;
4038// xbarrier();
4039 if (pendingsig)
4040 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041
4042 nextopt(nullstr);
4043 retval = 0;
4044
4045 argv = argptr;
4046 if (!*argv) {
4047 /* wait for all jobs */
4048 for (;;) {
4049 jp = curjob;
4050 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004051 if (!jp) /* no running procs */
4052 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004053 if (jp->state == JOBRUNNING)
4054 break;
4055 jp->waited = 1;
4056 jp = jp->prev_job;
4057 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004058 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 }
4060 }
4061
4062 retval = 127;
4063 do {
4064 if (**argv != '%') {
4065 pid_t pid = number(*argv);
4066 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004067 while (1) {
4068 if (!job)
4069 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 if (job->ps[job->nprocs - 1].pid == pid)
4071 break;
4072 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004073 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074 } else
4075 job = getjob(*argv, 0);
4076 /* loop until process terminated or stopped */
4077 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004078 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079 job->waited = 1;
4080 retval = getstatus(job);
4081 repeat:
4082 ;
4083 } while (*++argv);
4084
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004085 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 return retval;
4087}
4088
4089static struct job *
4090growjobtab(void)
4091{
4092 size_t len;
4093 ptrdiff_t offset;
4094 struct job *jp, *jq;
4095
4096 len = njobs * sizeof(*jp);
4097 jq = jobtab;
4098 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4099
4100 offset = (char *)jp - (char *)jq;
4101 if (offset) {
4102 /* Relocate pointers */
4103 size_t l = len;
4104
4105 jq = (struct job *)((char *)jq + l);
4106 while (l) {
4107 l -= sizeof(*jp);
4108 jq--;
4109#define joff(p) ((struct job *)((char *)(p) + l))
4110#define jmove(p) (p) = (void *)((char *)(p) + offset)
4111 if (joff(jp)->ps == &jq->ps0)
4112 jmove(joff(jp)->ps);
4113 if (joff(jp)->prev_job)
4114 jmove(joff(jp)->prev_job);
4115 }
4116 if (curjob)
4117 jmove(curjob);
4118#undef joff
4119#undef jmove
4120 }
4121
4122 njobs += 4;
4123 jobtab = jp;
4124 jp = (struct job *)((char *)jp + len);
4125 jq = jp + 3;
4126 do {
4127 jq->used = 0;
4128 } while (--jq >= jp);
4129 return jp;
4130}
4131
4132/*
4133 * Return a new job structure.
4134 * Called with interrupts off.
4135 */
4136static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004137makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138{
4139 int i;
4140 struct job *jp;
4141
4142 for (i = njobs, jp = jobtab; ; jp++) {
4143 if (--i < 0) {
4144 jp = growjobtab();
4145 break;
4146 }
4147 if (jp->used == 0)
4148 break;
4149 if (jp->state != JOBDONE || !jp->waited)
4150 continue;
4151#if JOBS
4152 if (jobctl)
4153 continue;
4154#endif
4155 freejob(jp);
4156 break;
4157 }
4158 memset(jp, 0, sizeof(*jp));
4159#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004160 /* jp->jobctl is a bitfield.
4161 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004162 if (jobctl)
4163 jp->jobctl = 1;
4164#endif
4165 jp->prev_job = curjob;
4166 curjob = jp;
4167 jp->used = 1;
4168 jp->ps = &jp->ps0;
4169 if (nprocs > 1) {
4170 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4171 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004172 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004173 jobno(jp)));
4174 return jp;
4175}
4176
4177#if JOBS
4178/*
4179 * Return a string identifying a command (to be printed by the
4180 * jobs command).
4181 */
4182static char *cmdnextc;
4183
4184static void
4185cmdputs(const char *s)
4186{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004187 static const char vstype[VSTYPE + 1][3] = {
4188 "", "}", "-", "+", "?", "=",
4189 "%", "%%", "#", "##"
4190 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4191 };
4192
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004193 const char *p, *str;
4194 char c, cc[2] = " ";
4195 char *nextc;
4196 int subtype = 0;
4197 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004198
4199 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4200 p = s;
4201 while ((c = *p++) != 0) {
4202 str = 0;
4203 switch (c) {
4204 case CTLESC:
4205 c = *p++;
4206 break;
4207 case CTLVAR:
4208 subtype = *p++;
4209 if ((subtype & VSTYPE) == VSLENGTH)
4210 str = "${#";
4211 else
4212 str = "${";
4213 if (!(subtype & VSQUOTE) == !(quoted & 1))
4214 goto dostr;
4215 quoted ^= 1;
4216 c = '"';
4217 break;
4218 case CTLENDVAR:
4219 str = "\"}" + !(quoted & 1);
4220 quoted >>= 1;
4221 subtype = 0;
4222 goto dostr;
4223 case CTLBACKQ:
4224 str = "$(...)";
4225 goto dostr;
4226 case CTLBACKQ+CTLQUOTE:
4227 str = "\"$(...)\"";
4228 goto dostr;
4229#if ENABLE_ASH_MATH_SUPPORT
4230 case CTLARI:
4231 str = "$((";
4232 goto dostr;
4233 case CTLENDARI:
4234 str = "))";
4235 goto dostr;
4236#endif
4237 case CTLQUOTEMARK:
4238 quoted ^= 1;
4239 c = '"';
4240 break;
4241 case '=':
4242 if (subtype == 0)
4243 break;
4244 if ((subtype & VSTYPE) != VSNORMAL)
4245 quoted <<= 1;
4246 str = vstype[subtype & VSTYPE];
4247 if (subtype & VSNUL)
4248 c = ':';
4249 else
4250 goto checkstr;
4251 break;
4252 case '\'':
4253 case '\\':
4254 case '"':
4255 case '$':
4256 /* These can only happen inside quotes */
4257 cc[0] = c;
4258 str = cc;
4259 c = '\\';
4260 break;
4261 default:
4262 break;
4263 }
4264 USTPUTC(c, nextc);
4265 checkstr:
4266 if (!str)
4267 continue;
4268 dostr:
4269 while ((c = *str++)) {
4270 USTPUTC(c, nextc);
4271 }
4272 }
4273 if (quoted & 1) {
4274 USTPUTC('"', nextc);
4275 }
4276 *nextc = 0;
4277 cmdnextc = nextc;
4278}
4279
4280/* cmdtxt() and cmdlist() call each other */
4281static void cmdtxt(union node *n);
4282
4283static void
4284cmdlist(union node *np, int sep)
4285{
4286 for (; np; np = np->narg.next) {
4287 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004288 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004289 cmdtxt(np);
4290 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004291 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004292 }
4293}
4294
4295static void
4296cmdtxt(union node *n)
4297{
4298 union node *np;
4299 struct nodelist *lp;
4300 const char *p;
4301 char s[2];
4302
4303 if (!n)
4304 return;
4305 switch (n->type) {
4306 default:
4307#if DEBUG
4308 abort();
4309#endif
4310 case NPIPE:
4311 lp = n->npipe.cmdlist;
4312 for (;;) {
4313 cmdtxt(lp->n);
4314 lp = lp->next;
4315 if (!lp)
4316 break;
4317 cmdputs(" | ");
4318 }
4319 break;
4320 case NSEMI:
4321 p = "; ";
4322 goto binop;
4323 case NAND:
4324 p = " && ";
4325 goto binop;
4326 case NOR:
4327 p = " || ";
4328 binop:
4329 cmdtxt(n->nbinary.ch1);
4330 cmdputs(p);
4331 n = n->nbinary.ch2;
4332 goto donode;
4333 case NREDIR:
4334 case NBACKGND:
4335 n = n->nredir.n;
4336 goto donode;
4337 case NNOT:
4338 cmdputs("!");
4339 n = n->nnot.com;
4340 donode:
4341 cmdtxt(n);
4342 break;
4343 case NIF:
4344 cmdputs("if ");
4345 cmdtxt(n->nif.test);
4346 cmdputs("; then ");
4347 n = n->nif.ifpart;
4348 if (n->nif.elsepart) {
4349 cmdtxt(n);
4350 cmdputs("; else ");
4351 n = n->nif.elsepart;
4352 }
4353 p = "; fi";
4354 goto dotail;
4355 case NSUBSHELL:
4356 cmdputs("(");
4357 n = n->nredir.n;
4358 p = ")";
4359 goto dotail;
4360 case NWHILE:
4361 p = "while ";
4362 goto until;
4363 case NUNTIL:
4364 p = "until ";
4365 until:
4366 cmdputs(p);
4367 cmdtxt(n->nbinary.ch1);
4368 n = n->nbinary.ch2;
4369 p = "; done";
4370 dodo:
4371 cmdputs("; do ");
4372 dotail:
4373 cmdtxt(n);
4374 goto dotail2;
4375 case NFOR:
4376 cmdputs("for ");
4377 cmdputs(n->nfor.var);
4378 cmdputs(" in ");
4379 cmdlist(n->nfor.args, 1);
4380 n = n->nfor.body;
4381 p = "; done";
4382 goto dodo;
4383 case NDEFUN:
4384 cmdputs(n->narg.text);
4385 p = "() { ... }";
4386 goto dotail2;
4387 case NCMD:
4388 cmdlist(n->ncmd.args, 1);
4389 cmdlist(n->ncmd.redirect, 0);
4390 break;
4391 case NARG:
4392 p = n->narg.text;
4393 dotail2:
4394 cmdputs(p);
4395 break;
4396 case NHERE:
4397 case NXHERE:
4398 p = "<<...";
4399 goto dotail2;
4400 case NCASE:
4401 cmdputs("case ");
4402 cmdputs(n->ncase.expr->narg.text);
4403 cmdputs(" in ");
4404 for (np = n->ncase.cases; np; np = np->nclist.next) {
4405 cmdtxt(np->nclist.pattern);
4406 cmdputs(") ");
4407 cmdtxt(np->nclist.body);
4408 cmdputs(";; ");
4409 }
4410 p = "esac";
4411 goto dotail2;
4412 case NTO:
4413 p = ">";
4414 goto redir;
4415 case NCLOBBER:
4416 p = ">|";
4417 goto redir;
4418 case NAPPEND:
4419 p = ">>";
4420 goto redir;
4421 case NTOFD:
4422 p = ">&";
4423 goto redir;
4424 case NFROM:
4425 p = "<";
4426 goto redir;
4427 case NFROMFD:
4428 p = "<&";
4429 goto redir;
4430 case NFROMTO:
4431 p = "<>";
4432 redir:
4433 s[0] = n->nfile.fd + '0';
4434 s[1] = '\0';
4435 cmdputs(s);
4436 cmdputs(p);
4437 if (n->type == NTOFD || n->type == NFROMFD) {
4438 s[0] = n->ndup.dupfd + '0';
4439 p = s;
4440 goto dotail2;
4441 }
4442 n = n->nfile.fname;
4443 goto donode;
4444 }
4445}
4446
4447static char *
4448commandtext(union node *n)
4449{
4450 char *name;
4451
4452 STARTSTACKSTR(cmdnextc);
4453 cmdtxt(n);
4454 name = stackblock();
4455 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4456 name, cmdnextc, cmdnextc));
4457 return ckstrdup(name);
4458}
4459#endif /* JOBS */
4460
4461/*
4462 * Fork off a subshell. If we are doing job control, give the subshell its
4463 * own process group. Jp is a job structure that the job is to be added to.
4464 * N is the command that will be evaluated by the child. Both jp and n may
4465 * be NULL. The mode parameter can be one of the following:
4466 * FORK_FG - Fork off a foreground process.
4467 * FORK_BG - Fork off a background process.
4468 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4469 * process group even if job control is on.
4470 *
4471 * When job control is turned off, background processes have their standard
4472 * input redirected to /dev/null (except for the second and later processes
4473 * in a pipeline).
4474 *
4475 * Called with interrupts off.
4476 */
4477/*
4478 * Clear traps on a fork.
4479 */
4480static void
4481clear_traps(void)
4482{
4483 char **tp;
4484
4485 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004486 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487 INT_OFF;
4488 free(*tp);
4489 *tp = NULL;
4490 if (tp != &trap[0])
4491 setsignal(tp - trap);
4492 INT_ON;
4493 }
4494 }
4495}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004496
4497/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004498static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004499
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004500/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004501static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004502forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503{
4504 int oldlvl;
4505
4506 TRACE(("Child shell %d\n", getpid()));
4507 oldlvl = shlvl;
4508 shlvl++;
4509
4510 closescript();
4511 clear_traps();
4512#if JOBS
4513 /* do job control only in root shell */
4514 jobctl = 0;
4515 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4516 pid_t pgrp;
4517
4518 if (jp->nprocs == 0)
4519 pgrp = getpid();
4520 else
4521 pgrp = jp->ps[0].pid;
4522 /* This can fail because we are doing it in the parent also */
4523 (void)setpgid(0, pgrp);
4524 if (mode == FORK_FG)
4525 xtcsetpgrp(ttyfd, pgrp);
4526 setsignal(SIGTSTP);
4527 setsignal(SIGTTOU);
4528 } else
4529#endif
4530 if (mode == FORK_BG) {
4531 ignoresig(SIGINT);
4532 ignoresig(SIGQUIT);
4533 if (jp->nprocs == 0) {
4534 close(0);
4535 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004536 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537 }
4538 }
4539 if (!oldlvl && iflag) {
4540 setsignal(SIGINT);
4541 setsignal(SIGQUIT);
4542 setsignal(SIGTERM);
4543 }
4544 for (jp = curjob; jp; jp = jp->prev_job)
4545 freejob(jp);
4546 jobless = 0;
4547}
4548
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004549/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004550#if !JOBS
4551#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4552#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004553static void
4554forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4555{
4556 TRACE(("In parent shell: child = %d\n", pid));
4557 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004558 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4559 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004560 jobless++;
4561 return;
4562 }
4563#if JOBS
4564 if (mode != FORK_NOJOB && jp->jobctl) {
4565 int pgrp;
4566
4567 if (jp->nprocs == 0)
4568 pgrp = pid;
4569 else
4570 pgrp = jp->ps[0].pid;
4571 /* This can fail because we are doing it in the child also */
4572 setpgid(pid, pgrp);
4573 }
4574#endif
4575 if (mode == FORK_BG) {
4576 backgndpid = pid; /* set $! */
4577 set_curjob(jp, CUR_RUNNING);
4578 }
4579 if (jp) {
4580 struct procstat *ps = &jp->ps[jp->nprocs++];
4581 ps->pid = pid;
4582 ps->status = -1;
4583 ps->cmd = nullstr;
4584#if JOBS
4585 if (jobctl && n)
4586 ps->cmd = commandtext(n);
4587#endif
4588 }
4589}
4590
4591static int
4592forkshell(struct job *jp, union node *n, int mode)
4593{
4594 int pid;
4595
4596 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4597 pid = fork();
4598 if (pid < 0) {
4599 TRACE(("Fork failed, errno=%d", errno));
4600 if (jp)
4601 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004602 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004603 }
4604 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004605 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004606 else
4607 forkparent(jp, n, mode, pid);
4608 return pid;
4609}
4610
4611/*
4612 * Wait for job to finish.
4613 *
4614 * Under job control we have the problem that while a child process is
4615 * running interrupts generated by the user are sent to the child but not
4616 * to the shell. This means that an infinite loop started by an inter-
4617 * active user may be hard to kill. With job control turned off, an
4618 * interactive user may place an interactive program inside a loop. If
4619 * the interactive program catches interrupts, the user doesn't want
4620 * these interrupts to also abort the loop. The approach we take here
4621 * is to have the shell ignore interrupt signals while waiting for a
4622 * foreground process to terminate, and then send itself an interrupt
4623 * signal if the child process was terminated by an interrupt signal.
4624 * Unfortunately, some programs want to do a bit of cleanup and then
4625 * exit on interrupt; unless these processes terminate themselves by
4626 * sending a signal to themselves (instead of calling exit) they will
4627 * confuse this approach.
4628 *
4629 * Called with interrupts off.
4630 */
4631static int
4632waitforjob(struct job *jp)
4633{
4634 int st;
4635
4636 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4637 while (jp->state == JOBRUNNING) {
4638 dowait(DOWAIT_BLOCK, jp);
4639 }
4640 st = getstatus(jp);
4641#if JOBS
4642 if (jp->jobctl) {
4643 xtcsetpgrp(ttyfd, rootpid);
4644 /*
4645 * This is truly gross.
4646 * If we're doing job control, then we did a TIOCSPGRP which
4647 * caused us (the shell) to no longer be in the controlling
4648 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4649 * intuit from the subprocess exit status whether a SIGINT
4650 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4651 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004652 if (jp->sigint) /* TODO: do the same with all signals */
4653 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004654 }
4655 if (jp->state == JOBDONE)
4656#endif
4657 freejob(jp);
4658 return st;
4659}
4660
4661/*
4662 * return 1 if there are stopped jobs, otherwise 0
4663 */
4664static int
4665stoppedjobs(void)
4666{
4667 struct job *jp;
4668 int retval;
4669
4670 retval = 0;
4671 if (job_warning)
4672 goto out;
4673 jp = curjob;
4674 if (jp && jp->state == JOBSTOPPED) {
4675 out2str("You have stopped jobs.\n");
4676 job_warning = 2;
4677 retval++;
4678 }
4679 out:
4680 return retval;
4681}
4682
4683
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004684/* ============ redir.c
4685 *
4686 * Code for dealing with input/output redirection.
4687 */
4688
4689#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004690#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004691#ifndef PIPE_BUF
4692# define PIPESIZE 4096 /* amount of buffering in a pipe */
4693#else
4694# define PIPESIZE PIPE_BUF
4695#endif
4696
4697/*
4698 * Open a file in noclobber mode.
4699 * The code was copied from bash.
4700 */
4701static int
4702noclobberopen(const char *fname)
4703{
4704 int r, fd;
4705 struct stat finfo, finfo2;
4706
4707 /*
4708 * If the file exists and is a regular file, return an error
4709 * immediately.
4710 */
4711 r = stat(fname, &finfo);
4712 if (r == 0 && S_ISREG(finfo.st_mode)) {
4713 errno = EEXIST;
4714 return -1;
4715 }
4716
4717 /*
4718 * If the file was not present (r != 0), make sure we open it
4719 * exclusively so that if it is created before we open it, our open
4720 * will fail. Make sure that we do not truncate an existing file.
4721 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4722 * file was not a regular file, we leave O_EXCL off.
4723 */
4724 if (r != 0)
4725 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4726 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4727
4728 /* If the open failed, return the file descriptor right away. */
4729 if (fd < 0)
4730 return fd;
4731
4732 /*
4733 * OK, the open succeeded, but the file may have been changed from a
4734 * non-regular file to a regular file between the stat and the open.
4735 * We are assuming that the O_EXCL open handles the case where FILENAME
4736 * did not exist and is symlinked to an existing file between the stat
4737 * and open.
4738 */
4739
4740 /*
4741 * If we can open it and fstat the file descriptor, and neither check
4742 * revealed that it was a regular file, and the file has not been
4743 * replaced, return the file descriptor.
4744 */
4745 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4746 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4747 return fd;
4748
4749 /* The file has been replaced. badness. */
4750 close(fd);
4751 errno = EEXIST;
4752 return -1;
4753}
4754
4755/*
4756 * Handle here documents. Normally we fork off a process to write the
4757 * data to a pipe. If the document is short, we can stuff the data in
4758 * the pipe without forking.
4759 */
4760/* openhere needs this forward reference */
4761static void expandhere(union node *arg, int fd);
4762static int
4763openhere(union node *redir)
4764{
4765 int pip[2];
4766 size_t len = 0;
4767
4768 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004769 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004770 if (redir->type == NHERE) {
4771 len = strlen(redir->nhere.doc->narg.text);
4772 if (len <= PIPESIZE) {
4773 full_write(pip[1], redir->nhere.doc->narg.text, len);
4774 goto out;
4775 }
4776 }
4777 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4778 close(pip[0]);
4779 signal(SIGINT, SIG_IGN);
4780 signal(SIGQUIT, SIG_IGN);
4781 signal(SIGHUP, SIG_IGN);
4782#ifdef SIGTSTP
4783 signal(SIGTSTP, SIG_IGN);
4784#endif
4785 signal(SIGPIPE, SIG_DFL);
4786 if (redir->type == NHERE)
4787 full_write(pip[1], redir->nhere.doc->narg.text, len);
4788 else
4789 expandhere(redir->nhere.doc, pip[1]);
4790 _exit(0);
4791 }
4792 out:
4793 close(pip[1]);
4794 return pip[0];
4795}
4796
4797static int
4798openredirect(union node *redir)
4799{
4800 char *fname;
4801 int f;
4802
4803 switch (redir->nfile.type) {
4804 case NFROM:
4805 fname = redir->nfile.expfname;
4806 f = open(fname, O_RDONLY);
4807 if (f < 0)
4808 goto eopen;
4809 break;
4810 case NFROMTO:
4811 fname = redir->nfile.expfname;
4812 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4813 if (f < 0)
4814 goto ecreate;
4815 break;
4816 case NTO:
4817 /* Take care of noclobber mode. */
4818 if (Cflag) {
4819 fname = redir->nfile.expfname;
4820 f = noclobberopen(fname);
4821 if (f < 0)
4822 goto ecreate;
4823 break;
4824 }
4825 /* FALLTHROUGH */
4826 case NCLOBBER:
4827 fname = redir->nfile.expfname;
4828 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4829 if (f < 0)
4830 goto ecreate;
4831 break;
4832 case NAPPEND:
4833 fname = redir->nfile.expfname;
4834 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4835 if (f < 0)
4836 goto ecreate;
4837 break;
4838 default:
4839#if DEBUG
4840 abort();
4841#endif
4842 /* Fall through to eliminate warning. */
4843 case NTOFD:
4844 case NFROMFD:
4845 f = -1;
4846 break;
4847 case NHERE:
4848 case NXHERE:
4849 f = openhere(redir);
4850 break;
4851 }
4852
4853 return f;
4854 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004855 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004856 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004857 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004858}
4859
4860/*
4861 * Copy a file descriptor to be >= to. Returns -1
4862 * if the source file descriptor is closed, EMPTY if there are no unused
4863 * file descriptors left.
4864 */
4865static int
4866copyfd(int from, int to)
4867{
4868 int newfd;
4869
4870 newfd = fcntl(from, F_DUPFD, to);
4871 if (newfd < 0) {
4872 if (errno == EMFILE)
4873 return EMPTY;
4874 ash_msg_and_raise_error("%d: %m", from);
4875 }
4876 return newfd;
4877}
4878
4879static void
4880dupredirect(union node *redir, int f)
4881{
4882 int fd = redir->nfile.fd;
4883
4884 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4885 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4886 copyfd(redir->ndup.dupfd, fd);
4887 }
4888 return;
4889 }
4890
4891 if (f != fd) {
4892 copyfd(f, fd);
4893 close(f);
4894 }
4895}
4896
4897/*
4898 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4899 * old file descriptors are stashed away so that the redirection can be
4900 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4901 * standard output, and the standard error if it becomes a duplicate of
4902 * stdout, is saved in memory.
4903 */
4904/* flags passed to redirect */
4905#define REDIR_PUSH 01 /* save previous values of file descriptors */
4906#define REDIR_SAVEFD2 03 /* set preverrout */
4907static void
4908redirect(union node *redir, int flags)
4909{
4910 union node *n;
4911 struct redirtab *sv;
4912 int i;
4913 int fd;
4914 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004915
Denis Vlasenko01631112007-12-16 17:20:38 +00004916 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004917 if (!redir) {
4918 return;
4919 }
4920 sv = NULL;
4921 INT_OFF;
4922 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004923 sv = ckmalloc(sizeof(*sv));
4924 sv->next = redirlist;
4925 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004926 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004927 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004928 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004929 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004930 }
4931 n = redir;
4932 do {
4933 fd = n->nfile.fd;
4934 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4935 && n->ndup.dupfd == fd)
4936 continue; /* redirect from/to same file descriptor */
4937
4938 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004939 if (fd == newfd) {
4940 /* Descriptor wasn't open before redirect.
4941 * Mark it for close in the future */
4942 if (sv && sv->renamed[fd] == EMPTY)
4943 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004944 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004945 }
4946 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004947 i = fcntl(fd, F_DUPFD, 10);
4948
4949 if (i == -1) {
4950 i = errno;
4951 if (i != EBADF) {
4952 close(newfd);
4953 errno = i;
4954 ash_msg_and_raise_error("%d: %m", fd);
4955 /* NOTREACHED */
4956 }
4957 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004958 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004959 close(fd);
4960 }
4961 } else {
4962 close(fd);
4963 }
4964 dupredirect(n, newfd);
4965 } while ((n = n->nfile.next));
4966 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004967 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004968 preverrout_fd = sv->renamed[2];
4969}
4970
4971/*
4972 * Undo the effects of the last redirection.
4973 */
4974static void
4975popredir(int drop)
4976{
4977 struct redirtab *rp;
4978 int i;
4979
Denis Vlasenko01631112007-12-16 17:20:38 +00004980 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 return;
4982 INT_OFF;
4983 rp = redirlist;
4984 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004985 if (rp->renamed[i] == CLOSED) {
4986 if (!drop)
4987 close(i);
4988 continue;
4989 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004990 if (rp->renamed[i] != EMPTY) {
4991 if (!drop) {
4992 close(i);
4993 copyfd(rp->renamed[i], i);
4994 }
4995 close(rp->renamed[i]);
4996 }
4997 }
4998 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004999 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005000 free(rp);
5001 INT_ON;
5002}
5003
5004/*
5005 * Undo all redirections. Called on error or interrupt.
5006 */
5007
5008/*
5009 * Discard all saved file descriptors.
5010 */
5011static void
5012clearredir(int drop)
5013{
5014 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005015 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005016 if (!redirlist)
5017 break;
5018 popredir(drop);
5019 }
5020}
5021
5022static int
5023redirectsafe(union node *redir, int flags)
5024{
5025 int err;
5026 volatile int saveint;
5027 struct jmploc *volatile savehandler = exception_handler;
5028 struct jmploc jmploc;
5029
5030 SAVE_INT(saveint);
5031 err = setjmp(jmploc.loc) * 2;
5032 if (!err) {
5033 exception_handler = &jmploc;
5034 redirect(redir, flags);
5035 }
5036 exception_handler = savehandler;
5037 if (err && exception != EXERROR)
5038 longjmp(exception_handler->loc, 1);
5039 RESTORE_INT(saveint);
5040 return err;
5041}
5042
5043
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005044/* ============ Routines to expand arguments to commands
5045 *
5046 * We have to deal with backquotes, shell variables, and file metacharacters.
5047 */
5048
5049/*
5050 * expandarg flags
5051 */
5052#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5053#define EXP_TILDE 0x2 /* do normal tilde expansion */
5054#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5055#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5056#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5057#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5058#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5059#define EXP_WORD 0x80 /* expand word in parameter expansion */
5060#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5061/*
5062 * _rmescape() flags
5063 */
5064#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5065#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5066#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5067#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5068#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5069
5070/*
5071 * Structure specifying which parts of the string should be searched
5072 * for IFS characters.
5073 */
5074struct ifsregion {
5075 struct ifsregion *next; /* next region in list */
5076 int begoff; /* offset of start of region */
5077 int endoff; /* offset of end of region */
5078 int nulonly; /* search for nul bytes only */
5079};
5080
5081struct arglist {
5082 struct strlist *list;
5083 struct strlist **lastp;
5084};
5085
5086/* output of current string */
5087static char *expdest;
5088/* list of back quote expressions */
5089static struct nodelist *argbackq;
5090/* first struct in list of ifs regions */
5091static struct ifsregion ifsfirst;
5092/* last struct in list */
5093static struct ifsregion *ifslastp;
5094/* holds expanded arg list */
5095static struct arglist exparg;
5096
5097/*
5098 * Our own itoa().
5099 */
5100static int
5101cvtnum(arith_t num)
5102{
5103 int len;
5104
5105 expdest = makestrspace(32, expdest);
5106#if ENABLE_ASH_MATH_SUPPORT_64
5107 len = fmtstr(expdest, 32, "%lld", (long long) num);
5108#else
5109 len = fmtstr(expdest, 32, "%ld", num);
5110#endif
5111 STADJUST(len, expdest);
5112 return len;
5113}
5114
5115static size_t
5116esclen(const char *start, const char *p)
5117{
5118 size_t esc = 0;
5119
5120 while (p > start && *--p == CTLESC) {
5121 esc++;
5122 }
5123 return esc;
5124}
5125
5126/*
5127 * Remove any CTLESC characters from a string.
5128 */
5129static char *
5130_rmescapes(char *str, int flag)
5131{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005132 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005133
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005134 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005135 unsigned inquotes;
5136 int notescaped;
5137 int globbing;
5138
5139 p = strpbrk(str, qchars);
5140 if (!p) {
5141 return str;
5142 }
5143 q = p;
5144 r = str;
5145 if (flag & RMESCAPE_ALLOC) {
5146 size_t len = p - str;
5147 size_t fulllen = len + strlen(p) + 1;
5148
5149 if (flag & RMESCAPE_GROW) {
5150 r = makestrspace(fulllen, expdest);
5151 } else if (flag & RMESCAPE_HEAP) {
5152 r = ckmalloc(fulllen);
5153 } else {
5154 r = stalloc(fulllen);
5155 }
5156 q = r;
5157 if (len > 0) {
5158 q = memcpy(q, str, len) + len;
5159 }
5160 }
5161 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5162 globbing = flag & RMESCAPE_GLOB;
5163 notescaped = globbing;
5164 while (*p) {
5165 if (*p == CTLQUOTEMARK) {
5166 inquotes = ~inquotes;
5167 p++;
5168 notescaped = globbing;
5169 continue;
5170 }
5171 if (*p == '\\') {
5172 /* naked back slash */
5173 notescaped = 0;
5174 goto copy;
5175 }
5176 if (*p == CTLESC) {
5177 p++;
5178 if (notescaped && inquotes && *p != '/') {
5179 *q++ = '\\';
5180 }
5181 }
5182 notescaped = globbing;
5183 copy:
5184 *q++ = *p++;
5185 }
5186 *q = '\0';
5187 if (flag & RMESCAPE_GROW) {
5188 expdest = r;
5189 STADJUST(q - r + 1, expdest);
5190 }
5191 return r;
5192}
5193#define rmescapes(p) _rmescapes((p), 0)
5194
5195#define pmatch(a, b) !fnmatch((a), (b), 0)
5196
5197/*
5198 * Prepare a pattern for a expmeta (internal glob(3)) call.
5199 *
5200 * Returns an stalloced string.
5201 */
5202static char *
5203preglob(const char *pattern, int quoted, int flag)
5204{
5205 flag |= RMESCAPE_GLOB;
5206 if (quoted) {
5207 flag |= RMESCAPE_QUOTED;
5208 }
5209 return _rmescapes((char *)pattern, flag);
5210}
5211
5212/*
5213 * Put a string on the stack.
5214 */
5215static void
5216memtodest(const char *p, size_t len, int syntax, int quotes)
5217{
5218 char *q = expdest;
5219
5220 q = makestrspace(len * 2, q);
5221
5222 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005223 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005224 if (!c)
5225 continue;
5226 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5227 USTPUTC(CTLESC, q);
5228 USTPUTC(c, q);
5229 }
5230
5231 expdest = q;
5232}
5233
5234static void
5235strtodest(const char *p, int syntax, int quotes)
5236{
5237 memtodest(p, strlen(p), syntax, quotes);
5238}
5239
5240/*
5241 * Record the fact that we have to scan this region of the
5242 * string for IFS characters.
5243 */
5244static void
5245recordregion(int start, int end, int nulonly)
5246{
5247 struct ifsregion *ifsp;
5248
5249 if (ifslastp == NULL) {
5250 ifsp = &ifsfirst;
5251 } else {
5252 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005253 ifsp = ckzalloc(sizeof(*ifsp));
5254 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005255 ifslastp->next = ifsp;
5256 INT_ON;
5257 }
5258 ifslastp = ifsp;
5259 ifslastp->begoff = start;
5260 ifslastp->endoff = end;
5261 ifslastp->nulonly = nulonly;
5262}
5263
5264static void
5265removerecordregions(int endoff)
5266{
5267 if (ifslastp == NULL)
5268 return;
5269
5270 if (ifsfirst.endoff > endoff) {
5271 while (ifsfirst.next != NULL) {
5272 struct ifsregion *ifsp;
5273 INT_OFF;
5274 ifsp = ifsfirst.next->next;
5275 free(ifsfirst.next);
5276 ifsfirst.next = ifsp;
5277 INT_ON;
5278 }
5279 if (ifsfirst.begoff > endoff)
5280 ifslastp = NULL;
5281 else {
5282 ifslastp = &ifsfirst;
5283 ifsfirst.endoff = endoff;
5284 }
5285 return;
5286 }
5287
5288 ifslastp = &ifsfirst;
5289 while (ifslastp->next && ifslastp->next->begoff < endoff)
5290 ifslastp=ifslastp->next;
5291 while (ifslastp->next != NULL) {
5292 struct ifsregion *ifsp;
5293 INT_OFF;
5294 ifsp = ifslastp->next->next;
5295 free(ifslastp->next);
5296 ifslastp->next = ifsp;
5297 INT_ON;
5298 }
5299 if (ifslastp->endoff > endoff)
5300 ifslastp->endoff = endoff;
5301}
5302
5303static char *
5304exptilde(char *startp, char *p, int flag)
5305{
5306 char c;
5307 char *name;
5308 struct passwd *pw;
5309 const char *home;
5310 int quotes = flag & (EXP_FULL | EXP_CASE);
5311 int startloc;
5312
5313 name = p + 1;
5314
5315 while ((c = *++p) != '\0') {
5316 switch (c) {
5317 case CTLESC:
5318 return startp;
5319 case CTLQUOTEMARK:
5320 return startp;
5321 case ':':
5322 if (flag & EXP_VARTILDE)
5323 goto done;
5324 break;
5325 case '/':
5326 case CTLENDVAR:
5327 goto done;
5328 }
5329 }
5330 done:
5331 *p = '\0';
5332 if (*name == '\0') {
5333 home = lookupvar(homestr);
5334 } else {
5335 pw = getpwnam(name);
5336 if (pw == NULL)
5337 goto lose;
5338 home = pw->pw_dir;
5339 }
5340 if (!home || !*home)
5341 goto lose;
5342 *p = c;
5343 startloc = expdest - (char *)stackblock();
5344 strtodest(home, SQSYNTAX, quotes);
5345 recordregion(startloc, expdest - (char *)stackblock(), 0);
5346 return p;
5347 lose:
5348 *p = c;
5349 return startp;
5350}
5351
5352/*
5353 * Execute a command inside back quotes. If it's a builtin command, we
5354 * want to save its output in a block obtained from malloc. Otherwise
5355 * we fork off a subprocess and get the output of the command via a pipe.
5356 * Should be called with interrupts off.
5357 */
5358struct backcmd { /* result of evalbackcmd */
5359 int fd; /* file descriptor to read from */
5360 char *buf; /* buffer */
5361 int nleft; /* number of chars in buffer */
5362 struct job *jp; /* job structure for command */
5363};
5364
5365/* These forward decls are needed to use "eval" code for backticks handling: */
5366static int back_exitstatus; /* exit status of backquoted command */
5367#define EV_EXIT 01 /* exit after evaluating tree */
5368static void evaltree(union node *, int);
5369
5370static void
5371evalbackcmd(union node *n, struct backcmd *result)
5372{
5373 int saveherefd;
5374
5375 result->fd = -1;
5376 result->buf = NULL;
5377 result->nleft = 0;
5378 result->jp = NULL;
5379 if (n == NULL) {
5380 goto out;
5381 }
5382
5383 saveherefd = herefd;
5384 herefd = -1;
5385
5386 {
5387 int pip[2];
5388 struct job *jp;
5389
5390 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005391 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005392 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005393 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5394 FORCE_INT_ON;
5395 close(pip[0]);
5396 if (pip[1] != 1) {
5397 close(1);
5398 copyfd(pip[1], 1);
5399 close(pip[1]);
5400 }
5401 eflag = 0;
5402 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5403 /* NOTREACHED */
5404 }
5405 close(pip[1]);
5406 result->fd = pip[0];
5407 result->jp = jp;
5408 }
5409 herefd = saveherefd;
5410 out:
5411 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5412 result->fd, result->buf, result->nleft, result->jp));
5413}
5414
5415/*
5416 * Expand stuff in backwards quotes.
5417 */
5418static void
5419expbackq(union node *cmd, int quoted, int quotes)
5420{
5421 struct backcmd in;
5422 int i;
5423 char buf[128];
5424 char *p;
5425 char *dest;
5426 int startloc;
5427 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5428 struct stackmark smark;
5429
5430 INT_OFF;
5431 setstackmark(&smark);
5432 dest = expdest;
5433 startloc = dest - (char *)stackblock();
5434 grabstackstr(dest);
5435 evalbackcmd(cmd, &in);
5436 popstackmark(&smark);
5437
5438 p = in.buf;
5439 i = in.nleft;
5440 if (i == 0)
5441 goto read;
5442 for (;;) {
5443 memtodest(p, i, syntax, quotes);
5444 read:
5445 if (in.fd < 0)
5446 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005447 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005448 TRACE(("expbackq: read returns %d\n", i));
5449 if (i <= 0)
5450 break;
5451 p = buf;
5452 }
5453
Denis Vlasenko60818682007-09-28 22:07:23 +00005454 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005455 if (in.fd >= 0) {
5456 close(in.fd);
5457 back_exitstatus = waitforjob(in.jp);
5458 }
5459 INT_ON;
5460
5461 /* Eat all trailing newlines */
5462 dest = expdest;
5463 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5464 STUNPUTC(dest);
5465 expdest = dest;
5466
5467 if (quoted == 0)
5468 recordregion(startloc, dest - (char *)stackblock(), 0);
5469 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5470 (dest - (char *)stackblock()) - startloc,
5471 (dest - (char *)stackblock()) - startloc,
5472 stackblock() + startloc));
5473}
5474
5475#if ENABLE_ASH_MATH_SUPPORT
5476/*
5477 * Expand arithmetic expression. Backup to start of expression,
5478 * evaluate, place result in (backed up) result, adjust string position.
5479 */
5480static void
5481expari(int quotes)
5482{
5483 char *p, *start;
5484 int begoff;
5485 int flag;
5486 int len;
5487
5488 /* ifsfree(); */
5489
5490 /*
5491 * This routine is slightly over-complicated for
5492 * efficiency. Next we scan backwards looking for the
5493 * start of arithmetic.
5494 */
5495 start = stackblock();
5496 p = expdest - 1;
5497 *p = '\0';
5498 p--;
5499 do {
5500 int esc;
5501
5502 while (*p != CTLARI) {
5503 p--;
5504#if DEBUG
5505 if (p < start) {
5506 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5507 }
5508#endif
5509 }
5510
5511 esc = esclen(start, p);
5512 if (!(esc % 2)) {
5513 break;
5514 }
5515
5516 p -= esc + 1;
5517 } while (1);
5518
5519 begoff = p - start;
5520
5521 removerecordregions(begoff);
5522
5523 flag = p[1];
5524
5525 expdest = p;
5526
5527 if (quotes)
5528 rmescapes(p + 2);
5529
5530 len = cvtnum(dash_arith(p + 2));
5531
5532 if (flag != '"')
5533 recordregion(begoff, begoff + len, 0);
5534}
5535#endif
5536
5537/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005538static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005539
5540/*
5541 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5542 * characters to allow for further processing. Otherwise treat
5543 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005544 *
5545 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5546 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5547 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005548 */
5549static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005550argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005551{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005552 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553 '=',
5554 ':',
5555 CTLQUOTEMARK,
5556 CTLENDVAR,
5557 CTLESC,
5558 CTLVAR,
5559 CTLBACKQ,
5560 CTLBACKQ | CTLQUOTE,
5561#if ENABLE_ASH_MATH_SUPPORT
5562 CTLENDARI,
5563#endif
5564 0
5565 };
5566 const char *reject = spclchars;
5567 int c;
5568 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5569 int breakall = flag & EXP_WORD;
5570 int inquotes;
5571 size_t length;
5572 int startloc;
5573
5574 if (!(flag & EXP_VARTILDE)) {
5575 reject += 2;
5576 } else if (flag & EXP_VARTILDE2) {
5577 reject++;
5578 }
5579 inquotes = 0;
5580 length = 0;
5581 if (flag & EXP_TILDE) {
5582 char *q;
5583
5584 flag &= ~EXP_TILDE;
5585 tilde:
5586 q = p;
5587 if (*q == CTLESC && (flag & EXP_QWORD))
5588 q++;
5589 if (*q == '~')
5590 p = exptilde(p, q, flag);
5591 }
5592 start:
5593 startloc = expdest - (char *)stackblock();
5594 for (;;) {
5595 length += strcspn(p + length, reject);
5596 c = p[length];
5597 if (c && (!(c & 0x80)
5598#if ENABLE_ASH_MATH_SUPPORT
5599 || c == CTLENDARI
5600#endif
5601 )) {
5602 /* c == '=' || c == ':' || c == CTLENDARI */
5603 length++;
5604 }
5605 if (length > 0) {
5606 int newloc;
5607 expdest = stack_nputstr(p, length, expdest);
5608 newloc = expdest - (char *)stackblock();
5609 if (breakall && !inquotes && newloc > startloc) {
5610 recordregion(startloc, newloc, 0);
5611 }
5612 startloc = newloc;
5613 }
5614 p += length + 1;
5615 length = 0;
5616
5617 switch (c) {
5618 case '\0':
5619 goto breakloop;
5620 case '=':
5621 if (flag & EXP_VARTILDE2) {
5622 p--;
5623 continue;
5624 }
5625 flag |= EXP_VARTILDE2;
5626 reject++;
5627 /* fall through */
5628 case ':':
5629 /*
5630 * sort of a hack - expand tildes in variable
5631 * assignments (after the first '=' and after ':'s).
5632 */
5633 if (*--p == '~') {
5634 goto tilde;
5635 }
5636 continue;
5637 }
5638
5639 switch (c) {
5640 case CTLENDVAR: /* ??? */
5641 goto breakloop;
5642 case CTLQUOTEMARK:
5643 /* "$@" syntax adherence hack */
5644 if (
5645 !inquotes &&
5646 !memcmp(p, dolatstr, 4) &&
5647 (p[4] == CTLQUOTEMARK || (
5648 p[4] == CTLENDVAR &&
5649 p[5] == CTLQUOTEMARK
5650 ))
5651 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005652 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005653 goto start;
5654 }
5655 inquotes = !inquotes;
5656 addquote:
5657 if (quotes) {
5658 p--;
5659 length++;
5660 startloc++;
5661 }
5662 break;
5663 case CTLESC:
5664 startloc++;
5665 length++;
5666 goto addquote;
5667 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005668 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005669 goto start;
5670 case CTLBACKQ:
5671 c = 0;
5672 case CTLBACKQ|CTLQUOTE:
5673 expbackq(argbackq->n, c, quotes);
5674 argbackq = argbackq->next;
5675 goto start;
5676#if ENABLE_ASH_MATH_SUPPORT
5677 case CTLENDARI:
5678 p--;
5679 expari(quotes);
5680 goto start;
5681#endif
5682 }
5683 }
5684 breakloop:
5685 ;
5686}
5687
5688static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005689scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005690 int zero)
5691{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005692 char *loc, *loc2, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005693 char c;
5694
5695 loc = startp;
5696 loc2 = rmesc;
5697 do {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005698 int match = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005699 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005700
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005701 c = *loc2;
5702 if (zero) {
5703 *loc2 = '\0';
5704 s = rmesc;
5705 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005706
5707 // chop off end if its '*'
5708 full = strrchr(str, '*');
5709 if (full && full != str)
5710 match--;
5711
5712 // If str starts with '*' replace with s.
5713 if ((*str == '*') && strlen(s) >= match) {
5714 full = xstrdup(s);
5715 strncpy(full+strlen(s)-match+1, str+1, match-1);
5716 } else
5717 full = xstrndup(str, match);
5718 match = strncmp(s, full, strlen(full));
5719 free(full);
5720
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005721 *loc2 = c;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005722 if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723 return loc;
5724 if (quotes && *loc == CTLESC)
5725 loc++;
5726 loc++;
5727 loc2++;
5728 } while (c);
5729 return 0;
5730}
5731
5732static char *
5733scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5734 int zero)
5735{
5736 int esc = 0;
5737 char *loc;
5738 char *loc2;
5739
5740 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5741 int match;
5742 char c = *loc2;
5743 const char *s = loc2;
5744 if (zero) {
5745 *loc2 = '\0';
5746 s = rmesc;
5747 }
5748 match = pmatch(str, s);
5749 *loc2 = c;
5750 if (match)
5751 return loc;
5752 loc--;
5753 if (quotes) {
5754 if (--esc < 0) {
5755 esc = esclen(startp, loc);
5756 }
5757 if (esc % 2) {
5758 esc--;
5759 loc--;
5760 }
5761 }
5762 }
5763 return 0;
5764}
5765
5766static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5767static void
5768varunset(const char *end, const char *var, const char *umsg, int varflags)
5769{
5770 const char *msg;
5771 const char *tail;
5772
5773 tail = nullstr;
5774 msg = "parameter not set";
5775 if (umsg) {
5776 if (*end == CTLENDVAR) {
5777 if (varflags & VSNUL)
5778 tail = " or null";
5779 } else
5780 msg = umsg;
5781 }
5782 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5783}
5784
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005785#if ENABLE_ASH_BASH_COMPAT
5786static char *
5787parse_sub_pattern(char *arg, int inquotes)
5788{
5789 char *idx, *repl = NULL;
5790 unsigned char c;
5791
5792 for (idx = arg; *arg; arg++) {
5793 if (*arg == '/') {
5794 /* Only the first '/' seen is our seperator */
5795 if (!repl) {
5796 *idx++ = '\0';
5797 repl = idx;
5798 } else
5799 *idx++ = *arg;
5800 } else if (*arg != '\\') {
5801 *idx++ = *arg;
5802 } else {
5803 if (inquotes)
5804 arg++;
5805 else {
5806 if (*(arg + 1) != '\\')
5807 goto single_backslash;
5808 arg += 2;
5809 }
5810
5811 switch (*arg) {
5812 case 'n': c = '\n'; break;
5813 case 'r': c = '\r'; break;
5814 case 't': c = '\t'; break;
5815 case 'v': c = '\v'; break;
5816 case 'f': c = '\f'; break;
5817 case 'b': c = '\b'; break;
5818 case 'a': c = '\a'; break;
5819 case '\\':
5820 if (*(arg + 1) != '\\' && !inquotes)
5821 goto single_backslash;
5822 arg++;
5823 /* FALLTHROUGH */
5824 case '\0':
5825 /* Trailing backslash, just stuff one in the buffer
5826 * and backup arg so the loop will exit.
5827 */
5828 c = '\\';
5829 if (!*arg)
5830 arg--;
5831 break;
5832 default:
5833 c = *arg;
5834 if (isdigit(c)) {
5835 /* It's an octal number, parse it. */
5836 int i;
5837 c = 0;
5838
5839 for (i = 0; *arg && i < 3; arg++, i++) {
5840 if (*arg >= '8' || *arg < '0')
5841 ash_msg_and_raise_error("Invalid octal char in pattern");
5842// TODO: number() instead? It does error checking...
5843 c = (c << 3) + atoi(arg);
5844 }
5845 /* back off one (so outer loop can do it) */
5846 arg--;
5847 }
5848 }
5849 *idx++ = c;
5850 }
5851 }
5852 *idx = *arg;
5853
5854 return repl;
5855
5856 single_backslash:
5857 ash_msg_and_raise_error("single backslash unexpected");
5858 /* NOTREACHED */
5859}
5860#endif /* ENABLE_ASH_BASH_COMPAT */
5861
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005862static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005863subevalvar(char *p, char *str, int strloc, int subtype,
5864 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005865{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005866 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867 char *startp;
5868 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005869 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005870 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5871 USE_ASH_BASH_COMPAT(char null = '\0';)
5872 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5873 int saveherefd = herefd;
5874 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005875 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005876 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005877
5878 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005879 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5880 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005881 STPUTC('\0', expdest);
5882 herefd = saveherefd;
5883 argbackq = saveargbackq;
5884 startp = stackblock() + startloc;
5885
5886 switch (subtype) {
5887 case VSASSIGN:
5888 setvar(str, startp, 0);
5889 amount = startp - expdest;
5890 STADJUST(amount, expdest);
5891 return startp;
5892
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005893#if ENABLE_ASH_BASH_COMPAT
5894 case VSSUBSTR:
5895 loc = str = stackblock() + strloc;
5896// TODO: number() instead? It does error checking...
5897 pos = atoi(loc);
5898 len = str - startp - 1;
5899
5900 /* *loc != '\0', guaranteed by parser */
5901 if (quotes) {
5902 char *ptr;
5903
5904 /* We must adjust the length by the number of escapes we find. */
5905 for (ptr = startp; ptr < (str - 1); ptr++) {
5906 if(*ptr == CTLESC) {
5907 len--;
5908 ptr++;
5909 }
5910 }
5911 }
5912 orig_len = len;
5913
5914 if (*loc++ == ':') {
5915// TODO: number() instead? It does error checking...
5916 len = atoi(loc);
5917 } else {
5918 len = orig_len;
5919 while (*loc && *loc != ':')
5920 loc++;
5921 if (*loc++ == ':')
5922// TODO: number() instead? It does error checking...
5923 len = atoi(loc);
5924 }
5925 if (pos >= orig_len) {
5926 pos = 0;
5927 len = 0;
5928 }
5929 if (len > (orig_len - pos))
5930 len = orig_len - pos;
5931
5932 for (str = startp; pos; str++, pos--) {
5933 if (quotes && *str == CTLESC)
5934 str++;
5935 }
5936 for (loc = startp; len; len--) {
5937 if (quotes && *str == CTLESC)
5938 *loc++ = *str++;
5939 *loc++ = *str++;
5940 }
5941 *loc = '\0';
5942 amount = loc - expdest;
5943 STADJUST(amount, expdest);
5944 return loc;
5945#endif
5946
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005947 case VSQUESTION:
5948 varunset(p, str, startp, varflags);
5949 /* NOTREACHED */
5950 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005951 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005952
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953 /* We'll comeback here if we grow the stack while handling
5954 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5955 * stack will need rebasing, and we'll need to remove our work
5956 * areas each time
5957 */
5958 USE_ASH_BASH_COMPAT(restart:)
5959
5960 amount = expdest - ((char *)stackblock() + resetloc);
5961 STADJUST(-amount, expdest);
5962 startp = stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963
5964 rmesc = startp;
5965 rmescend = stackblock() + strloc;
5966 if (quotes) {
5967 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5968 if (rmesc != startp) {
5969 rmescend = expdest;
5970 startp = stackblock() + startloc;
5971 }
5972 }
5973 rmescend--;
5974 str = stackblock() + strloc;
5975 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005976 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005978#if ENABLE_ASH_BASH_COMPAT
5979 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5980 char *idx, *end, *restart_detect;
5981
5982 if(!repl) {
5983 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5984 if (!repl)
5985 repl = &null;
5986 }
5987
5988 /* If there's no pattern to match, return the expansion unmolested */
5989 if (*str == '\0')
5990 return 0;
5991
5992 len = 0;
5993 idx = startp;
5994 end = str - 1;
5995 while (idx < end) {
5996 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5997 if (!loc) {
5998 /* No match, advance */
5999 restart_detect = stackblock();
6000 STPUTC(*idx, expdest);
6001 if (quotes && *idx == CTLESC) {
6002 idx++;
6003 len++;
6004 STPUTC(*idx, expdest);
6005 }
6006 if (stackblock() != restart_detect)
6007 goto restart;
6008 idx++;
6009 len++;
6010 rmesc++;
6011 continue;
6012 }
6013
6014 if (subtype == VSREPLACEALL) {
6015 while (idx < loc) {
6016 if (quotes && *idx == CTLESC)
6017 idx++;
6018 idx++;
6019 rmesc++;
6020 }
6021 } else
6022 idx = loc;
6023
6024 for (loc = repl; *loc; loc++) {
6025 restart_detect = stackblock();
6026 STPUTC(*loc, expdest);
6027 if (stackblock() != restart_detect)
6028 goto restart;
6029 len++;
6030 }
6031
6032 if (subtype == VSREPLACE) {
6033 while (*idx) {
6034 restart_detect = stackblock();
6035 STPUTC(*idx, expdest);
6036 if (stackblock() != restart_detect)
6037 goto restart;
6038 len++;
6039 idx++;
6040 }
6041 break;
6042 }
6043 }
6044
6045 /* We've put the replaced text into a buffer at workloc, now
6046 * move it to the right place and adjust the stack.
6047 */
6048 startp = stackblock() + startloc;
6049 STPUTC('\0', expdest);
6050 memmove(startp, stackblock() + workloc, len);
6051 startp[len++] = '\0';
6052 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6053 STADJUST(-amount, expdest);
6054 return startp;
6055 }
6056#endif /* ENABLE_ASH_BASH_COMPAT */
6057
6058 subtype -= VSTRIMRIGHT;
6059#if DEBUG
6060 if (subtype < 0 || subtype > 7)
6061 abort();
6062#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6064 zero = subtype >> 1;
6065 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6066 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6067
6068 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6069 if (loc) {
6070 if (zero) {
6071 memmove(startp, loc, str - loc);
6072 loc = startp + (str - loc) - 1;
6073 }
6074 *loc = '\0';
6075 amount = loc - expdest;
6076 STADJUST(amount, expdest);
6077 }
6078 return loc;
6079}
6080
6081/*
6082 * Add the value of a specialized variable to the stack string.
6083 */
6084static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006085varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006086{
6087 int num;
6088 char *p;
6089 int i;
6090 int sep = 0;
6091 int sepq = 0;
6092 ssize_t len = 0;
6093 char **ap;
6094 int syntax;
6095 int quoted = varflags & VSQUOTE;
6096 int subtype = varflags & VSTYPE;
6097 int quotes = flags & (EXP_FULL | EXP_CASE);
6098
6099 if (quoted && (flags & EXP_FULL))
6100 sep = 1 << CHAR_BIT;
6101
6102 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6103 switch (*name) {
6104 case '$':
6105 num = rootpid;
6106 goto numvar;
6107 case '?':
6108 num = exitstatus;
6109 goto numvar;
6110 case '#':
6111 num = shellparam.nparam;
6112 goto numvar;
6113 case '!':
6114 num = backgndpid;
6115 if (num == 0)
6116 return -1;
6117 numvar:
6118 len = cvtnum(num);
6119 break;
6120 case '-':
6121 p = makestrspace(NOPTS, expdest);
6122 for (i = NOPTS - 1; i >= 0; i--) {
6123 if (optlist[i]) {
6124 USTPUTC(optletters(i), p);
6125 len++;
6126 }
6127 }
6128 expdest = p;
6129 break;
6130 case '@':
6131 if (sep)
6132 goto param;
6133 /* fall through */
6134 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006135 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6137 sepq = 1;
6138 param:
6139 ap = shellparam.p;
6140 if (!ap)
6141 return -1;
6142 while ((p = *ap++)) {
6143 size_t partlen;
6144
6145 partlen = strlen(p);
6146 len += partlen;
6147
6148 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6149 memtodest(p, partlen, syntax, quotes);
6150
6151 if (*ap && sep) {
6152 char *q;
6153
6154 len++;
6155 if (subtype == VSPLUS || subtype == VSLENGTH) {
6156 continue;
6157 }
6158 q = expdest;
6159 if (sepq)
6160 STPUTC(CTLESC, q);
6161 STPUTC(sep, q);
6162 expdest = q;
6163 }
6164 }
6165 return len;
6166 case '0':
6167 case '1':
6168 case '2':
6169 case '3':
6170 case '4':
6171 case '5':
6172 case '6':
6173 case '7':
6174 case '8':
6175 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006176// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 num = atoi(name);
6178 if (num < 0 || num > shellparam.nparam)
6179 return -1;
6180 p = num ? shellparam.p[num - 1] : arg0;
6181 goto value;
6182 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006183 /* NB: name has form "VAR=..." */
6184
6185 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6186 * which should be considered before we check variables. */
6187 if (var_str_list) {
6188 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6189 p = NULL;
6190 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006191 char *str, *eq;
6192 str = var_str_list->text;
6193 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006194 if (!eq) /* stop at first non-assignment */
6195 break;
6196 eq++;
6197 if (name_len == (eq - str)
6198 && strncmp(str, name, name_len) == 0) {
6199 p = eq;
6200 /* goto value; - WRONG! */
6201 /* think "A=1 A=2 B=$A" */
6202 }
6203 var_str_list = var_str_list->next;
6204 } while (var_str_list);
6205 if (p)
6206 goto value;
6207 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006208 p = lookupvar(name);
6209 value:
6210 if (!p)
6211 return -1;
6212
6213 len = strlen(p);
6214 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6215 memtodest(p, len, syntax, quotes);
6216 return len;
6217 }
6218
6219 if (subtype == VSPLUS || subtype == VSLENGTH)
6220 STADJUST(-len, expdest);
6221 return len;
6222}
6223
6224/*
6225 * Expand a variable, and return a pointer to the next character in the
6226 * input string.
6227 */
6228static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006229evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006230{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006231 char varflags;
6232 char subtype;
6233 char quoted;
6234 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006235 char *var;
6236 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006237 int startloc;
6238 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006239
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006240 varflags = *p++;
6241 subtype = varflags & VSTYPE;
6242 quoted = varflags & VSQUOTE;
6243 var = p;
6244 easy = (!quoted || (*var == '@' && shellparam.nparam));
6245 startloc = expdest - (char *)stackblock();
6246 p = strchr(p, '=') + 1;
6247
6248 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006249 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006250 if (varflags & VSNUL)
6251 varlen--;
6252
6253 if (subtype == VSPLUS) {
6254 varlen = -1 - varlen;
6255 goto vsplus;
6256 }
6257
6258 if (subtype == VSMINUS) {
6259 vsplus:
6260 if (varlen < 0) {
6261 argstr(
6262 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006263 (quoted ? EXP_QWORD : EXP_WORD),
6264 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006265 );
6266 goto end;
6267 }
6268 if (easy)
6269 goto record;
6270 goto end;
6271 }
6272
6273 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6274 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006275 if (subevalvar(p, var, /* strloc: */ 0,
6276 subtype, startloc, varflags,
6277 /* quotes: */ 0,
6278 var_str_list)
6279 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280 varflags &= ~VSNUL;
6281 /*
6282 * Remove any recorded regions beyond
6283 * start of variable
6284 */
6285 removerecordregions(startloc);
6286 goto again;
6287 }
6288 goto end;
6289 }
6290 if (easy)
6291 goto record;
6292 goto end;
6293 }
6294
6295 if (varlen < 0 && uflag)
6296 varunset(p, var, 0, 0);
6297
6298 if (subtype == VSLENGTH) {
6299 cvtnum(varlen > 0 ? varlen : 0);
6300 goto record;
6301 }
6302
6303 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006304 if (easy)
6305 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006306 goto end;
6307 }
6308
6309#if DEBUG
6310 switch (subtype) {
6311 case VSTRIMLEFT:
6312 case VSTRIMLEFTMAX:
6313 case VSTRIMRIGHT:
6314 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006315#if ENABLE_ASH_BASH_COMPAT
6316 case VSSUBSTR:
6317 case VSREPLACE:
6318 case VSREPLACEALL:
6319#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006320 break;
6321 default:
6322 abort();
6323 }
6324#endif
6325
6326 if (varlen >= 0) {
6327 /*
6328 * Terminate the string and start recording the pattern
6329 * right after it
6330 */
6331 STPUTC('\0', expdest);
6332 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006333 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6334 startloc, varflags,
6335 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6336 var_str_list)
6337 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006338 int amount = expdest - (
6339 (char *)stackblock() + patloc - 1
6340 );
6341 STADJUST(-amount, expdest);
6342 }
6343 /* Remove any recorded regions beyond start of variable */
6344 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006345 record:
6346 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006347 }
6348
6349 end:
6350 if (subtype != VSNORMAL) { /* skip to end of alternative */
6351 int nesting = 1;
6352 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006353 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006354 if (c == CTLESC)
6355 p++;
6356 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6357 if (varlen >= 0)
6358 argbackq = argbackq->next;
6359 } else if (c == CTLVAR) {
6360 if ((*p++ & VSTYPE) != VSNORMAL)
6361 nesting++;
6362 } else if (c == CTLENDVAR) {
6363 if (--nesting == 0)
6364 break;
6365 }
6366 }
6367 }
6368 return p;
6369}
6370
6371/*
6372 * Break the argument string into pieces based upon IFS and add the
6373 * strings to the argument list. The regions of the string to be
6374 * searched for IFS characters have been stored by recordregion.
6375 */
6376static void
6377ifsbreakup(char *string, struct arglist *arglist)
6378{
6379 struct ifsregion *ifsp;
6380 struct strlist *sp;
6381 char *start;
6382 char *p;
6383 char *q;
6384 const char *ifs, *realifs;
6385 int ifsspc;
6386 int nulonly;
6387
6388 start = string;
6389 if (ifslastp != NULL) {
6390 ifsspc = 0;
6391 nulonly = 0;
6392 realifs = ifsset() ? ifsval() : defifs;
6393 ifsp = &ifsfirst;
6394 do {
6395 p = string + ifsp->begoff;
6396 nulonly = ifsp->nulonly;
6397 ifs = nulonly ? nullstr : realifs;
6398 ifsspc = 0;
6399 while (p < string + ifsp->endoff) {
6400 q = p;
6401 if (*p == CTLESC)
6402 p++;
6403 if (!strchr(ifs, *p)) {
6404 p++;
6405 continue;
6406 }
6407 if (!nulonly)
6408 ifsspc = (strchr(defifs, *p) != NULL);
6409 /* Ignore IFS whitespace at start */
6410 if (q == start && ifsspc) {
6411 p++;
6412 start = p;
6413 continue;
6414 }
6415 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006416 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006417 sp->text = start;
6418 *arglist->lastp = sp;
6419 arglist->lastp = &sp->next;
6420 p++;
6421 if (!nulonly) {
6422 for (;;) {
6423 if (p >= string + ifsp->endoff) {
6424 break;
6425 }
6426 q = p;
6427 if (*p == CTLESC)
6428 p++;
6429 if (strchr(ifs, *p) == NULL ) {
6430 p = q;
6431 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006432 }
6433 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006434 if (ifsspc) {
6435 p++;
6436 ifsspc = 0;
6437 } else {
6438 p = q;
6439 break;
6440 }
6441 } else
6442 p++;
6443 }
6444 }
6445 start = p;
6446 } /* while */
6447 ifsp = ifsp->next;
6448 } while (ifsp != NULL);
6449 if (nulonly)
6450 goto add;
6451 }
6452
6453 if (!*start)
6454 return;
6455
6456 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006457 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006458 sp->text = start;
6459 *arglist->lastp = sp;
6460 arglist->lastp = &sp->next;
6461}
6462
6463static void
6464ifsfree(void)
6465{
6466 struct ifsregion *p;
6467
6468 INT_OFF;
6469 p = ifsfirst.next;
6470 do {
6471 struct ifsregion *ifsp;
6472 ifsp = p->next;
6473 free(p);
6474 p = ifsp;
6475 } while (p);
6476 ifslastp = NULL;
6477 ifsfirst.next = NULL;
6478 INT_ON;
6479}
6480
6481/*
6482 * Add a file name to the list.
6483 */
6484static void
6485addfname(const char *name)
6486{
6487 struct strlist *sp;
6488
Denis Vlasenko597906c2008-02-20 16:38:54 +00006489 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006490 sp->text = ststrdup(name);
6491 *exparg.lastp = sp;
6492 exparg.lastp = &sp->next;
6493}
6494
6495static char *expdir;
6496
6497/*
6498 * Do metacharacter (i.e. *, ?, [...]) expansion.
6499 */
6500static void
6501expmeta(char *enddir, char *name)
6502{
6503 char *p;
6504 const char *cp;
6505 char *start;
6506 char *endname;
6507 int metaflag;
6508 struct stat statb;
6509 DIR *dirp;
6510 struct dirent *dp;
6511 int atend;
6512 int matchdot;
6513
6514 metaflag = 0;
6515 start = name;
6516 for (p = name; *p; p++) {
6517 if (*p == '*' || *p == '?')
6518 metaflag = 1;
6519 else if (*p == '[') {
6520 char *q = p + 1;
6521 if (*q == '!')
6522 q++;
6523 for (;;) {
6524 if (*q == '\\')
6525 q++;
6526 if (*q == '/' || *q == '\0')
6527 break;
6528 if (*++q == ']') {
6529 metaflag = 1;
6530 break;
6531 }
6532 }
6533 } else if (*p == '\\')
6534 p++;
6535 else if (*p == '/') {
6536 if (metaflag)
6537 goto out;
6538 start = p + 1;
6539 }
6540 }
6541 out:
6542 if (metaflag == 0) { /* we've reached the end of the file name */
6543 if (enddir != expdir)
6544 metaflag++;
6545 p = name;
6546 do {
6547 if (*p == '\\')
6548 p++;
6549 *enddir++ = *p;
6550 } while (*p++);
6551 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6552 addfname(expdir);
6553 return;
6554 }
6555 endname = p;
6556 if (name < start) {
6557 p = name;
6558 do {
6559 if (*p == '\\')
6560 p++;
6561 *enddir++ = *p++;
6562 } while (p < start);
6563 }
6564 if (enddir == expdir) {
6565 cp = ".";
6566 } else if (enddir == expdir + 1 && *expdir == '/') {
6567 cp = "/";
6568 } else {
6569 cp = expdir;
6570 enddir[-1] = '\0';
6571 }
6572 dirp = opendir(cp);
6573 if (dirp == NULL)
6574 return;
6575 if (enddir != expdir)
6576 enddir[-1] = '/';
6577 if (*endname == 0) {
6578 atend = 1;
6579 } else {
6580 atend = 0;
6581 *endname++ = '\0';
6582 }
6583 matchdot = 0;
6584 p = start;
6585 if (*p == '\\')
6586 p++;
6587 if (*p == '.')
6588 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006589 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 if (dp->d_name[0] == '.' && ! matchdot)
6591 continue;
6592 if (pmatch(start, dp->d_name)) {
6593 if (atend) {
6594 strcpy(enddir, dp->d_name);
6595 addfname(expdir);
6596 } else {
6597 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6598 continue;
6599 p[-1] = '/';
6600 expmeta(p, endname);
6601 }
6602 }
6603 }
6604 closedir(dirp);
6605 if (! atend)
6606 endname[-1] = '/';
6607}
6608
6609static struct strlist *
6610msort(struct strlist *list, int len)
6611{
6612 struct strlist *p, *q = NULL;
6613 struct strlist **lpp;
6614 int half;
6615 int n;
6616
6617 if (len <= 1)
6618 return list;
6619 half = len >> 1;
6620 p = list;
6621 for (n = half; --n >= 0; ) {
6622 q = p;
6623 p = p->next;
6624 }
6625 q->next = NULL; /* terminate first half of list */
6626 q = msort(list, half); /* sort first half of list */
6627 p = msort(p, len - half); /* sort second half */
6628 lpp = &list;
6629 for (;;) {
6630#if ENABLE_LOCALE_SUPPORT
6631 if (strcoll(p->text, q->text) < 0)
6632#else
6633 if (strcmp(p->text, q->text) < 0)
6634#endif
6635 {
6636 *lpp = p;
6637 lpp = &p->next;
6638 p = *lpp;
6639 if (p == NULL) {
6640 *lpp = q;
6641 break;
6642 }
6643 } else {
6644 *lpp = q;
6645 lpp = &q->next;
6646 q = *lpp;
6647 if (q == NULL) {
6648 *lpp = p;
6649 break;
6650 }
6651 }
6652 }
6653 return list;
6654}
6655
6656/*
6657 * Sort the results of file name expansion. It calculates the number of
6658 * strings to sort and then calls msort (short for merge sort) to do the
6659 * work.
6660 */
6661static struct strlist *
6662expsort(struct strlist *str)
6663{
6664 int len;
6665 struct strlist *sp;
6666
6667 len = 0;
6668 for (sp = str; sp; sp = sp->next)
6669 len++;
6670 return msort(str, len);
6671}
6672
6673static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006674expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006675{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006676 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006677 '*', '?', '[', 0
6678 };
6679 /* TODO - EXP_REDIR */
6680
6681 while (str) {
6682 struct strlist **savelastp;
6683 struct strlist *sp;
6684 char *p;
6685
6686 if (fflag)
6687 goto nometa;
6688 if (!strpbrk(str->text, metachars))
6689 goto nometa;
6690 savelastp = exparg.lastp;
6691
6692 INT_OFF;
6693 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6694 {
6695 int i = strlen(str->text);
6696 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6697 }
6698
6699 expmeta(expdir, p);
6700 free(expdir);
6701 if (p != str->text)
6702 free(p);
6703 INT_ON;
6704 if (exparg.lastp == savelastp) {
6705 /*
6706 * no matches
6707 */
6708 nometa:
6709 *exparg.lastp = str;
6710 rmescapes(str->text);
6711 exparg.lastp = &str->next;
6712 } else {
6713 *exparg.lastp = NULL;
6714 *savelastp = sp = expsort(*savelastp);
6715 while (sp->next != NULL)
6716 sp = sp->next;
6717 exparg.lastp = &sp->next;
6718 }
6719 str = str->next;
6720 }
6721}
6722
6723/*
6724 * Perform variable substitution and command substitution on an argument,
6725 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6726 * perform splitting and file name expansion. When arglist is NULL, perform
6727 * here document expansion.
6728 */
6729static void
6730expandarg(union node *arg, struct arglist *arglist, int flag)
6731{
6732 struct strlist *sp;
6733 char *p;
6734
6735 argbackq = arg->narg.backquote;
6736 STARTSTACKSTR(expdest);
6737 ifsfirst.next = NULL;
6738 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006739 argstr(arg->narg.text, flag,
6740 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 p = _STPUTC('\0', expdest);
6742 expdest = p - 1;
6743 if (arglist == NULL) {
6744 return; /* here document expanded */
6745 }
6746 p = grabstackstr(p);
6747 exparg.lastp = &exparg.list;
6748 /*
6749 * TODO - EXP_REDIR
6750 */
6751 if (flag & EXP_FULL) {
6752 ifsbreakup(p, &exparg);
6753 *exparg.lastp = NULL;
6754 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006755 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006756 } else {
6757 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6758 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006759 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006760 sp->text = p;
6761 *exparg.lastp = sp;
6762 exparg.lastp = &sp->next;
6763 }
6764 if (ifsfirst.next)
6765 ifsfree();
6766 *exparg.lastp = NULL;
6767 if (exparg.list) {
6768 *arglist->lastp = exparg.list;
6769 arglist->lastp = exparg.lastp;
6770 }
6771}
6772
6773/*
6774 * Expand shell variables and backquotes inside a here document.
6775 */
6776static void
6777expandhere(union node *arg, int fd)
6778{
6779 herefd = fd;
6780 expandarg(arg, (struct arglist *)NULL, 0);
6781 full_write(fd, stackblock(), expdest - (char *)stackblock());
6782}
6783
6784/*
6785 * Returns true if the pattern matches the string.
6786 */
6787static int
6788patmatch(char *pattern, const char *string)
6789{
6790 return pmatch(preglob(pattern, 0, 0), string);
6791}
6792
6793/*
6794 * See if a pattern matches in a case statement.
6795 */
6796static int
6797casematch(union node *pattern, char *val)
6798{
6799 struct stackmark smark;
6800 int result;
6801
6802 setstackmark(&smark);
6803 argbackq = pattern->narg.backquote;
6804 STARTSTACKSTR(expdest);
6805 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006806 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6807 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006808 STACKSTRNUL(expdest);
6809 result = patmatch(stackblock(), val);
6810 popstackmark(&smark);
6811 return result;
6812}
6813
6814
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006815/* ============ find_command */
6816
6817struct builtincmd {
6818 const char *name;
6819 int (*builtin)(int, char **);
6820 /* unsigned flags; */
6821};
6822#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006823/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006824 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006825#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006826#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006827
6828struct cmdentry {
6829 int cmdtype;
6830 union param {
6831 int index;
6832 const struct builtincmd *cmd;
6833 struct funcnode *func;
6834 } u;
6835};
6836/* values of cmdtype */
6837#define CMDUNKNOWN -1 /* no entry in table for command */
6838#define CMDNORMAL 0 /* command is an executable program */
6839#define CMDFUNCTION 1 /* command is a shell function */
6840#define CMDBUILTIN 2 /* command is a shell builtin */
6841
6842/* action to find_command() */
6843#define DO_ERR 0x01 /* prints errors */
6844#define DO_ABS 0x02 /* checks absolute paths */
6845#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6846#define DO_ALTPATH 0x08 /* using alternate path */
6847#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6848
6849static void find_command(char *, struct cmdentry *, int, const char *);
6850
6851
6852/* ============ Hashing commands */
6853
6854/*
6855 * When commands are first encountered, they are entered in a hash table.
6856 * This ensures that a full path search will not have to be done for them
6857 * on each invocation.
6858 *
6859 * We should investigate converting to a linear search, even though that
6860 * would make the command name "hash" a misnomer.
6861 */
6862
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006863#define ARB 1 /* actual size determined at run time */
6864
6865struct tblentry {
6866 struct tblentry *next; /* next entry in hash chain */
6867 union param param; /* definition of builtin function */
6868 short cmdtype; /* index identifying command */
6869 char rehash; /* if set, cd done since entry created */
6870 char cmdname[ARB]; /* name of command */
6871};
6872
Denis Vlasenko01631112007-12-16 17:20:38 +00006873static struct tblentry **cmdtable;
6874#define INIT_G_cmdtable() do { \
6875 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6876} while (0)
6877
6878static int builtinloc = -1; /* index in path of %builtin, or -1 */
6879
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006880
6881static void
6882tryexec(char *cmd, char **argv, char **envp)
6883{
6884 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006885
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006886#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006887 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006888 int a = find_applet_by_name(cmd);
6889 if (a >= 0) {
6890 if (APPLET_IS_NOEXEC(a))
6891 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006892 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006893 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006894 /* If they called chroot or otherwise made the binary no longer
6895 * executable, fall through */
6896 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006897 }
6898#endif
6899
6900 repeat:
6901#ifdef SYSV
6902 do {
6903 execve(cmd, argv, envp);
6904 } while (errno == EINTR);
6905#else
6906 execve(cmd, argv, envp);
6907#endif
6908 if (repeated++) {
6909 free(argv);
6910 } else if (errno == ENOEXEC) {
6911 char **ap;
6912 char **new;
6913
6914 for (ap = argv; *ap; ap++)
6915 ;
6916 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6917 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006918 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006919 ap += 2;
6920 argv++;
6921 while ((*ap++ = *argv++))
Denis Vlasenko597906c2008-02-20 16:38:54 +00006922 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006923 argv = new;
6924 goto repeat;
6925 }
6926}
6927
6928/*
6929 * Exec a program. Never returns. If you change this routine, you may
6930 * have to change the find_command routine as well.
6931 */
6932#define environment() listvars(VEXPORT, VUNSET, 0)
6933static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6934static void
6935shellexec(char **argv, const char *path, int idx)
6936{
6937 char *cmdname;
6938 int e;
6939 char **envp;
6940 int exerrno;
6941
6942 clearredir(1);
6943 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006944 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006945#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006946 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006947#endif
6948 ) {
6949 tryexec(argv[0], argv, envp);
6950 e = errno;
6951 } else {
6952 e = ENOENT;
6953 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6954 if (--idx < 0 && pathopt == NULL) {
6955 tryexec(cmdname, argv, envp);
6956 if (errno != ENOENT && errno != ENOTDIR)
6957 e = errno;
6958 }
6959 stunalloc(cmdname);
6960 }
6961 }
6962
6963 /* Map to POSIX errors */
6964 switch (e) {
6965 case EACCES:
6966 exerrno = 126;
6967 break;
6968 case ENOENT:
6969 exerrno = 127;
6970 break;
6971 default:
6972 exerrno = 2;
6973 break;
6974 }
6975 exitstatus = exerrno;
6976 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6977 argv[0], e, suppressint ));
6978 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6979 /* NOTREACHED */
6980}
6981
6982static void
6983printentry(struct tblentry *cmdp)
6984{
6985 int idx;
6986 const char *path;
6987 char *name;
6988
6989 idx = cmdp->param.index;
6990 path = pathval();
6991 do {
6992 name = padvance(&path, cmdp->cmdname);
6993 stunalloc(name);
6994 } while (--idx >= 0);
6995 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6996}
6997
6998/*
6999 * Clear out command entries. The argument specifies the first entry in
7000 * PATH which has changed.
7001 */
7002static void
7003clearcmdentry(int firstchange)
7004{
7005 struct tblentry **tblp;
7006 struct tblentry **pp;
7007 struct tblentry *cmdp;
7008
7009 INT_OFF;
7010 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7011 pp = tblp;
7012 while ((cmdp = *pp) != NULL) {
7013 if ((cmdp->cmdtype == CMDNORMAL &&
7014 cmdp->param.index >= firstchange)
7015 || (cmdp->cmdtype == CMDBUILTIN &&
7016 builtinloc >= firstchange)
7017 ) {
7018 *pp = cmdp->next;
7019 free(cmdp);
7020 } else {
7021 pp = &cmdp->next;
7022 }
7023 }
7024 }
7025 INT_ON;
7026}
7027
7028/*
7029 * Locate a command in the command hash table. If "add" is nonzero,
7030 * add the command to the table if it is not already present. The
7031 * variable "lastcmdentry" is set to point to the address of the link
7032 * pointing to the entry, so that delete_cmd_entry can delete the
7033 * entry.
7034 *
7035 * Interrupts must be off if called with add != 0.
7036 */
7037static struct tblentry **lastcmdentry;
7038
7039static struct tblentry *
7040cmdlookup(const char *name, int add)
7041{
7042 unsigned int hashval;
7043 const char *p;
7044 struct tblentry *cmdp;
7045 struct tblentry **pp;
7046
7047 p = name;
7048 hashval = (unsigned char)*p << 4;
7049 while (*p)
7050 hashval += (unsigned char)*p++;
7051 hashval &= 0x7FFF;
7052 pp = &cmdtable[hashval % CMDTABLESIZE];
7053 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7054 if (strcmp(cmdp->cmdname, name) == 0)
7055 break;
7056 pp = &cmdp->next;
7057 }
7058 if (add && cmdp == NULL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00007059 cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007060 + strlen(name) + 1);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007061 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007062 cmdp->cmdtype = CMDUNKNOWN;
7063 strcpy(cmdp->cmdname, name);
7064 }
7065 lastcmdentry = pp;
7066 return cmdp;
7067}
7068
7069/*
7070 * Delete the command entry returned on the last lookup.
7071 */
7072static void
7073delete_cmd_entry(void)
7074{
7075 struct tblentry *cmdp;
7076
7077 INT_OFF;
7078 cmdp = *lastcmdentry;
7079 *lastcmdentry = cmdp->next;
7080 if (cmdp->cmdtype == CMDFUNCTION)
7081 freefunc(cmdp->param.func);
7082 free(cmdp);
7083 INT_ON;
7084}
7085
7086/*
7087 * Add a new command entry, replacing any existing command entry for
7088 * the same name - except special builtins.
7089 */
7090static void
7091addcmdentry(char *name, struct cmdentry *entry)
7092{
7093 struct tblentry *cmdp;
7094
7095 cmdp = cmdlookup(name, 1);
7096 if (cmdp->cmdtype == CMDFUNCTION) {
7097 freefunc(cmdp->param.func);
7098 }
7099 cmdp->cmdtype = entry->cmdtype;
7100 cmdp->param = entry->u;
7101 cmdp->rehash = 0;
7102}
7103
7104static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007105hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007106{
7107 struct tblentry **pp;
7108 struct tblentry *cmdp;
7109 int c;
7110 struct cmdentry entry;
7111 char *name;
7112
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007113 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007114 clearcmdentry(0);
7115 return 0;
7116 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007117
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007118 if (*argptr == NULL) {
7119 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7120 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7121 if (cmdp->cmdtype == CMDNORMAL)
7122 printentry(cmdp);
7123 }
7124 }
7125 return 0;
7126 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007127
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007128 c = 0;
7129 while ((name = *argptr) != NULL) {
7130 cmdp = cmdlookup(name, 0);
7131 if (cmdp != NULL
7132 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007133 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7134 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007135 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007136 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007137 find_command(name, &entry, DO_ERR, pathval());
7138 if (entry.cmdtype == CMDUNKNOWN)
7139 c = 1;
7140 argptr++;
7141 }
7142 return c;
7143}
7144
7145/*
7146 * Called when a cd is done. Marks all commands so the next time they
7147 * are executed they will be rehashed.
7148 */
7149static void
7150hashcd(void)
7151{
7152 struct tblentry **pp;
7153 struct tblentry *cmdp;
7154
7155 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7156 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007157 if (cmdp->cmdtype == CMDNORMAL
7158 || (cmdp->cmdtype == CMDBUILTIN
7159 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7160 && builtinloc > 0)
7161 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007162 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007163 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007164 }
7165 }
7166}
7167
7168/*
7169 * Fix command hash table when PATH changed.
7170 * Called before PATH is changed. The argument is the new value of PATH;
7171 * pathval() still returns the old value at this point.
7172 * Called with interrupts off.
7173 */
7174static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007175changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007176{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007177 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007178 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007179 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007180 int idx_bltin;
7181
7182 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007183 firstchange = 9999; /* assume no change */
7184 idx = 0;
7185 idx_bltin = -1;
7186 for (;;) {
7187 if (*old != *new) {
7188 firstchange = idx;
7189 if ((*old == '\0' && *new == ':')
7190 || (*old == ':' && *new == '\0'))
7191 firstchange++;
7192 old = new; /* ignore subsequent differences */
7193 }
7194 if (*new == '\0')
7195 break;
7196 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7197 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007198 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007199 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007200 new++, old++;
7201 }
7202 if (builtinloc < 0 && idx_bltin >= 0)
7203 builtinloc = idx_bltin; /* zap builtins */
7204 if (builtinloc >= 0 && idx_bltin < 0)
7205 firstchange = 0;
7206 clearcmdentry(firstchange);
7207 builtinloc = idx_bltin;
7208}
7209
7210#define TEOF 0
7211#define TNL 1
7212#define TREDIR 2
7213#define TWORD 3
7214#define TSEMI 4
7215#define TBACKGND 5
7216#define TAND 6
7217#define TOR 7
7218#define TPIPE 8
7219#define TLP 9
7220#define TRP 10
7221#define TENDCASE 11
7222#define TENDBQUOTE 12
7223#define TNOT 13
7224#define TCASE 14
7225#define TDO 15
7226#define TDONE 16
7227#define TELIF 17
7228#define TELSE 18
7229#define TESAC 19
7230#define TFI 20
7231#define TFOR 21
7232#define TIF 22
7233#define TIN 23
7234#define TTHEN 24
7235#define TUNTIL 25
7236#define TWHILE 26
7237#define TBEGIN 27
7238#define TEND 28
7239
7240/* first char is indicating which tokens mark the end of a list */
7241static const char *const tokname_array[] = {
7242 "\1end of file",
7243 "\0newline",
7244 "\0redirection",
7245 "\0word",
7246 "\0;",
7247 "\0&",
7248 "\0&&",
7249 "\0||",
7250 "\0|",
7251 "\0(",
7252 "\1)",
7253 "\1;;",
7254 "\1`",
7255#define KWDOFFSET 13
7256 /* the following are keywords */
7257 "\0!",
7258 "\0case",
7259 "\1do",
7260 "\1done",
7261 "\1elif",
7262 "\1else",
7263 "\1esac",
7264 "\1fi",
7265 "\0for",
7266 "\0if",
7267 "\0in",
7268 "\1then",
7269 "\0until",
7270 "\0while",
7271 "\0{",
7272 "\1}",
7273};
7274
7275static const char *
7276tokname(int tok)
7277{
7278 static char buf[16];
7279
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007280//try this:
7281//if (tok < TSEMI) return tokname_array[tok] + 1;
7282//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7283//return buf;
7284
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007285 if (tok >= TSEMI)
7286 buf[0] = '"';
7287 sprintf(buf + (tok >= TSEMI), "%s%c",
7288 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7289 return buf;
7290}
7291
7292/* Wrapper around strcmp for qsort/bsearch/... */
7293static int
7294pstrcmp(const void *a, const void *b)
7295{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007296 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007297}
7298
7299static const char *const *
7300findkwd(const char *s)
7301{
7302 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007303 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7304 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305}
7306
7307/*
7308 * Locate and print what a word is...
7309 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007310static int
7311describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007312{
7313 struct cmdentry entry;
7314 struct tblentry *cmdp;
7315#if ENABLE_ASH_ALIAS
7316 const struct alias *ap;
7317#endif
7318 const char *path = pathval();
7319
7320 if (describe_command_verbose) {
7321 out1str(command);
7322 }
7323
7324 /* First look at the keywords */
7325 if (findkwd(command)) {
7326 out1str(describe_command_verbose ? " is a shell keyword" : command);
7327 goto out;
7328 }
7329
7330#if ENABLE_ASH_ALIAS
7331 /* Then look at the aliases */
7332 ap = lookupalias(command, 0);
7333 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007334 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335 out1str("alias ");
7336 printalias(ap);
7337 return 0;
7338 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007339 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340 goto out;
7341 }
7342#endif
7343 /* Then check if it is a tracked alias */
7344 cmdp = cmdlookup(command, 0);
7345 if (cmdp != NULL) {
7346 entry.cmdtype = cmdp->cmdtype;
7347 entry.u = cmdp->param;
7348 } else {
7349 /* Finally use brute force */
7350 find_command(command, &entry, DO_ABS, path);
7351 }
7352
7353 switch (entry.cmdtype) {
7354 case CMDNORMAL: {
7355 int j = entry.u.index;
7356 char *p;
7357 if (j == -1) {
7358 p = command;
7359 } else {
7360 do {
7361 p = padvance(&path, command);
7362 stunalloc(p);
7363 } while (--j >= 0);
7364 }
7365 if (describe_command_verbose) {
7366 out1fmt(" is%s %s",
7367 (cmdp ? " a tracked alias for" : nullstr), p
7368 );
7369 } else {
7370 out1str(p);
7371 }
7372 break;
7373 }
7374
7375 case CMDFUNCTION:
7376 if (describe_command_verbose) {
7377 out1str(" is a shell function");
7378 } else {
7379 out1str(command);
7380 }
7381 break;
7382
7383 case CMDBUILTIN:
7384 if (describe_command_verbose) {
7385 out1fmt(" is a %sshell builtin",
7386 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7387 "special " : nullstr
7388 );
7389 } else {
7390 out1str(command);
7391 }
7392 break;
7393
7394 default:
7395 if (describe_command_verbose) {
7396 out1str(": not found\n");
7397 }
7398 return 127;
7399 }
7400 out:
7401 outstr("\n", stdout);
7402 return 0;
7403}
7404
7405static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007406typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007408 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007410 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411
Denis Vlasenko46846e22007-05-20 13:08:31 +00007412 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007413 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007414 i++;
7415 verbose = 0;
7416 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007417 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007418 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007419 }
7420 return err;
7421}
7422
7423#if ENABLE_ASH_CMDCMD
7424static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007425commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007426{
7427 int c;
7428 enum {
7429 VERIFY_BRIEF = 1,
7430 VERIFY_VERBOSE = 2,
7431 } verify = 0;
7432
7433 while ((c = nextopt("pvV")) != '\0')
7434 if (c == 'V')
7435 verify |= VERIFY_VERBOSE;
7436 else if (c == 'v')
7437 verify |= VERIFY_BRIEF;
7438#if DEBUG
7439 else if (c != 'p')
7440 abort();
7441#endif
7442 if (verify)
7443 return describe_command(*argptr, verify - VERIFY_BRIEF);
7444
7445 return 0;
7446}
7447#endif
7448
7449
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007450/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007451
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007452static int funcblocksize; /* size of structures in function */
7453static int funcstringsize; /* size of strings in node */
7454static void *funcblock; /* block to allocate function from */
7455static char *funcstring; /* block to allocate strings from */
7456
Eric Andersencb57d552001-06-28 07:25:16 +00007457/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007458#define EV_EXIT 01 /* exit after evaluating tree */
7459#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7460#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007461
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007462static const short nodesize[26] = {
7463 SHELL_ALIGN(sizeof(struct ncmd)),
7464 SHELL_ALIGN(sizeof(struct npipe)),
7465 SHELL_ALIGN(sizeof(struct nredir)),
7466 SHELL_ALIGN(sizeof(struct nredir)),
7467 SHELL_ALIGN(sizeof(struct nredir)),
7468 SHELL_ALIGN(sizeof(struct nbinary)),
7469 SHELL_ALIGN(sizeof(struct nbinary)),
7470 SHELL_ALIGN(sizeof(struct nbinary)),
7471 SHELL_ALIGN(sizeof(struct nif)),
7472 SHELL_ALIGN(sizeof(struct nbinary)),
7473 SHELL_ALIGN(sizeof(struct nbinary)),
7474 SHELL_ALIGN(sizeof(struct nfor)),
7475 SHELL_ALIGN(sizeof(struct ncase)),
7476 SHELL_ALIGN(sizeof(struct nclist)),
7477 SHELL_ALIGN(sizeof(struct narg)),
7478 SHELL_ALIGN(sizeof(struct narg)),
7479 SHELL_ALIGN(sizeof(struct nfile)),
7480 SHELL_ALIGN(sizeof(struct nfile)),
7481 SHELL_ALIGN(sizeof(struct nfile)),
7482 SHELL_ALIGN(sizeof(struct nfile)),
7483 SHELL_ALIGN(sizeof(struct nfile)),
7484 SHELL_ALIGN(sizeof(struct ndup)),
7485 SHELL_ALIGN(sizeof(struct ndup)),
7486 SHELL_ALIGN(sizeof(struct nhere)),
7487 SHELL_ALIGN(sizeof(struct nhere)),
7488 SHELL_ALIGN(sizeof(struct nnot)),
7489};
7490
7491static void calcsize(union node *n);
7492
7493static void
7494sizenodelist(struct nodelist *lp)
7495{
7496 while (lp) {
7497 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7498 calcsize(lp->n);
7499 lp = lp->next;
7500 }
7501}
7502
7503static void
7504calcsize(union node *n)
7505{
7506 if (n == NULL)
7507 return;
7508 funcblocksize += nodesize[n->type];
7509 switch (n->type) {
7510 case NCMD:
7511 calcsize(n->ncmd.redirect);
7512 calcsize(n->ncmd.args);
7513 calcsize(n->ncmd.assign);
7514 break;
7515 case NPIPE:
7516 sizenodelist(n->npipe.cmdlist);
7517 break;
7518 case NREDIR:
7519 case NBACKGND:
7520 case NSUBSHELL:
7521 calcsize(n->nredir.redirect);
7522 calcsize(n->nredir.n);
7523 break;
7524 case NAND:
7525 case NOR:
7526 case NSEMI:
7527 case NWHILE:
7528 case NUNTIL:
7529 calcsize(n->nbinary.ch2);
7530 calcsize(n->nbinary.ch1);
7531 break;
7532 case NIF:
7533 calcsize(n->nif.elsepart);
7534 calcsize(n->nif.ifpart);
7535 calcsize(n->nif.test);
7536 break;
7537 case NFOR:
7538 funcstringsize += strlen(n->nfor.var) + 1;
7539 calcsize(n->nfor.body);
7540 calcsize(n->nfor.args);
7541 break;
7542 case NCASE:
7543 calcsize(n->ncase.cases);
7544 calcsize(n->ncase.expr);
7545 break;
7546 case NCLIST:
7547 calcsize(n->nclist.body);
7548 calcsize(n->nclist.pattern);
7549 calcsize(n->nclist.next);
7550 break;
7551 case NDEFUN:
7552 case NARG:
7553 sizenodelist(n->narg.backquote);
7554 funcstringsize += strlen(n->narg.text) + 1;
7555 calcsize(n->narg.next);
7556 break;
7557 case NTO:
7558 case NCLOBBER:
7559 case NFROM:
7560 case NFROMTO:
7561 case NAPPEND:
7562 calcsize(n->nfile.fname);
7563 calcsize(n->nfile.next);
7564 break;
7565 case NTOFD:
7566 case NFROMFD:
7567 calcsize(n->ndup.vname);
7568 calcsize(n->ndup.next);
7569 break;
7570 case NHERE:
7571 case NXHERE:
7572 calcsize(n->nhere.doc);
7573 calcsize(n->nhere.next);
7574 break;
7575 case NNOT:
7576 calcsize(n->nnot.com);
7577 break;
7578 };
7579}
7580
7581static char *
7582nodeckstrdup(char *s)
7583{
7584 char *rtn = funcstring;
7585
7586 strcpy(funcstring, s);
7587 funcstring += strlen(s) + 1;
7588 return rtn;
7589}
7590
7591static union node *copynode(union node *);
7592
7593static struct nodelist *
7594copynodelist(struct nodelist *lp)
7595{
7596 struct nodelist *start;
7597 struct nodelist **lpp;
7598
7599 lpp = &start;
7600 while (lp) {
7601 *lpp = funcblock;
7602 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7603 (*lpp)->n = copynode(lp->n);
7604 lp = lp->next;
7605 lpp = &(*lpp)->next;
7606 }
7607 *lpp = NULL;
7608 return start;
7609}
7610
7611static union node *
7612copynode(union node *n)
7613{
7614 union node *new;
7615
7616 if (n == NULL)
7617 return NULL;
7618 new = funcblock;
7619 funcblock = (char *) funcblock + nodesize[n->type];
7620
7621 switch (n->type) {
7622 case NCMD:
7623 new->ncmd.redirect = copynode(n->ncmd.redirect);
7624 new->ncmd.args = copynode(n->ncmd.args);
7625 new->ncmd.assign = copynode(n->ncmd.assign);
7626 break;
7627 case NPIPE:
7628 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7629 new->npipe.backgnd = n->npipe.backgnd;
7630 break;
7631 case NREDIR:
7632 case NBACKGND:
7633 case NSUBSHELL:
7634 new->nredir.redirect = copynode(n->nredir.redirect);
7635 new->nredir.n = copynode(n->nredir.n);
7636 break;
7637 case NAND:
7638 case NOR:
7639 case NSEMI:
7640 case NWHILE:
7641 case NUNTIL:
7642 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7643 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7644 break;
7645 case NIF:
7646 new->nif.elsepart = copynode(n->nif.elsepart);
7647 new->nif.ifpart = copynode(n->nif.ifpart);
7648 new->nif.test = copynode(n->nif.test);
7649 break;
7650 case NFOR:
7651 new->nfor.var = nodeckstrdup(n->nfor.var);
7652 new->nfor.body = copynode(n->nfor.body);
7653 new->nfor.args = copynode(n->nfor.args);
7654 break;
7655 case NCASE:
7656 new->ncase.cases = copynode(n->ncase.cases);
7657 new->ncase.expr = copynode(n->ncase.expr);
7658 break;
7659 case NCLIST:
7660 new->nclist.body = copynode(n->nclist.body);
7661 new->nclist.pattern = copynode(n->nclist.pattern);
7662 new->nclist.next = copynode(n->nclist.next);
7663 break;
7664 case NDEFUN:
7665 case NARG:
7666 new->narg.backquote = copynodelist(n->narg.backquote);
7667 new->narg.text = nodeckstrdup(n->narg.text);
7668 new->narg.next = copynode(n->narg.next);
7669 break;
7670 case NTO:
7671 case NCLOBBER:
7672 case NFROM:
7673 case NFROMTO:
7674 case NAPPEND:
7675 new->nfile.fname = copynode(n->nfile.fname);
7676 new->nfile.fd = n->nfile.fd;
7677 new->nfile.next = copynode(n->nfile.next);
7678 break;
7679 case NTOFD:
7680 case NFROMFD:
7681 new->ndup.vname = copynode(n->ndup.vname);
7682 new->ndup.dupfd = n->ndup.dupfd;
7683 new->ndup.fd = n->ndup.fd;
7684 new->ndup.next = copynode(n->ndup.next);
7685 break;
7686 case NHERE:
7687 case NXHERE:
7688 new->nhere.doc = copynode(n->nhere.doc);
7689 new->nhere.fd = n->nhere.fd;
7690 new->nhere.next = copynode(n->nhere.next);
7691 break;
7692 case NNOT:
7693 new->nnot.com = copynode(n->nnot.com);
7694 break;
7695 };
7696 new->type = n->type;
7697 return new;
7698}
7699
7700/*
7701 * Make a copy of a parse tree.
7702 */
7703static struct funcnode *
7704copyfunc(union node *n)
7705{
7706 struct funcnode *f;
7707 size_t blocksize;
7708
7709 funcblocksize = offsetof(struct funcnode, n);
7710 funcstringsize = 0;
7711 calcsize(n);
7712 blocksize = funcblocksize;
7713 f = ckmalloc(blocksize + funcstringsize);
7714 funcblock = (char *) f + offsetof(struct funcnode, n);
7715 funcstring = (char *) f + blocksize;
7716 copynode(n);
7717 f->count = 0;
7718 return f;
7719}
7720
7721/*
7722 * Define a shell function.
7723 */
7724static void
7725defun(char *name, union node *func)
7726{
7727 struct cmdentry entry;
7728
7729 INT_OFF;
7730 entry.cmdtype = CMDFUNCTION;
7731 entry.u.func = copyfunc(func);
7732 addcmdentry(name, &entry);
7733 INT_ON;
7734}
7735
7736static int evalskip; /* set if we are skipping commands */
7737/* reasons for skipping commands (see comment on breakcmd routine) */
7738#define SKIPBREAK (1 << 0)
7739#define SKIPCONT (1 << 1)
7740#define SKIPFUNC (1 << 2)
7741#define SKIPFILE (1 << 3)
7742#define SKIPEVAL (1 << 4)
7743static int skipcount; /* number of levels to skip */
7744static int funcnest; /* depth of function calls */
7745
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007746/* forward decl way out to parsing code - dotrap needs it */
7747static int evalstring(char *s, int mask);
7748
7749/*
7750 * Called to execute a trap. Perhaps we should avoid entering new trap
7751 * handlers while we are executing a trap handler.
7752 */
7753static int
7754dotrap(void)
7755{
7756 char *p;
7757 char *q;
7758 int i;
7759 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007760 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007761
7762 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007763 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007764 xbarrier();
7765
7766 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7767 if (!*q)
7768 continue;
7769 *q = '\0';
7770
7771 p = trap[i + 1];
7772 if (!p)
7773 continue;
7774 skip = evalstring(p, SKIPEVAL);
7775 exitstatus = savestatus;
7776 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007777 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007778 }
7779
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007780 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007781}
7782
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007783/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007784static void evalloop(union node *, int);
7785static void evalfor(union node *, int);
7786static void evalcase(union node *, int);
7787static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007788static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007789static void evalpipe(union node *, int);
7790static void evalcommand(union node *, int);
7791static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007792static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007793
Eric Andersen62483552001-07-10 06:09:16 +00007794/*
Eric Andersenc470f442003-07-28 09:56:35 +00007795 * Evaluate a parse tree. The value is left in the global variable
7796 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007797 */
Eric Andersenc470f442003-07-28 09:56:35 +00007798static void
7799evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007800{
Eric Andersenc470f442003-07-28 09:56:35 +00007801 int checkexit = 0;
7802 void (*evalfn)(union node *, int);
7803 unsigned isor;
7804 int status;
7805 if (n == NULL) {
7806 TRACE(("evaltree(NULL) called\n"));
7807 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007808 }
Eric Andersenc470f442003-07-28 09:56:35 +00007809 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007810 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007811 switch (n->type) {
7812 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007813#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007814 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007815 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007816 break;
7817#endif
7818 case NNOT:
7819 evaltree(n->nnot.com, EV_TESTED);
7820 status = !exitstatus;
7821 goto setstatus;
7822 case NREDIR:
7823 expredir(n->nredir.redirect);
7824 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7825 if (!status) {
7826 evaltree(n->nredir.n, flags & EV_TESTED);
7827 status = exitstatus;
7828 }
7829 popredir(0);
7830 goto setstatus;
7831 case NCMD:
7832 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007833 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007834 if (eflag && !(flags & EV_TESTED))
7835 checkexit = ~0;
7836 goto calleval;
7837 case NFOR:
7838 evalfn = evalfor;
7839 goto calleval;
7840 case NWHILE:
7841 case NUNTIL:
7842 evalfn = evalloop;
7843 goto calleval;
7844 case NSUBSHELL:
7845 case NBACKGND:
7846 evalfn = evalsubshell;
7847 goto calleval;
7848 case NPIPE:
7849 evalfn = evalpipe;
7850 goto checkexit;
7851 case NCASE:
7852 evalfn = evalcase;
7853 goto calleval;
7854 case NAND:
7855 case NOR:
7856 case NSEMI:
7857#if NAND + 1 != NOR
7858#error NAND + 1 != NOR
7859#endif
7860#if NOR + 1 != NSEMI
7861#error NOR + 1 != NSEMI
7862#endif
7863 isor = n->type - NAND;
7864 evaltree(
7865 n->nbinary.ch1,
7866 (flags | ((isor >> 1) - 1)) & EV_TESTED
7867 );
7868 if (!exitstatus == isor)
7869 break;
7870 if (!evalskip) {
7871 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007872 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007873 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007874 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007875 evalfn(n, flags);
7876 break;
7877 }
7878 break;
7879 case NIF:
7880 evaltree(n->nif.test, EV_TESTED);
7881 if (evalskip)
7882 break;
7883 if (exitstatus == 0) {
7884 n = n->nif.ifpart;
7885 goto evaln;
7886 } else if (n->nif.elsepart) {
7887 n = n->nif.elsepart;
7888 goto evaln;
7889 }
7890 goto success;
7891 case NDEFUN:
7892 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007893 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007894 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007895 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007896 exitstatus = status;
7897 break;
7898 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007899 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007900 if ((checkexit & exitstatus))
7901 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007902 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007903 goto exexit;
7904
7905 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007906 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007907 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007908 }
Eric Andersen62483552001-07-10 06:09:16 +00007909}
7910
Eric Andersenc470f442003-07-28 09:56:35 +00007911#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7912static
7913#endif
7914void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7915
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007916static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007917
7918static void
7919evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007920{
7921 int status;
7922
7923 loopnest++;
7924 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007925 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007926 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007927 int i;
7928
Eric Andersencb57d552001-06-28 07:25:16 +00007929 evaltree(n->nbinary.ch1, EV_TESTED);
7930 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007931 skipping:
7932 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007933 evalskip = 0;
7934 continue;
7935 }
7936 if (evalskip == SKIPBREAK && --skipcount <= 0)
7937 evalskip = 0;
7938 break;
7939 }
Eric Andersenc470f442003-07-28 09:56:35 +00007940 i = exitstatus;
7941 if (n->type != NWHILE)
7942 i = !i;
7943 if (i != 0)
7944 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007945 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007946 status = exitstatus;
7947 if (evalskip)
7948 goto skipping;
7949 }
7950 loopnest--;
7951 exitstatus = status;
7952}
7953
Eric Andersenc470f442003-07-28 09:56:35 +00007954static void
7955evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007956{
7957 struct arglist arglist;
7958 union node *argp;
7959 struct strlist *sp;
7960 struct stackmark smark;
7961
7962 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007963 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007964 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007965 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007966 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007967 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007968 if (evalskip)
7969 goto out;
7970 }
7971 *arglist.lastp = NULL;
7972
7973 exitstatus = 0;
7974 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007975 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007976 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007977 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007978 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007979 if (evalskip) {
7980 if (evalskip == SKIPCONT && --skipcount <= 0) {
7981 evalskip = 0;
7982 continue;
7983 }
7984 if (evalskip == SKIPBREAK && --skipcount <= 0)
7985 evalskip = 0;
7986 break;
7987 }
7988 }
7989 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007990 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007991 popstackmark(&smark);
7992}
7993
Eric Andersenc470f442003-07-28 09:56:35 +00007994static void
7995evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007996{
7997 union node *cp;
7998 union node *patp;
7999 struct arglist arglist;
8000 struct stackmark smark;
8001
8002 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008003 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008004 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008005 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008006 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008007 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8008 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008009 if (casematch(patp, arglist.list->text)) {
8010 if (evalskip == 0) {
8011 evaltree(cp->nclist.body, flags);
8012 }
8013 goto out;
8014 }
8015 }
8016 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008017 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008018 popstackmark(&smark);
8019}
8020
Eric Andersenc470f442003-07-28 09:56:35 +00008021/*
8022 * Kick off a subshell to evaluate a tree.
8023 */
Eric Andersenc470f442003-07-28 09:56:35 +00008024static void
8025evalsubshell(union node *n, int flags)
8026{
8027 struct job *jp;
8028 int backgnd = (n->type == NBACKGND);
8029 int status;
8030
8031 expredir(n->nredir.redirect);
8032 if (!backgnd && flags & EV_EXIT && !trap[0])
8033 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008034 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008035 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008036 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008037 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008038 flags |= EV_EXIT;
8039 if (backgnd)
8040 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008041 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008042 redirect(n->nredir.redirect, 0);
8043 evaltreenr(n->nredir.n, flags);
8044 /* never returns */
8045 }
8046 status = 0;
8047 if (! backgnd)
8048 status = waitforjob(jp);
8049 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008050 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008051}
8052
Eric Andersenc470f442003-07-28 09:56:35 +00008053/*
8054 * Compute the names of the files in a redirection list.
8055 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008056static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008057static void
8058expredir(union node *n)
8059{
8060 union node *redir;
8061
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008062 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008063 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008064
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008065 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008066 fn.lastp = &fn.list;
8067 switch (redir->type) {
8068 case NFROMTO:
8069 case NFROM:
8070 case NTO:
8071 case NCLOBBER:
8072 case NAPPEND:
8073 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8074 redir->nfile.expfname = fn.list->text;
8075 break;
8076 case NFROMFD:
8077 case NTOFD:
8078 if (redir->ndup.vname) {
8079 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008080 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008081 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008082 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008083 }
8084 break;
8085 }
8086 }
8087}
8088
Eric Andersencb57d552001-06-28 07:25:16 +00008089/*
Eric Andersencb57d552001-06-28 07:25:16 +00008090 * Evaluate a pipeline. All the processes in the pipeline are children
8091 * of the process creating the pipeline. (This differs from some versions
8092 * of the shell, which make the last process in a pipeline the parent
8093 * of all the rest.)
8094 */
Eric Andersenc470f442003-07-28 09:56:35 +00008095static void
8096evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008097{
8098 struct job *jp;
8099 struct nodelist *lp;
8100 int pipelen;
8101 int prevfd;
8102 int pip[2];
8103
Eric Andersenc470f442003-07-28 09:56:35 +00008104 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008105 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008106 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008107 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008108 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008109 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008110 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008111 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008112 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008113 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008114 pip[1] = -1;
8115 if (lp->next) {
8116 if (pipe(pip) < 0) {
8117 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008118 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008119 }
8120 }
8121 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008122 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008123 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008124 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008125 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008126 if (prevfd > 0) {
8127 dup2(prevfd, 0);
8128 close(prevfd);
8129 }
8130 if (pip[1] > 1) {
8131 dup2(pip[1], 1);
8132 close(pip[1]);
8133 }
Eric Andersenc470f442003-07-28 09:56:35 +00008134 evaltreenr(lp->n, flags);
8135 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008136 }
8137 if (prevfd >= 0)
8138 close(prevfd);
8139 prevfd = pip[0];
8140 close(pip[1]);
8141 }
Eric Andersencb57d552001-06-28 07:25:16 +00008142 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008143 exitstatus = waitforjob(jp);
8144 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008145 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008146 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008147}
8148
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008149/*
8150 * Controls whether the shell is interactive or not.
8151 */
8152static void
8153setinteractive(int on)
8154{
8155 static int is_interactive;
8156
8157 if (++on == is_interactive)
8158 return;
8159 is_interactive = on;
8160 setsignal(SIGINT);
8161 setsignal(SIGQUIT);
8162 setsignal(SIGTERM);
8163#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8164 if (is_interactive > 1) {
8165 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008166 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008167
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008168 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008169 out1fmt(
8170 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008171 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008172 "Enter 'help' for a list of built-in commands."
8173 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008174 bb_banner);
8175 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008176 }
8177 }
8178#endif
8179}
8180
8181#if ENABLE_FEATURE_EDITING_VI
8182#define setvimode(on) do { \
8183 if (on) line_input_state->flags |= VI_MODE; \
8184 else line_input_state->flags &= ~VI_MODE; \
8185} while (0)
8186#else
8187#define setvimode(on) viflag = 0 /* forcibly keep the option off */
8188#endif
8189
8190static void
8191optschanged(void)
8192{
8193#if DEBUG
8194 opentrace();
8195#endif
8196 setinteractive(iflag);
8197 setjobctl(mflag);
8198 setvimode(viflag);
8199}
8200
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008201static struct localvar *localvars;
8202
8203/*
8204 * Called after a function returns.
8205 * Interrupts must be off.
8206 */
8207static void
8208poplocalvars(void)
8209{
8210 struct localvar *lvp;
8211 struct var *vp;
8212
8213 while ((lvp = localvars) != NULL) {
8214 localvars = lvp->next;
8215 vp = lvp->vp;
8216 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8217 if (vp == NULL) { /* $- saved */
8218 memcpy(optlist, lvp->text, sizeof(optlist));
8219 free((char*)lvp->text);
8220 optschanged();
8221 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8222 unsetvar(vp->text);
8223 } else {
8224 if (vp->func)
8225 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8226 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8227 free((char*)vp->text);
8228 vp->flags = lvp->flags;
8229 vp->text = lvp->text;
8230 }
8231 free(lvp);
8232 }
8233}
8234
8235static int
8236evalfun(struct funcnode *func, int argc, char **argv, int flags)
8237{
8238 volatile struct shparam saveparam;
8239 struct localvar *volatile savelocalvars;
8240 struct jmploc *volatile savehandler;
8241 struct jmploc jmploc;
8242 int e;
8243
8244 saveparam = shellparam;
8245 savelocalvars = localvars;
8246 e = setjmp(jmploc.loc);
8247 if (e) {
8248 goto funcdone;
8249 }
8250 INT_OFF;
8251 savehandler = exception_handler;
8252 exception_handler = &jmploc;
8253 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008254 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008255 func->count++;
8256 funcnest++;
8257 INT_ON;
8258 shellparam.nparam = argc - 1;
8259 shellparam.p = argv + 1;
8260#if ENABLE_ASH_GETOPTS
8261 shellparam.optind = 1;
8262 shellparam.optoff = -1;
8263#endif
8264 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008265 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008266 INT_OFF;
8267 funcnest--;
8268 freefunc(func);
8269 poplocalvars();
8270 localvars = savelocalvars;
8271 freeparam(&shellparam);
8272 shellparam = saveparam;
8273 exception_handler = savehandler;
8274 INT_ON;
8275 evalskip &= ~SKIPFUNC;
8276 return e;
8277}
8278
Denis Vlasenko131ae172007-02-18 13:00:19 +00008279#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008280static char **
8281parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008282{
8283 char *cp, c;
8284
8285 for (;;) {
8286 cp = *++argv;
8287 if (!cp)
8288 return 0;
8289 if (*cp++ != '-')
8290 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008291 c = *cp++;
8292 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008293 break;
8294 if (c == '-' && !*cp) {
8295 argv++;
8296 break;
8297 }
8298 do {
8299 switch (c) {
8300 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008301 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008302 break;
8303 default:
8304 /* run 'typecmd' for other options */
8305 return 0;
8306 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008307 c = *cp++;
8308 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008309 }
8310 return argv;
8311}
8312#endif
8313
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008314/*
8315 * Make a variable a local variable. When a variable is made local, it's
8316 * value and flags are saved in a localvar structure. The saved values
8317 * will be restored when the shell function returns. We handle the name
8318 * "-" as a special case.
8319 */
8320static void
8321mklocal(char *name)
8322{
8323 struct localvar *lvp;
8324 struct var **vpp;
8325 struct var *vp;
8326
8327 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008328 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008329 if (LONE_DASH(name)) {
8330 char *p;
8331 p = ckmalloc(sizeof(optlist));
8332 lvp->text = memcpy(p, optlist, sizeof(optlist));
8333 vp = NULL;
8334 } else {
8335 char *eq;
8336
8337 vpp = hashvar(name);
8338 vp = *findvar(vpp, name);
8339 eq = strchr(name, '=');
8340 if (vp == NULL) {
8341 if (eq)
8342 setvareq(name, VSTRFIXED);
8343 else
8344 setvar(name, NULL, VSTRFIXED);
8345 vp = *vpp; /* the new variable */
8346 lvp->flags = VUNSET;
8347 } else {
8348 lvp->text = vp->text;
8349 lvp->flags = vp->flags;
8350 vp->flags |= VSTRFIXED|VTEXTFIXED;
8351 if (eq)
8352 setvareq(name, 0);
8353 }
8354 }
8355 lvp->vp = vp;
8356 lvp->next = localvars;
8357 localvars = lvp;
8358 INT_ON;
8359}
8360
8361/*
8362 * The "local" command.
8363 */
8364static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008365localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008366{
8367 char *name;
8368
8369 argv = argptr;
8370 while ((name = *argv++) != NULL) {
8371 mklocal(name);
8372 }
8373 return 0;
8374}
8375
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008376static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008377falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008378{
8379 return 1;
8380}
8381
8382static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008383truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008384{
8385 return 0;
8386}
8387
8388static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008389execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008390{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008391 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008392 iflag = 0; /* exit on error */
8393 mflag = 0;
8394 optschanged();
8395 shellexec(argv + 1, pathval(), 0);
8396 }
8397 return 0;
8398}
8399
8400/*
8401 * The return command.
8402 */
8403static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008404returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008405{
8406 /*
8407 * If called outside a function, do what ksh does;
8408 * skip the rest of the file.
8409 */
8410 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8411 return argv[1] ? number(argv[1]) : exitstatus;
8412}
8413
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008414/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008416static int dotcmd(int, char **);
8417static int evalcmd(int, char **);
8418#if ENABLE_ASH_BUILTIN_ECHO
8419static int echocmd(int, char **);
8420#endif
8421#if ENABLE_ASH_BUILTIN_TEST
8422static int testcmd(int, char **);
8423#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008424static int exitcmd(int, char **);
8425static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008426#if ENABLE_ASH_GETOPTS
8427static int getoptscmd(int, char **);
8428#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008429#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8430static int helpcmd(int argc, char **argv);
8431#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008432#if ENABLE_ASH_MATH_SUPPORT
8433static int letcmd(int, char **);
8434#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008435static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008436static int setcmd(int, char **);
8437static int shiftcmd(int, char **);
8438static int timescmd(int, char **);
8439static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008440static int umaskcmd(int, char **);
8441static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008442static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008444#define BUILTIN_NOSPEC "0"
8445#define BUILTIN_SPECIAL "1"
8446#define BUILTIN_REGULAR "2"
8447#define BUILTIN_SPEC_REG "3"
8448#define BUILTIN_ASSIGN "4"
8449#define BUILTIN_SPEC_ASSG "5"
8450#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008451#define BUILTIN_SPEC_REG_ASSG "7"
8452
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008453/* make sure to keep these in proper order since it is searched via bsearch() */
8454static const struct builtincmd builtintab[] = {
8455 { BUILTIN_SPEC_REG ".", dotcmd },
8456 { BUILTIN_SPEC_REG ":", truecmd },
8457#if ENABLE_ASH_BUILTIN_TEST
8458 { BUILTIN_REGULAR "[", testcmd },
8459 { BUILTIN_REGULAR "[[", testcmd },
8460#endif
8461#if ENABLE_ASH_ALIAS
8462 { BUILTIN_REG_ASSG "alias", aliascmd },
8463#endif
8464#if JOBS
8465 { BUILTIN_REGULAR "bg", fg_bgcmd },
8466#endif
8467 { BUILTIN_SPEC_REG "break", breakcmd },
8468 { BUILTIN_REGULAR "cd", cdcmd },
8469 { BUILTIN_NOSPEC "chdir", cdcmd },
8470#if ENABLE_ASH_CMDCMD
8471 { BUILTIN_REGULAR "command", commandcmd },
8472#endif
8473 { BUILTIN_SPEC_REG "continue", breakcmd },
8474#if ENABLE_ASH_BUILTIN_ECHO
8475 { BUILTIN_REGULAR "echo", echocmd },
8476#endif
8477 { BUILTIN_SPEC_REG "eval", evalcmd },
8478 { BUILTIN_SPEC_REG "exec", execcmd },
8479 { BUILTIN_SPEC_REG "exit", exitcmd },
8480 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8481 { BUILTIN_REGULAR "false", falsecmd },
8482#if JOBS
8483 { BUILTIN_REGULAR "fg", fg_bgcmd },
8484#endif
8485#if ENABLE_ASH_GETOPTS
8486 { BUILTIN_REGULAR "getopts", getoptscmd },
8487#endif
8488 { BUILTIN_NOSPEC "hash", hashcmd },
8489#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8490 { BUILTIN_NOSPEC "help", helpcmd },
8491#endif
8492#if JOBS
8493 { BUILTIN_REGULAR "jobs", jobscmd },
8494 { BUILTIN_REGULAR "kill", killcmd },
8495#endif
8496#if ENABLE_ASH_MATH_SUPPORT
8497 { BUILTIN_NOSPEC "let", letcmd },
8498#endif
8499 { BUILTIN_ASSIGN "local", localcmd },
8500 { BUILTIN_NOSPEC "pwd", pwdcmd },
8501 { BUILTIN_REGULAR "read", readcmd },
8502 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8503 { BUILTIN_SPEC_REG "return", returncmd },
8504 { BUILTIN_SPEC_REG "set", setcmd },
8505 { BUILTIN_SPEC_REG "shift", shiftcmd },
8506 { BUILTIN_SPEC_REG "source", dotcmd },
8507#if ENABLE_ASH_BUILTIN_TEST
8508 { BUILTIN_REGULAR "test", testcmd },
8509#endif
8510 { BUILTIN_SPEC_REG "times", timescmd },
8511 { BUILTIN_SPEC_REG "trap", trapcmd },
8512 { BUILTIN_REGULAR "true", truecmd },
8513 { BUILTIN_NOSPEC "type", typecmd },
8514 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8515 { BUILTIN_REGULAR "umask", umaskcmd },
8516#if ENABLE_ASH_ALIAS
8517 { BUILTIN_REGULAR "unalias", unaliascmd },
8518#endif
8519 { BUILTIN_SPEC_REG "unset", unsetcmd },
8520 { BUILTIN_REGULAR "wait", waitcmd },
8521};
8522
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008523
8524#define COMMANDCMD (builtintab + 5 + \
8525 2 * ENABLE_ASH_BUILTIN_TEST + \
8526 ENABLE_ASH_ALIAS + \
8527 ENABLE_ASH_JOB_CONTROL)
8528#define EXECCMD (builtintab + 7 + \
8529 2 * ENABLE_ASH_BUILTIN_TEST + \
8530 ENABLE_ASH_ALIAS + \
8531 ENABLE_ASH_JOB_CONTROL + \
8532 ENABLE_ASH_CMDCMD + \
8533 ENABLE_ASH_BUILTIN_ECHO)
8534
8535/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008536 * Search the table of builtin commands.
8537 */
8538static struct builtincmd *
8539find_builtin(const char *name)
8540{
8541 struct builtincmd *bp;
8542
8543 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008544 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008545 pstrcmp
8546 );
8547 return bp;
8548}
8549
8550/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008551 * Execute a simple command.
8552 */
8553static int back_exitstatus; /* exit status of backquoted command */
8554static int
8555isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008556{
8557 const char *q = endofname(p);
8558 if (p == q)
8559 return 0;
8560 return *q == '=';
8561}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008562static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008563bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008564{
8565 /* Preserve exitstatus of a previous possible redirection
8566 * as POSIX mandates */
8567 return back_exitstatus;
8568}
Eric Andersenc470f442003-07-28 09:56:35 +00008569static void
8570evalcommand(union node *cmd, int flags)
8571{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008572 static const struct builtincmd null_bltin = {
8573 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008574 };
Eric Andersenc470f442003-07-28 09:56:35 +00008575 struct stackmark smark;
8576 union node *argp;
8577 struct arglist arglist;
8578 struct arglist varlist;
8579 char **argv;
8580 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008581 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008582 struct cmdentry cmdentry;
8583 struct job *jp;
8584 char *lastarg;
8585 const char *path;
8586 int spclbltin;
8587 int cmd_is_exec;
8588 int status;
8589 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008590 struct builtincmd *bcmd;
8591 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008592
8593 /* First expand the arguments. */
8594 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8595 setstackmark(&smark);
8596 back_exitstatus = 0;
8597
8598 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008599 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008600 varlist.lastp = &varlist.list;
8601 *varlist.lastp = NULL;
8602 arglist.lastp = &arglist.list;
8603 *arglist.lastp = NULL;
8604
8605 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008606 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008607 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8608 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8609 }
8610
Eric Andersenc470f442003-07-28 09:56:35 +00008611 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8612 struct strlist **spp;
8613
8614 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008615 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008616 expandarg(argp, &arglist, EXP_VARTILDE);
8617 else
8618 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8619
Eric Andersenc470f442003-07-28 09:56:35 +00008620 for (sp = *spp; sp; sp = sp->next)
8621 argc++;
8622 }
8623
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008624 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008625 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008626 TRACE(("evalcommand arg: %s\n", sp->text));
8627 *nargv++ = sp->text;
8628 }
8629 *nargv = NULL;
8630
8631 lastarg = NULL;
8632 if (iflag && funcnest == 0 && argc > 0)
8633 lastarg = nargv[-1];
8634
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008635 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008636 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008637 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008638
8639 path = vpath.text;
8640 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8641 struct strlist **spp;
8642 char *p;
8643
8644 spp = varlist.lastp;
8645 expandarg(argp, &varlist, EXP_VARTILDE);
8646
8647 /*
8648 * Modify the command lookup path, if a PATH= assignment
8649 * is present
8650 */
8651 p = (*spp)->text;
8652 if (varequal(p, path))
8653 path = p;
8654 }
8655
8656 /* Print the command if xflag is set. */
8657 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008658 int n;
8659 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008660
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008661 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008662 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008663
8664 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008665 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008666 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008667 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008668 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008669 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008670 p--;
8671 }
8672 }
8673 sp = arglist.list;
8674 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008675 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008676 }
8677
8678 cmd_is_exec = 0;
8679 spclbltin = -1;
8680
8681 /* Now locate the command. */
8682 if (argc) {
8683 const char *oldpath;
8684 int cmd_flag = DO_ERR;
8685
8686 path += 5;
8687 oldpath = path;
8688 for (;;) {
8689 find_command(argv[0], &cmdentry, cmd_flag, path);
8690 if (cmdentry.cmdtype == CMDUNKNOWN) {
8691 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008692 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008693 goto bail;
8694 }
8695
8696 /* implement bltin and command here */
8697 if (cmdentry.cmdtype != CMDBUILTIN)
8698 break;
8699 if (spclbltin < 0)
8700 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8701 if (cmdentry.u.cmd == EXECCMD)
8702 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008703#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008704 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008705 path = oldpath;
8706 nargv = parse_command_args(argv, &path);
8707 if (!nargv)
8708 break;
8709 argc -= nargv - argv;
8710 argv = nargv;
8711 cmd_flag |= DO_NOFUNC;
8712 } else
8713#endif
8714 break;
8715 }
8716 }
8717
8718 if (status) {
8719 /* We have a redirection error. */
8720 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008721 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008722 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008723 exitstatus = status;
8724 goto out;
8725 }
8726
8727 /* Execute the command. */
8728 switch (cmdentry.cmdtype) {
8729 default:
8730 /* Fork off a child process if necessary. */
8731 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008732 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008733 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008734 if (forkshell(jp, cmd, FORK_FG) != 0) {
8735 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008736 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008737 break;
8738 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008739 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008740 }
8741 listsetvar(varlist.list, VEXPORT|VSTACK);
8742 shellexec(argv, path, cmdentry.u.index);
8743 /* NOTREACHED */
8744
8745 case CMDBUILTIN:
8746 cmdenviron = varlist.list;
8747 if (cmdenviron) {
8748 struct strlist *list = cmdenviron;
8749 int i = VNOSET;
8750 if (spclbltin > 0 || argc == 0) {
8751 i = 0;
8752 if (cmd_is_exec && argc > 1)
8753 i = VEXPORT;
8754 }
8755 listsetvar(list, i);
8756 }
8757 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8758 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008759 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008760 if (i == EXEXIT)
8761 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008762 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008763 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008764 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008765 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008766 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008767 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008768 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008769 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008770 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008771 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008772 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008773 }
8774 break;
8775
8776 case CMDFUNCTION:
8777 listsetvar(varlist.list, 0);
8778 if (evalfun(cmdentry.u.func, argc, argv, flags))
8779 goto raise;
8780 break;
8781 }
8782
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008783 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008784 popredir(cmd_is_exec);
8785 if (lastarg)
8786 /* dsl: I think this is intended to be used to support
8787 * '_' in 'vi' command mode during line editing...
8788 * However I implemented that within libedit itself.
8789 */
8790 setvar("_", lastarg, 0);
8791 popstackmark(&smark);
8792}
8793
8794static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008795evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8796{
Eric Andersenc470f442003-07-28 09:56:35 +00008797 char *volatile savecmdname;
8798 struct jmploc *volatile savehandler;
8799 struct jmploc jmploc;
8800 int i;
8801
8802 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008803 i = setjmp(jmploc.loc);
8804 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008805 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008806 savehandler = exception_handler;
8807 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008808 commandname = argv[0];
8809 argptr = argv + 1;
8810 optptr = NULL; /* initialize nextopt */
8811 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008812 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008813 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008814 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008815 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008816 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008817// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008818 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008819
8820 return i;
8821}
8822
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008823static int
8824goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008825{
8826 return !*endofname(p);
8827}
8828
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008829
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008830/*
8831 * Search for a command. This is called before we fork so that the
8832 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008833 * the child. The check for "goodname" is an overly conservative
8834 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008835 */
Eric Andersenc470f442003-07-28 09:56:35 +00008836static void
8837prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008838{
8839 struct cmdentry entry;
8840
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008841 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8842 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008843}
8844
Eric Andersencb57d552001-06-28 07:25:16 +00008845
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008846/* ============ Builtin commands
8847 *
8848 * Builtin commands whose functions are closely tied to evaluation
8849 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008850 */
8851
8852/*
Eric Andersencb57d552001-06-28 07:25:16 +00008853 * Handle break and continue commands. Break, continue, and return are
8854 * all handled by setting the evalskip flag. The evaluation routines
8855 * above all check this flag, and if it is set they start skipping
8856 * commands rather than executing them. The variable skipcount is
8857 * the number of loops to break/continue, or the number of function
8858 * levels to return. (The latter is always 1.) It should probably
8859 * be an error to break out of more loops than exist, but it isn't
8860 * in the standard shell so we don't make it one here.
8861 */
Eric Andersenc470f442003-07-28 09:56:35 +00008862static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008863breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008864{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008865 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008866
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008867 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008868 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008869 if (n > loopnest)
8870 n = loopnest;
8871 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008872 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008873 skipcount = n;
8874 }
8875 return 0;
8876}
8877
Eric Andersenc470f442003-07-28 09:56:35 +00008878
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008879/* ============ input.c
8880 *
Eric Andersen90898442003-08-06 11:20:52 +00008881 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008882 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008883
Eric Andersenc470f442003-07-28 09:56:35 +00008884#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008885
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008886enum {
8887 INPUT_PUSH_FILE = 1,
8888 INPUT_NOFILE_OK = 2,
8889};
Eric Andersencb57d552001-06-28 07:25:16 +00008890
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008891static int plinno = 1; /* input line number */
8892/* number of characters left in input buffer */
8893static int parsenleft; /* copy of parsefile->nleft */
8894static int parselleft; /* copy of parsefile->lleft */
8895/* next character in input buffer */
8896static char *parsenextc; /* copy of parsefile->nextc */
8897
8898static int checkkwd;
8899/* values of checkkwd variable */
8900#define CHKALIAS 0x1
8901#define CHKKWD 0x2
8902#define CHKNL 0x4
8903
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008904static void
8905popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008906{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008907 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008908
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008909 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008910#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008911 if (sp->ap) {
8912 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8913 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008914 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008915 if (sp->string != sp->ap->val) {
8916 free(sp->string);
8917 }
8918 sp->ap->flag &= ~ALIASINUSE;
8919 if (sp->ap->flag & ALIASDEAD) {
8920 unalias(sp->ap->name);
8921 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008922 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008923#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008924 parsenextc = sp->prevstring;
8925 parsenleft = sp->prevnleft;
8926/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8927 parsefile->strpush = sp->prev;
8928 if (sp != &(parsefile->basestrpush))
8929 free(sp);
8930 INT_ON;
8931}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008932
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008933static int
8934preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008935{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008936 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008937 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008938 parsenextc = buf;
8939
Denis Vlasenko38f63192007-01-22 09:03:07 +00008940#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008941 retry:
Eric Andersenc470f442003-07-28 09:56:35 +00008942 if (!iflag || parsefile->fd)
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008943 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008944 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008945#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008946 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008947#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008948 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8949 if (nr == 0) {
8950 /* Ctrl+C pressed */
8951 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008952 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008953 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008954 raise(SIGINT);
8955 return 1;
8956 }
Eric Andersenc470f442003-07-28 09:56:35 +00008957 goto retry;
8958 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008959 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008960 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008961 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008962 }
Eric Andersencb57d552001-06-28 07:25:16 +00008963 }
8964#else
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008965 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008966#endif
8967
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008968#if 0
8969/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008970 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008971 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008972 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008973 if (flags >= 0 && (flags & O_NONBLOCK)) {
8974 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008975 if (fcntl(0, F_SETFL, flags) >= 0) {
8976 out2str("sh: turning off NDELAY mode\n");
8977 goto retry;
8978 }
8979 }
8980 }
8981 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008982#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008983 return nr;
8984}
8985
8986/*
8987 * Refill the input buffer and return the next input character:
8988 *
8989 * 1) If a string was pushed back on the input, pop it;
8990 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8991 * from a string so we can't refill the buffer, return EOF.
8992 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8993 * 4) Process input up to the next newline, deleting nul characters.
8994 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008995static int
Eric Andersenc470f442003-07-28 09:56:35 +00008996preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008997{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008998 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008999 int more;
9000 char savec;
9001
9002 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009003#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00009004 if (parsenleft == -1 && parsefile->strpush->ap &&
9005 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009006 return PEOA;
9007 }
Eric Andersen2870d962001-07-02 17:27:21 +00009008#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009009 popstring();
9010 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009011 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009012 }
9013 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
9014 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009015 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009016
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009017 more = parselleft;
9018 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009019 again:
9020 more = preadfd();
9021 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009022 parselleft = parsenleft = EOF_NLEFT;
9023 return PEOF;
9024 }
9025 }
9026
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009027 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009028
9029 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009030 for (;;) {
9031 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009032
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009033 more--;
9034 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009035
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009036 if (!c)
9037 memmove(q, q + 1, more);
9038 else {
9039 q++;
9040 if (c == '\n') {
9041 parsenleft = q - parsenextc - 1;
9042 break;
9043 }
Eric Andersencb57d552001-06-28 07:25:16 +00009044 }
9045
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009046 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009047 parsenleft = q - parsenextc - 1;
9048 if (parsenleft < 0)
9049 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009050 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009051 }
9052 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009053 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009054
9055 savec = *q;
9056 *q = '\0';
9057
9058 if (vflag) {
9059 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009060 }
9061
9062 *q = savec;
9063
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009064 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009065}
9066
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009067#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009068static int
9069pgetc(void)
9070{
9071 return pgetc_as_macro();
9072}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009073
9074#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9075#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009076#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009077#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009078#endif
9079
9080/*
9081 * Same as pgetc(), but ignores PEOA.
9082 */
9083#if ENABLE_ASH_ALIAS
9084static int
9085pgetc2(void)
9086{
9087 int c;
9088
9089 do {
9090 c = pgetc_macro();
9091 } while (c == PEOA);
9092 return c;
9093}
9094#else
9095static int
9096pgetc2(void)
9097{
9098 return pgetc_macro();
9099}
9100#endif
9101
9102/*
9103 * Read a line from the script.
9104 */
9105static char *
9106pfgets(char *line, int len)
9107{
9108 char *p = line;
9109 int nleft = len;
9110 int c;
9111
9112 while (--nleft > 0) {
9113 c = pgetc2();
9114 if (c == PEOF) {
9115 if (p == line)
9116 return NULL;
9117 break;
9118 }
9119 *p++ = c;
9120 if (c == '\n')
9121 break;
9122 }
9123 *p = '\0';
9124 return line;
9125}
9126
Eric Andersenc470f442003-07-28 09:56:35 +00009127/*
9128 * Undo the last call to pgetc. Only one character may be pushed back.
9129 * PEOF may be pushed back.
9130 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009131static void
Eric Andersenc470f442003-07-28 09:56:35 +00009132pungetc(void)
9133{
9134 parsenleft++;
9135 parsenextc--;
9136}
Eric Andersencb57d552001-06-28 07:25:16 +00009137
9138/*
9139 * Push a string back onto the input at this current parsefile level.
9140 * We handle aliases this way.
9141 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009142#if !ENABLE_ASH_ALIAS
9143#define pushstring(s, ap) pushstring(s)
9144#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009145static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009146pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009147{
Eric Andersencb57d552001-06-28 07:25:16 +00009148 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009149 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009150
Eric Andersenc470f442003-07-28 09:56:35 +00009151 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009152 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009153/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
9154 if (parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009155 sp = ckzalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00009156 sp->prev = parsefile->strpush;
9157 parsefile->strpush = sp;
9158 } else
9159 sp = parsefile->strpush = &(parsefile->basestrpush);
9160 sp->prevstring = parsenextc;
9161 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009162#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009163 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009164 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009165 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009166 sp->string = s;
9167 }
Eric Andersen2870d962001-07-02 17:27:21 +00009168#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009169 parsenextc = s;
9170 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009171 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009172}
9173
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009174/*
9175 * To handle the "." command, a stack of input files is used. Pushfile
9176 * adds a new entry to the stack and popfile restores the previous level.
9177 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009178static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009179pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009180{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009181 struct parsefile *pf;
9182
9183 parsefile->nleft = parsenleft;
9184 parsefile->lleft = parselleft;
9185 parsefile->nextc = parsenextc;
9186 parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009187 pf = ckzalloc(sizeof(*pf));
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009188 pf->prev = parsefile;
9189 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009190 /*pf->strpush = NULL; - ckzalloc did it */
9191 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009192 parsefile = pf;
9193}
9194
9195static void
9196popfile(void)
9197{
9198 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009199
Denis Vlasenkob012b102007-02-19 22:43:01 +00009200 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009201 if (pf->fd >= 0)
9202 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009203 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009204 while (pf->strpush)
9205 popstring();
9206 parsefile = pf->prev;
9207 free(pf);
9208 parsenleft = parsefile->nleft;
9209 parselleft = parsefile->lleft;
9210 parsenextc = parsefile->nextc;
9211 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009212 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009213}
9214
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215/*
9216 * Return to top level.
9217 */
9218static void
9219popallfiles(void)
9220{
9221 while (parsefile != &basepf)
9222 popfile();
9223}
9224
9225/*
9226 * Close the file(s) that the shell is reading commands from. Called
9227 * after a fork is done.
9228 */
9229static void
9230closescript(void)
9231{
9232 popallfiles();
9233 if (parsefile->fd > 0) {
9234 close(parsefile->fd);
9235 parsefile->fd = 0;
9236 }
9237}
9238
9239/*
9240 * Like setinputfile, but takes an open file descriptor. Call this with
9241 * interrupts off.
9242 */
9243static void
9244setinputfd(int fd, int push)
9245{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009246 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009247 if (push) {
9248 pushfile();
9249 parsefile->buf = 0;
9250 }
9251 parsefile->fd = fd;
9252 if (parsefile->buf == NULL)
9253 parsefile->buf = ckmalloc(IBUFSIZ);
9254 parselleft = parsenleft = 0;
9255 plinno = 1;
9256}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009257
Eric Andersenc470f442003-07-28 09:56:35 +00009258/*
9259 * Set the input to take input from a file. If push is set, push the
9260 * old input onto the stack first.
9261 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009262static int
9263setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009264{
9265 int fd;
9266 int fd2;
9267
Denis Vlasenkob012b102007-02-19 22:43:01 +00009268 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009269 fd = open(fname, O_RDONLY);
9270 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009271 if (flags & INPUT_NOFILE_OK)
9272 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009273 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009274 }
Eric Andersenc470f442003-07-28 09:56:35 +00009275 if (fd < 10) {
9276 fd2 = copyfd(fd, 10);
9277 close(fd);
9278 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009279 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009280 fd = fd2;
9281 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009282 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009283 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009284 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009285 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009286}
9287
Eric Andersencb57d552001-06-28 07:25:16 +00009288/*
9289 * Like setinputfile, but takes input from a string.
9290 */
Eric Andersenc470f442003-07-28 09:56:35 +00009291static void
9292setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009293{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009294 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009295 pushfile();
9296 parsenextc = string;
9297 parsenleft = strlen(string);
9298 parsefile->buf = NULL;
9299 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009300 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009301}
9302
9303
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009304/* ============ mail.c
9305 *
9306 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009307 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009308
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009309#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009310
Eric Andersencb57d552001-06-28 07:25:16 +00009311#define MAXMBOXES 10
9312
Eric Andersenc470f442003-07-28 09:56:35 +00009313/* times of mailboxes */
9314static time_t mailtime[MAXMBOXES];
9315/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009316static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009317
Eric Andersencb57d552001-06-28 07:25:16 +00009318/*
Eric Andersenc470f442003-07-28 09:56:35 +00009319 * Print appropriate message(s) if mail has arrived.
9320 * If mail_var_path_changed is set,
9321 * then the value of MAIL has mail_var_path_changed,
9322 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009323 */
Eric Andersenc470f442003-07-28 09:56:35 +00009324static void
9325chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009326{
Eric Andersencb57d552001-06-28 07:25:16 +00009327 const char *mpath;
9328 char *p;
9329 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009330 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009331 struct stackmark smark;
9332 struct stat statb;
9333
Eric Andersencb57d552001-06-28 07:25:16 +00009334 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009335 mpath = mpathset() ? mpathval() : mailval();
9336 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009337 p = padvance(&mpath, nullstr);
9338 if (p == NULL)
9339 break;
9340 if (*p == '\0')
9341 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009342 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009343#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009344 if (q[-1] != '/')
9345 abort();
9346#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009347 q[-1] = '\0'; /* delete trailing '/' */
9348 if (stat(p, &statb) < 0) {
9349 *mtp = 0;
9350 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009351 }
Eric Andersenc470f442003-07-28 09:56:35 +00009352 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9353 fprintf(
9354 stderr, snlfmt,
9355 pathopt ? pathopt : "you have mail"
9356 );
9357 }
9358 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009359 }
Eric Andersenc470f442003-07-28 09:56:35 +00009360 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009361 popstackmark(&smark);
9362}
Eric Andersencb57d552001-06-28 07:25:16 +00009363
Eric Andersenc470f442003-07-28 09:56:35 +00009364static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009365changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009366{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009367 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009368}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009369
Denis Vlasenko131ae172007-02-18 13:00:19 +00009370#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009371
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009372
9373/* ============ ??? */
9374
Eric Andersencb57d552001-06-28 07:25:16 +00009375/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009376 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009377 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009378static void
9379setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009380{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009381 char **newparam;
9382 char **ap;
9383 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009384
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009385 for (nparam = 0; argv[nparam]; nparam++);
9386 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9387 while (*argv) {
9388 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009389 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009390 *ap = NULL;
9391 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009392 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009393 shellparam.nparam = nparam;
9394 shellparam.p = newparam;
9395#if ENABLE_ASH_GETOPTS
9396 shellparam.optind = 1;
9397 shellparam.optoff = -1;
9398#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009399}
9400
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009401/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009402 * Process shell options. The global variable argptr contains a pointer
9403 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009404 *
9405 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9406 * For a non-interactive shell, an error condition encountered
9407 * by a special built-in ... shall cause the shell to write a diagnostic message
9408 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009409 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009410 * ...
9411 * Utility syntax error (option or operand error) Shall exit
9412 * ...
9413 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9414 * we see that bash does not do that (set "finishes" with error code 1 instead,
9415 * and shell continues), and people rely on this behavior!
9416 * Testcase:
9417 * set -o barfoo 2>/dev/null
9418 * echo $?
9419 *
9420 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009421 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009422static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009423minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009424{
9425 int i;
9426
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009427 if (name) {
9428 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009429 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009430 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009431 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009432 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009433 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009434 ash_msg("illegal option -o %s", name);
9435 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009436 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009437 out1str("Current option settings\n");
9438 for (i = 0; i < NOPTS; i++)
9439 out1fmt("%-16s%s\n", optnames(i),
9440 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009441 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009442}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009443static void
9444setoption(int flag, int val)
9445{
9446 int i;
9447
9448 for (i = 0; i < NOPTS; i++) {
9449 if (optletters(i) == flag) {
9450 optlist[i] = val;
9451 return;
9452 }
9453 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009454 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009455 /* NOTREACHED */
9456}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009457static int
Eric Andersenc470f442003-07-28 09:56:35 +00009458options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009459{
9460 char *p;
9461 int val;
9462 int c;
9463
9464 if (cmdline)
9465 minusc = NULL;
9466 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009467 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009468 if (c != '-' && c != '+')
9469 break;
9470 argptr++;
9471 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009472 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009473 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009474 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009475 if (!cmdline) {
9476 /* "-" means turn off -x and -v */
9477 if (p[0] == '\0')
9478 xflag = vflag = 0;
9479 /* "--" means reset params */
9480 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009481 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009482 }
Eric Andersenc470f442003-07-28 09:56:35 +00009483 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009484 }
Eric Andersencb57d552001-06-28 07:25:16 +00009485 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009486 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009487 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009488 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009489 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009490 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009491 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009492 if (minus_o(*argptr, val)) {
9493 /* it already printed err message */
9494 return 1; /* error */
9495 }
Eric Andersencb57d552001-06-28 07:25:16 +00009496 if (*argptr)
9497 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009498 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9499 isloginsh = 1;
9500 /* bash does not accept +-login, we also won't */
9501 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009502 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009503 isloginsh = 1;
9504 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009505 } else {
9506 setoption(c, val);
9507 }
9508 }
9509 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009510 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009511}
9512
Eric Andersencb57d552001-06-28 07:25:16 +00009513/*
Eric Andersencb57d552001-06-28 07:25:16 +00009514 * The shift builtin command.
9515 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009516static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009517shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009518{
9519 int n;
9520 char **ap1, **ap2;
9521
9522 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009523 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009524 n = number(argv[1]);
9525 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009526 ash_msg_and_raise_error("can't shift that many");
9527 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009528 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009529 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009530 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009531 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009532 }
9533 ap2 = shellparam.p;
9534 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009535#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009536 shellparam.optind = 1;
9537 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009538#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009539 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009540 return 0;
9541}
9542
Eric Andersencb57d552001-06-28 07:25:16 +00009543/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009544 * POSIX requires that 'set' (but not export or readonly) output the
9545 * variables in lexicographic order - by the locale's collating order (sigh).
9546 * Maybe we could keep them in an ordered balanced binary tree
9547 * instead of hashed lists.
9548 * For now just roll 'em through qsort for printing...
9549 */
9550static int
9551showvars(const char *sep_prefix, int on, int off)
9552{
9553 const char *sep;
9554 char **ep, **epend;
9555
9556 ep = listvars(on, off, &epend);
9557 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9558
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009559 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009560
9561 for (; ep < epend; ep++) {
9562 const char *p;
9563 const char *q;
9564
9565 p = strchrnul(*ep, '=');
9566 q = nullstr;
9567 if (*p)
9568 q = single_quote(++p);
9569 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9570 }
9571 return 0;
9572}
9573
9574/*
Eric Andersencb57d552001-06-28 07:25:16 +00009575 * The set command builtin.
9576 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009577static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009578setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009579{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009580 int retval;
9581
Denis Vlasenko68404f12008-03-17 09:00:54 +00009582 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009583 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009584 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009585 retval = 1;
9586 if (!options(0)) { /* if no parse error... */
9587 retval = 0;
9588 optschanged();
9589 if (*argptr != NULL) {
9590 setparam(argptr);
9591 }
Eric Andersencb57d552001-06-28 07:25:16 +00009592 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009593 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009594 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009595}
9596
Denis Vlasenko131ae172007-02-18 13:00:19 +00009597#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009598/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009599static void
9600change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009601{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009602 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009603 /* "get", generate */
9604 char buf[16];
9605
9606 rseed = rseed * 1103515245 + 12345;
9607 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9608 /* set without recursion */
9609 setvar(vrandom.text, buf, VNOFUNC);
9610 vrandom.flags &= ~VNOFUNC;
9611 } else {
9612 /* set/reset */
9613 rseed = strtoul(value, (char **)NULL, 10);
9614 }
Eric Andersenef02f822004-03-11 13:34:24 +00009615}
Eric Andersen16767e22004-03-16 05:14:10 +00009616#endif
9617
Denis Vlasenko131ae172007-02-18 13:00:19 +00009618#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009619static int
Eric Andersenc470f442003-07-28 09:56:35 +00009620getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009621{
9622 char *p, *q;
9623 char c = '?';
9624 int done = 0;
9625 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009626 char s[12];
9627 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009628
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009629 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009630 return 1;
9631 optnext = optfirst + *param_optind - 1;
9632
9633 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009634 p = NULL;
9635 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009636 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009637 if (p == NULL || *p == '\0') {
9638 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009639 p = *optnext;
9640 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009641 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009642 p = NULL;
9643 done = 1;
9644 goto out;
9645 }
9646 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009647 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009648 goto atend;
9649 }
9650
9651 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009652 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009653 if (*q == '\0') {
9654 if (optstr[0] == ':') {
9655 s[0] = c;
9656 s[1] = '\0';
9657 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009658 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009659 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009660 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009661 }
9662 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009663 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009664 }
9665 if (*++q == ':')
9666 q++;
9667 }
9668
9669 if (*++q == ':') {
9670 if (*p == '\0' && (p = *optnext) == NULL) {
9671 if (optstr[0] == ':') {
9672 s[0] = c;
9673 s[1] = '\0';
9674 err |= setvarsafe("OPTARG", s, 0);
9675 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009676 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009677 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009678 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009679 c = '?';
9680 }
Eric Andersenc470f442003-07-28 09:56:35 +00009681 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009682 }
9683
9684 if (p == *optnext)
9685 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009686 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009687 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009688 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009689 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009690 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009691 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009692 *param_optind = optnext - optfirst + 1;
9693 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009694 err |= setvarsafe("OPTIND", s, VNOFUNC);
9695 s[0] = c;
9696 s[1] = '\0';
9697 err |= setvarsafe(optvar, s, 0);
9698 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009699 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009700 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009701 flush_stdout_stderr();
9702 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009703 }
9704 return done;
9705}
Eric Andersenc470f442003-07-28 09:56:35 +00009706
9707/*
9708 * The getopts builtin. Shellparam.optnext points to the next argument
9709 * to be processed. Shellparam.optptr points to the next character to
9710 * be processed in the current argument. If shellparam.optnext is NULL,
9711 * then it's the first time getopts has been called.
9712 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009713static int
Eric Andersenc470f442003-07-28 09:56:35 +00009714getoptscmd(int argc, char **argv)
9715{
9716 char **optbase;
9717
9718 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009719 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009720 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009721 optbase = shellparam.p;
9722 if (shellparam.optind > shellparam.nparam + 1) {
9723 shellparam.optind = 1;
9724 shellparam.optoff = -1;
9725 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009726 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009727 optbase = &argv[3];
9728 if (shellparam.optind > argc - 2) {
9729 shellparam.optind = 1;
9730 shellparam.optoff = -1;
9731 }
9732 }
9733
9734 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009735 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009736}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009737#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009738
Eric Andersencb57d552001-06-28 07:25:16 +00009739
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009740/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009741
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009742/*
9743 * NEOF is returned by parsecmd when it encounters an end of file. It
9744 * must be distinct from NULL, so we use the address of a variable that
9745 * happens to be handy.
9746 */
9747static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009748#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009749static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009750static int lasttoken; /* last token read */
9751static char *wordtext; /* text of last word returned by readtoken */
9752static struct nodelist *backquotelist;
9753static union node *redirnode;
9754static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009755static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009756
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009757static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9758static void
9759raise_error_syntax(const char *msg)
9760{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009761 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009762 /* NOTREACHED */
9763}
9764
9765/*
9766 * Called when an unexpected token is read during the parse. The argument
9767 * is the token that is expected, or -1 if more than one type of token can
9768 * occur at this point.
9769 */
9770static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9771static void
9772raise_error_unexpected_syntax(int token)
9773{
9774 char msg[64];
9775 int l;
9776
9777 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9778 if (token >= 0)
9779 sprintf(msg + l, " (expecting %s)", tokname(token));
9780 raise_error_syntax(msg);
9781 /* NOTREACHED */
9782}
Eric Andersencb57d552001-06-28 07:25:16 +00009783
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009784#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009785
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009786struct heredoc {
9787 struct heredoc *next; /* next here document in list */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009788 union node *here; /* redirection node */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009789 char *eofmark; /* string indicating end of input */
9790 int striptabs; /* if set, strip leading tabs */
9791};
Eric Andersencb57d552001-06-28 07:25:16 +00009792
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009793static struct heredoc *heredoclist; /* list of here documents to read */
9794
9795/* parsing is heavily cross-recursive, need these forward decls */
9796static union node *andor(void);
9797static union node *pipeline(void);
9798static union node *parse_command(void);
9799static void parseheredoc(void);
9800static char peektoken(void);
9801static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009802
Eric Andersenc470f442003-07-28 09:56:35 +00009803static union node *
9804list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009805{
9806 union node *n1, *n2, *n3;
9807 int tok;
9808
Eric Andersenc470f442003-07-28 09:56:35 +00009809 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9810 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009811 return NULL;
9812 n1 = NULL;
9813 for (;;) {
9814 n2 = andor();
9815 tok = readtoken();
9816 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009817 if (n2->type == NPIPE) {
9818 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009819 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009820 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009821 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009822 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009823 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009824 n2 = n3;
9825 }
9826 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009827 }
9828 }
9829 if (n1 == NULL) {
9830 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009831 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009832 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009833 n3->type = NSEMI;
9834 n3->nbinary.ch1 = n1;
9835 n3->nbinary.ch2 = n2;
9836 n1 = n3;
9837 }
9838 switch (tok) {
9839 case TBACKGND:
9840 case TSEMI:
9841 tok = readtoken();
9842 /* fall through */
9843 case TNL:
9844 if (tok == TNL) {
9845 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009846 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009847 return n1;
9848 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009849 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009850 }
Eric Andersenc470f442003-07-28 09:56:35 +00009851 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009852 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009853 return n1;
9854 break;
9855 case TEOF:
9856 if (heredoclist)
9857 parseheredoc();
9858 else
Eric Andersenc470f442003-07-28 09:56:35 +00009859 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009860 return n1;
9861 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009862 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009863 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009864 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009865 return n1;
9866 }
9867 }
9868}
9869
Eric Andersenc470f442003-07-28 09:56:35 +00009870static union node *
9871andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009872{
Eric Andersencb57d552001-06-28 07:25:16 +00009873 union node *n1, *n2, *n3;
9874 int t;
9875
Eric Andersencb57d552001-06-28 07:25:16 +00009876 n1 = pipeline();
9877 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009878 t = readtoken();
9879 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009880 t = NAND;
9881 } else if (t == TOR) {
9882 t = NOR;
9883 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009884 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009885 return n1;
9886 }
Eric Andersenc470f442003-07-28 09:56:35 +00009887 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009888 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009889 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009890 n3->type = t;
9891 n3->nbinary.ch1 = n1;
9892 n3->nbinary.ch2 = n2;
9893 n1 = n3;
9894 }
9895}
9896
Eric Andersenc470f442003-07-28 09:56:35 +00009897static union node *
9898pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009899{
Eric Andersencb57d552001-06-28 07:25:16 +00009900 union node *n1, *n2, *pipenode;
9901 struct nodelist *lp, *prev;
9902 int negate;
9903
9904 negate = 0;
9905 TRACE(("pipeline: entered\n"));
9906 if (readtoken() == TNOT) {
9907 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009908 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009910 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009911 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009912 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009913 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009914 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009915 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009916 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009917 pipenode->npipe.cmdlist = lp;
9918 lp->n = n1;
9919 do {
9920 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009921 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009922 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009923 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009924 prev->next = lp;
9925 } while (readtoken() == TPIPE);
9926 lp->next = NULL;
9927 n1 = pipenode;
9928 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009929 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009930 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009931 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009932 n2->type = NNOT;
9933 n2->nnot.com = n1;
9934 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009935 }
9936 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009937}
9938
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009939static union node *
9940makename(void)
9941{
9942 union node *n;
9943
Denis Vlasenko597906c2008-02-20 16:38:54 +00009944 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009945 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009946 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009947 n->narg.text = wordtext;
9948 n->narg.backquote = backquotelist;
9949 return n;
9950}
9951
9952static void
9953fixredir(union node *n, const char *text, int err)
9954{
9955 TRACE(("Fix redir %s %d\n", text, err));
9956 if (!err)
9957 n->ndup.vname = NULL;
9958
9959 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009960 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009961 else if (LONE_DASH(text))
9962 n->ndup.dupfd = -1;
9963 else {
9964 if (err)
9965 raise_error_syntax("Bad fd number");
9966 n->ndup.vname = makename();
9967 }
9968}
9969
9970/*
9971 * Returns true if the text contains nothing to expand (no dollar signs
9972 * or backquotes).
9973 */
9974static int
9975noexpand(char *text)
9976{
9977 char *p;
9978 char c;
9979
9980 p = text;
9981 while ((c = *p++) != '\0') {
9982 if (c == CTLQUOTEMARK)
9983 continue;
9984 if (c == CTLESC)
9985 p++;
9986 else if (SIT(c, BASESYNTAX) == CCTL)
9987 return 0;
9988 }
9989 return 1;
9990}
9991
9992static void
9993parsefname(void)
9994{
9995 union node *n = redirnode;
9996
9997 if (readtoken() != TWORD)
9998 raise_error_unexpected_syntax(-1);
9999 if (n->type == NHERE) {
10000 struct heredoc *here = heredoc;
10001 struct heredoc *p;
10002 int i;
10003
10004 if (quoteflag == 0)
10005 n->type = NXHERE;
10006 TRACE(("Here document %d\n", n->type));
10007 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10008 raise_error_syntax("Illegal eof marker for << redirection");
10009 rmescapes(wordtext);
10010 here->eofmark = wordtext;
10011 here->next = NULL;
10012 if (heredoclist == NULL)
10013 heredoclist = here;
10014 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010015 for (p = heredoclist; p->next; p = p->next)
10016 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010017 p->next = here;
10018 }
10019 } else if (n->type == NTOFD || n->type == NFROMFD) {
10020 fixredir(n, wordtext, 0);
10021 } else {
10022 n->nfile.fname = makename();
10023 }
10024}
Eric Andersencb57d552001-06-28 07:25:16 +000010025
Eric Andersenc470f442003-07-28 09:56:35 +000010026static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010027simplecmd(void)
10028{
10029 union node *args, **app;
10030 union node *n = NULL;
10031 union node *vars, **vpp;
10032 union node **rpp, *redir;
10033 int savecheckkwd;
10034
10035 args = NULL;
10036 app = &args;
10037 vars = NULL;
10038 vpp = &vars;
10039 redir = NULL;
10040 rpp = &redir;
10041
10042 savecheckkwd = CHKALIAS;
10043 for (;;) {
10044 checkkwd = savecheckkwd;
10045 switch (readtoken()) {
10046 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010047 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010048 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010049 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010050 n->narg.text = wordtext;
10051 n->narg.backquote = backquotelist;
10052 if (savecheckkwd && isassignment(wordtext)) {
10053 *vpp = n;
10054 vpp = &n->narg.next;
10055 } else {
10056 *app = n;
10057 app = &n->narg.next;
10058 savecheckkwd = 0;
10059 }
10060 break;
10061 case TREDIR:
10062 *rpp = n = redirnode;
10063 rpp = &n->nfile.next;
10064 parsefname(); /* read name of redirection file */
10065 break;
10066 case TLP:
10067 if (args && app == &args->narg.next
10068 && !vars && !redir
10069 ) {
10070 struct builtincmd *bcmd;
10071 const char *name;
10072
10073 /* We have a function */
10074 if (readtoken() != TRP)
10075 raise_error_unexpected_syntax(TRP);
10076 name = n->narg.text;
10077 if (!goodname(name)
10078 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10079 ) {
10080 raise_error_syntax("Bad function name");
10081 }
10082 n->type = NDEFUN;
10083 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10084 n->narg.next = parse_command();
10085 return n;
10086 }
10087 /* fall through */
10088 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010089 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010090 goto out;
10091 }
10092 }
10093 out:
10094 *app = NULL;
10095 *vpp = NULL;
10096 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010097 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010098 n->type = NCMD;
10099 n->ncmd.args = args;
10100 n->ncmd.assign = vars;
10101 n->ncmd.redirect = redir;
10102 return n;
10103}
10104
10105static union node *
10106parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010107{
Eric Andersencb57d552001-06-28 07:25:16 +000010108 union node *n1, *n2;
10109 union node *ap, **app;
10110 union node *cp, **cpp;
10111 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010112 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010113 int t;
10114
10115 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010116 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010117
Eric Andersencb57d552001-06-28 07:25:16 +000010118 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010119 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010120 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010121 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010122 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010123 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010124 n1->type = NIF;
10125 n1->nif.test = list(0);
10126 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010127 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010128 n1->nif.ifpart = list(0);
10129 n2 = n1;
10130 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010131 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010132 n2 = n2->nif.elsepart;
10133 n2->type = NIF;
10134 n2->nif.test = list(0);
10135 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010136 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010137 n2->nif.ifpart = list(0);
10138 }
10139 if (lasttoken == TELSE)
10140 n2->nif.elsepart = list(0);
10141 else {
10142 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010143 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010144 }
Eric Andersenc470f442003-07-28 09:56:35 +000010145 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010146 break;
10147 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010148 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010149 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010150 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010151 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010152 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010153 got = readtoken();
10154 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010155 TRACE(("expecting DO got %s %s\n", tokname(got),
10156 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010157 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010158 }
10159 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010160 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010161 break;
10162 }
10163 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010164 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010165 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010166 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010167 n1->type = NFOR;
10168 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010169 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010170 if (readtoken() == TIN) {
10171 app = &ap;
10172 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010173 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010174 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010175 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010176 n2->narg.text = wordtext;
10177 n2->narg.backquote = backquotelist;
10178 *app = n2;
10179 app = &n2->narg.next;
10180 }
10181 *app = NULL;
10182 n1->nfor.args = ap;
10183 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010184 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010185 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010186 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010187 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010188 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010189 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010190 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010191 n1->nfor.args = n2;
10192 /*
10193 * Newline or semicolon here is optional (but note
10194 * that the original Bourne shell only allowed NL).
10195 */
10196 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010197 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010198 }
Eric Andersenc470f442003-07-28 09:56:35 +000010199 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010200 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010201 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010202 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010203 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010204 break;
10205 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010206 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010207 n1->type = NCASE;
10208 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010209 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010210 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010211 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010212 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010213 n2->narg.text = wordtext;
10214 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010215 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010216 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010217 } while (readtoken() == TNL);
10218 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010219 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010220 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010221 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010222 checkkwd = CHKNL | CHKKWD;
10223 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010224 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010225 if (lasttoken == TLP)
10226 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010227 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010228 cp->type = NCLIST;
10229 app = &cp->nclist.pattern;
10230 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010231 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010232 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010233 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010234 ap->narg.text = wordtext;
10235 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010236 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010237 break;
10238 app = &ap->narg.next;
10239 readtoken();
10240 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010241 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010242 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010243 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010244 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010245
Eric Andersenc470f442003-07-28 09:56:35 +000010246 cpp = &cp->nclist.next;
10247
10248 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010249 t = readtoken();
10250 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010251 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010252 raise_error_unexpected_syntax(TENDCASE);
10253 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010254 }
Eric Andersenc470f442003-07-28 09:56:35 +000010255 }
Eric Andersencb57d552001-06-28 07:25:16 +000010256 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010257 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010258 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010259 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010260 n1->type = NSUBSHELL;
10261 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010262 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010263 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010264 break;
10265 case TBEGIN:
10266 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010267 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010268 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010269 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010270 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010271 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010272 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010273 }
10274
Eric Andersenc470f442003-07-28 09:56:35 +000010275 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010276 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010277
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010278 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010279 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010280 checkkwd = CHKKWD | CHKALIAS;
10281 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010282 while (readtoken() == TREDIR) {
10283 *rpp = n2 = redirnode;
10284 rpp = &n2->nfile.next;
10285 parsefname();
10286 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010287 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010288 *rpp = NULL;
10289 if (redir) {
10290 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010291 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010292 n2->type = NREDIR;
10293 n2->nredir.n = n1;
10294 n1 = n2;
10295 }
10296 n1->nredir.redirect = redir;
10297 }
Eric Andersencb57d552001-06-28 07:25:16 +000010298 return n1;
10299}
10300
Eric Andersencb57d552001-06-28 07:25:16 +000010301/*
10302 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10303 * is not NULL, read a here document. In the latter case, eofmark is the
10304 * word which marks the end of the document and striptabs is true if
10305 * leading tabs should be stripped from the document. The argument firstc
10306 * is the first character of the input token or document.
10307 *
10308 * Because C does not have internal subroutines, I have simulated them
10309 * using goto's to implement the subroutine linkage. The following macros
10310 * will run code that appears at the end of readtoken1.
10311 */
10312
Eric Andersen2870d962001-07-02 17:27:21 +000010313#define CHECKEND() {goto checkend; checkend_return:;}
10314#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10315#define PARSESUB() {goto parsesub; parsesub_return:;}
10316#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10317#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10318#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010319
10320static int
Eric Andersenc470f442003-07-28 09:56:35 +000010321readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010322{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010323 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010324 int c = firstc;
10325 char *out;
10326 int len;
10327 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010328 struct nodelist *bqlist;
10329 smallint quotef;
10330 smallint dblquote;
10331 smallint oldstyle;
10332 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010333#if ENABLE_ASH_EXPAND_PRMT
10334 smallint pssyntax; /* we are expanding a prompt string */
10335#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010336 int varnest; /* levels of variables expansion */
10337 int arinest; /* levels of arithmetic expansion */
10338 int parenlevel; /* levels of parens in arithmetic */
10339 int dqvarnest; /* levels of variables expansion within double quotes */
10340
Eric Andersencb57d552001-06-28 07:25:16 +000010341#if __GNUC__
10342 /* Avoid longjmp clobbering */
10343 (void) &out;
10344 (void) &quotef;
10345 (void) &dblquote;
10346 (void) &varnest;
10347 (void) &arinest;
10348 (void) &parenlevel;
10349 (void) &dqvarnest;
10350 (void) &oldstyle;
10351 (void) &prevsyntax;
10352 (void) &syntax;
10353#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010354 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010355 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010356 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010357 oldstyle = 0;
10358 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010359#if ENABLE_ASH_EXPAND_PRMT
10360 pssyntax = (syntax == PSSYNTAX);
10361 if (pssyntax)
10362 syntax = DQSYNTAX;
10363#endif
10364 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010365 varnest = 0;
10366 arinest = 0;
10367 parenlevel = 0;
10368 dqvarnest = 0;
10369
10370 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010371 loop: { /* for each line, until end of word */
10372 CHECKEND(); /* set c to PEOF if at end of here document */
10373 for (;;) { /* until end of line or end of word */
10374 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010375 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010376 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010377 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010378 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010379 USTPUTC(c, out);
10380 plinno++;
10381 if (doprompt)
10382 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010383 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010384 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010385 case CWORD:
10386 USTPUTC(c, out);
10387 break;
10388 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010389 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010390 USTPUTC(CTLESC, out);
10391 USTPUTC(c, out);
10392 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010393 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010394 c = pgetc2();
10395 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010396 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010397 USTPUTC('\\', out);
10398 pungetc();
10399 } else if (c == '\n') {
10400 if (doprompt)
10401 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010402 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010403#if ENABLE_ASH_EXPAND_PRMT
10404 if (c == '$' && pssyntax) {
10405 USTPUTC(CTLESC, out);
10406 USTPUTC('\\', out);
10407 }
10408#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010409 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010410 c != '\\' && c != '`' &&
10411 c != '$' && (
10412 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010413 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010414 ) {
10415 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010416 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010417 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010418 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010419 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010420 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010421 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010422 }
10423 break;
10424 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010425 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010426 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010427 if (eofmark == NULL) {
10428 USTPUTC(CTLQUOTEMARK, out);
10429 }
Eric Andersencb57d552001-06-28 07:25:16 +000010430 break;
10431 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010432 syntax = DQSYNTAX;
10433 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010434 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010435 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010436 if (eofmark != NULL && arinest == 0
10437 && varnest == 0
10438 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010439 USTPUTC(c, out);
10440 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010441 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010442 syntax = BASESYNTAX;
10443 dblquote = 0;
10444 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010445 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010446 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 }
10448 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010449 case CVAR: /* '$' */
10450 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010451 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010452 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010453 if (varnest > 0) {
10454 varnest--;
10455 if (dqvarnest > 0) {
10456 dqvarnest--;
10457 }
10458 USTPUTC(CTLENDVAR, out);
10459 } else {
10460 USTPUTC(c, out);
10461 }
10462 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010463#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010464 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010465 parenlevel++;
10466 USTPUTC(c, out);
10467 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010468 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010469 if (parenlevel > 0) {
10470 USTPUTC(c, out);
10471 --parenlevel;
10472 } else {
10473 if (pgetc() == ')') {
10474 if (--arinest == 0) {
10475 USTPUTC(CTLENDARI, out);
10476 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010477 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010478 } else
10479 USTPUTC(')', out);
10480 } else {
10481 /*
10482 * unbalanced parens
10483 * (don't 2nd guess - no error)
10484 */
10485 pungetc();
10486 USTPUTC(')', out);
10487 }
10488 }
10489 break;
10490#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010491 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010492 PARSEBACKQOLD();
10493 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010494 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010495 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010496 case CIGN:
10497 break;
10498 default:
10499 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010500 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010501#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010502 if (c != PEOA)
10503#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010504 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010505
Eric Andersencb57d552001-06-28 07:25:16 +000010506 }
10507 c = pgetc_macro();
10508 }
10509 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010510 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010511#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010512 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010513 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010514#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010515 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010516 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010517 if (varnest != 0) {
10518 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010519 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010520 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010521 }
10522 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010523 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010524 out = stackblock();
10525 if (eofmark == NULL) {
10526 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010527 && quotef == 0
10528 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010529 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010530 PARSEREDIR();
10531 return lasttoken = TREDIR;
10532 } else {
10533 pungetc();
10534 }
10535 }
10536 quoteflag = quotef;
10537 backquotelist = bqlist;
10538 grabstackblock(len);
10539 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010540 lasttoken = TWORD;
10541 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010542/* end of readtoken routine */
10543
Eric Andersencb57d552001-06-28 07:25:16 +000010544/*
10545 * Check to see whether we are at the end of the here document. When this
10546 * is called, c is set to the first character of the next input line. If
10547 * we are at the end of the here document, this routine sets the c to PEOF.
10548 */
Eric Andersenc470f442003-07-28 09:56:35 +000010549checkend: {
10550 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010551#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010552 if (c == PEOA) {
10553 c = pgetc2();
10554 }
10555#endif
10556 if (striptabs) {
10557 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010558 c = pgetc2();
10559 }
Eric Andersenc470f442003-07-28 09:56:35 +000010560 }
10561 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010562 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010563 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010564
Eric Andersenc470f442003-07-28 09:56:35 +000010565 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010566 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010567 if (*p == '\n' && *q == '\0') {
10568 c = PEOF;
10569 plinno++;
10570 needprompt = doprompt;
10571 } else {
10572 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010573 }
10574 }
10575 }
10576 }
Eric Andersenc470f442003-07-28 09:56:35 +000010577 goto checkend_return;
10578}
Eric Andersencb57d552001-06-28 07:25:16 +000010579
Eric Andersencb57d552001-06-28 07:25:16 +000010580/*
10581 * Parse a redirection operator. The variable "out" points to a string
10582 * specifying the fd to be redirected. The variable "c" contains the
10583 * first character of the redirection operator.
10584 */
Eric Andersenc470f442003-07-28 09:56:35 +000010585parseredir: {
10586 char fd = *out;
10587 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010588
Denis Vlasenko597906c2008-02-20 16:38:54 +000010589 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010590 if (c == '>') {
10591 np->nfile.fd = 1;
10592 c = pgetc();
10593 if (c == '>')
10594 np->type = NAPPEND;
10595 else if (c == '|')
10596 np->type = NCLOBBER;
10597 else if (c == '&')
10598 np->type = NTOFD;
10599 else {
10600 np->type = NTO;
10601 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010602 }
Eric Andersenc470f442003-07-28 09:56:35 +000010603 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010604 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010605 c = pgetc();
10606 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010607 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010608 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010609 np = stzalloc(sizeof(struct nhere));
10610 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010611 }
10612 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010613 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010614 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010615 c = pgetc();
10616 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010617 heredoc->striptabs = 1;
10618 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010619 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010620 pungetc();
10621 }
10622 break;
10623
10624 case '&':
10625 np->type = NFROMFD;
10626 break;
10627
10628 case '>':
10629 np->type = NFROMTO;
10630 break;
10631
10632 default:
10633 np->type = NFROM;
10634 pungetc();
10635 break;
10636 }
Eric Andersencb57d552001-06-28 07:25:16 +000010637 }
Eric Andersenc470f442003-07-28 09:56:35 +000010638 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010639 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010640 redirnode = np;
10641 goto parseredir_return;
10642}
Eric Andersencb57d552001-06-28 07:25:16 +000010643
Eric Andersencb57d552001-06-28 07:25:16 +000010644/*
10645 * Parse a substitution. At this point, we have read the dollar sign
10646 * and nothing else.
10647 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010648
10649/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10650 * (assuming ascii char codes, as the original implementation did) */
10651#define is_special(c) \
10652 ((((unsigned int)c) - 33 < 32) \
10653 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010654parsesub: {
10655 int subtype;
10656 int typeloc;
10657 int flags;
10658 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010659 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010660
Eric Andersenc470f442003-07-28 09:56:35 +000010661 c = pgetc();
10662 if (
10663 c <= PEOA_OR_PEOF ||
10664 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10665 ) {
10666 USTPUTC('$', out);
10667 pungetc();
10668 } else if (c == '(') { /* $(command) or $((arith)) */
10669 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010670#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010671 PARSEARITH();
10672#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010673 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010674#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010675 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010676 pungetc();
10677 PARSEBACKQNEW();
10678 }
10679 } else {
10680 USTPUTC(CTLVAR, out);
10681 typeloc = out - (char *)stackblock();
10682 USTPUTC(VSNORMAL, out);
10683 subtype = VSNORMAL;
10684 if (c == '{') {
10685 c = pgetc();
10686 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010687 c = pgetc();
10688 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010689 c = '#';
10690 else
10691 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010692 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010693 subtype = 0;
10694 }
10695 if (c > PEOA_OR_PEOF && is_name(c)) {
10696 do {
10697 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010698 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010699 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010700 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010701 do {
10702 STPUTC(c, out);
10703 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010704 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010705 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010706 USTPUTC(c, out);
10707 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010708 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010709 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010710
Eric Andersenc470f442003-07-28 09:56:35 +000010711 STPUTC('=', out);
10712 flags = 0;
10713 if (subtype == 0) {
10714 switch (c) {
10715 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010716 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010717#if ENABLE_ASH_BASH_COMPAT
10718 if (c == ':' || c == '$' || isdigit(c)) {
10719 pungetc();
10720 subtype = VSSUBSTR;
10721 break;
10722 }
10723#endif
10724 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010725 /*FALLTHROUGH*/
10726 default:
10727 p = strchr(types, c);
10728 if (p == NULL)
10729 goto badsub;
10730 subtype = p - types + VSNORMAL;
10731 break;
10732 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010733 case '#': {
10734 int cc = c;
10735 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10736 c = pgetc();
10737 if (c == cc)
10738 subtype++;
10739 else
10740 pungetc();
10741 break;
10742 }
10743#if ENABLE_ASH_BASH_COMPAT
10744 case '/':
10745 subtype = VSREPLACE;
10746 c = pgetc();
10747 if (c == '/')
10748 subtype++; /* VSREPLACEALL */
10749 else
10750 pungetc();
10751 break;
10752#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010753 }
Eric Andersenc470f442003-07-28 09:56:35 +000010754 } else {
10755 pungetc();
10756 }
10757 if (dblquote || arinest)
10758 flags |= VSQUOTE;
10759 *((char *)stackblock() + typeloc) = subtype | flags;
10760 if (subtype != VSNORMAL) {
10761 varnest++;
10762 if (dblquote || arinest) {
10763 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010764 }
10765 }
10766 }
Eric Andersenc470f442003-07-28 09:56:35 +000010767 goto parsesub_return;
10768}
Eric Andersencb57d552001-06-28 07:25:16 +000010769
Eric Andersencb57d552001-06-28 07:25:16 +000010770/*
10771 * Called to parse command substitutions. Newstyle is set if the command
10772 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10773 * list of commands (passed by reference), and savelen is the number of
10774 * characters on the top of the stack which must be preserved.
10775 */
Eric Andersenc470f442003-07-28 09:56:35 +000010776parsebackq: {
10777 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010778 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010779 union node *n;
10780 char *volatile str;
10781 struct jmploc jmploc;
10782 struct jmploc *volatile savehandler;
10783 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010784 smallint saveprompt = 0;
10785
Eric Andersencb57d552001-06-28 07:25:16 +000010786#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010787 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010788#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010789 savepbq = parsebackquote;
10790 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010791 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010792 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010793 exception_handler = savehandler;
10794 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010795 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010796 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010797 str = NULL;
10798 savelen = out - (char *)stackblock();
10799 if (savelen > 0) {
10800 str = ckmalloc(savelen);
10801 memcpy(str, stackblock(), savelen);
10802 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010803 savehandler = exception_handler;
10804 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010805 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010806 if (oldstyle) {
10807 /* We must read until the closing backquote, giving special
10808 treatment to some slashes, and then push the string and
10809 reread it as input, interpreting it normally. */
10810 char *pout;
10811 int pc;
10812 size_t psavelen;
10813 char *pstr;
10814
10815
10816 STARTSTACKSTR(pout);
10817 for (;;) {
10818 if (needprompt) {
10819 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010820 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010821 pc = pgetc();
10822 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010823 case '`':
10824 goto done;
10825
10826 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010827 pc = pgetc();
10828 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010829 plinno++;
10830 if (doprompt)
10831 setprompt(2);
10832 /*
10833 * If eating a newline, avoid putting
10834 * the newline into the new character
10835 * stream (via the STPUTC after the
10836 * switch).
10837 */
10838 continue;
10839 }
10840 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010841 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010842 STPUTC('\\', pout);
10843 if (pc > PEOA_OR_PEOF) {
10844 break;
10845 }
10846 /* fall through */
10847
10848 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010849#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010850 case PEOA:
10851#endif
10852 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010853 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010854
10855 case '\n':
10856 plinno++;
10857 needprompt = doprompt;
10858 break;
10859
10860 default:
10861 break;
10862 }
10863 STPUTC(pc, pout);
10864 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010865 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010866 STPUTC('\0', pout);
10867 psavelen = pout - (char *)stackblock();
10868 if (psavelen > 0) {
10869 pstr = grabstackstr(pout);
10870 setinputstring(pstr);
10871 }
10872 }
10873 nlpp = &bqlist;
10874 while (*nlpp)
10875 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010876 *nlpp = stzalloc(sizeof(**nlpp));
10877 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010878 parsebackquote = oldstyle;
10879
10880 if (oldstyle) {
10881 saveprompt = doprompt;
10882 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010883 }
10884
Eric Andersenc470f442003-07-28 09:56:35 +000010885 n = list(2);
10886
10887 if (oldstyle)
10888 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010889 else if (readtoken() != TRP)
10890 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010891
10892 (*nlpp)->n = n;
10893 if (oldstyle) {
10894 /*
10895 * Start reading from old file again, ignoring any pushed back
10896 * tokens left from the backquote parsing
10897 */
10898 popfile();
10899 tokpushback = 0;
10900 }
10901 while (stackblocksize() <= savelen)
10902 growstackblock();
10903 STARTSTACKSTR(out);
10904 if (str) {
10905 memcpy(out, str, savelen);
10906 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010907 INT_OFF;
10908 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010909 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010910 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010911 }
10912 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010913 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010914 if (arinest || dblquote)
10915 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10916 else
10917 USTPUTC(CTLBACKQ, out);
10918 if (oldstyle)
10919 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010920 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010921}
10922
Denis Vlasenko131ae172007-02-18 13:00:19 +000010923#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010924/*
10925 * Parse an arithmetic expansion (indicate start of one and set state)
10926 */
Eric Andersenc470f442003-07-28 09:56:35 +000010927parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010928 if (++arinest == 1) {
10929 prevsyntax = syntax;
10930 syntax = ARISYNTAX;
10931 USTPUTC(CTLARI, out);
10932 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010933 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010934 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010935 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010936 } else {
10937 /*
10938 * we collapse embedded arithmetic expansion to
10939 * parenthesis, which should be equivalent
10940 */
10941 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010942 }
Eric Andersenc470f442003-07-28 09:56:35 +000010943 goto parsearith_return;
10944}
10945#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010946
Eric Andersenc470f442003-07-28 09:56:35 +000010947} /* end of readtoken */
10948
Eric Andersencb57d552001-06-28 07:25:16 +000010949/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010950 * Read the next input token.
10951 * If the token is a word, we set backquotelist to the list of cmds in
10952 * backquotes. We set quoteflag to true if any part of the word was
10953 * quoted.
10954 * If the token is TREDIR, then we set redirnode to a structure containing
10955 * the redirection.
10956 * In all cases, the variable startlinno is set to the number of the line
10957 * on which the token starts.
10958 *
10959 * [Change comment: here documents and internal procedures]
10960 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10961 * word parsing code into a separate routine. In this case, readtoken
10962 * doesn't need to have any internal procedures, but parseword does.
10963 * We could also make parseoperator in essence the main routine, and
10964 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010965 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010966#define NEW_xxreadtoken
10967#ifdef NEW_xxreadtoken
10968/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010969static const char xxreadtoken_chars[7] ALIGN1 = {
10970 '\n', '(', ')', '&', '|', ';', 0
10971};
Eric Andersencb57d552001-06-28 07:25:16 +000010972
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010973static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010974 TNL, TLP, TRP, /* only single occurrence allowed */
10975 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10976 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010977 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010978};
10979
10980#define xxreadtoken_doubles \
10981 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10982#define xxreadtoken_singles \
10983 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10984
10985static int
10986xxreadtoken(void)
10987{
10988 int c;
10989
10990 if (tokpushback) {
10991 tokpushback = 0;
10992 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010993 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010994 if (needprompt) {
10995 setprompt(2);
10996 }
10997 startlinno = plinno;
10998 for (;;) { /* until token or start of word found */
10999 c = pgetc_macro();
11000
11001 if ((c != ' ') && (c != '\t')
11002#if ENABLE_ASH_ALIAS
11003 && (c != PEOA)
11004#endif
11005 ) {
11006 if (c == '#') {
11007 while ((c = pgetc()) != '\n' && c != PEOF);
11008 pungetc();
11009 } else if (c == '\\') {
11010 if (pgetc() != '\n') {
11011 pungetc();
11012 goto READTOKEN1;
11013 }
11014 startlinno = ++plinno;
11015 if (doprompt)
11016 setprompt(2);
11017 } else {
11018 const char *p
11019 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11020
11021 if (c != PEOF) {
11022 if (c == '\n') {
11023 plinno++;
11024 needprompt = doprompt;
11025 }
11026
11027 p = strchr(xxreadtoken_chars, c);
11028 if (p == NULL) {
11029 READTOKEN1:
11030 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11031 }
11032
11033 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
11034 if (pgetc() == *p) { /* double occurrence? */
11035 p += xxreadtoken_doubles + 1;
11036 } else {
11037 pungetc();
11038 }
11039 }
11040 }
11041 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11042 }
11043 }
11044 } /* for */
11045}
11046#else
11047#define RETURN(token) return lasttoken = token
11048static int
11049xxreadtoken(void)
11050{
11051 int c;
11052
11053 if (tokpushback) {
11054 tokpushback = 0;
11055 return lasttoken;
11056 }
11057 if (needprompt) {
11058 setprompt(2);
11059 }
11060 startlinno = plinno;
11061 for (;;) { /* until token or start of word found */
11062 c = pgetc_macro();
11063 switch (c) {
11064 case ' ': case '\t':
11065#if ENABLE_ASH_ALIAS
11066 case PEOA:
11067#endif
11068 continue;
11069 case '#':
11070 while ((c = pgetc()) != '\n' && c != PEOF);
11071 pungetc();
11072 continue;
11073 case '\\':
11074 if (pgetc() == '\n') {
11075 startlinno = ++plinno;
11076 if (doprompt)
11077 setprompt(2);
11078 continue;
11079 }
11080 pungetc();
11081 goto breakloop;
11082 case '\n':
11083 plinno++;
11084 needprompt = doprompt;
11085 RETURN(TNL);
11086 case PEOF:
11087 RETURN(TEOF);
11088 case '&':
11089 if (pgetc() == '&')
11090 RETURN(TAND);
11091 pungetc();
11092 RETURN(TBACKGND);
11093 case '|':
11094 if (pgetc() == '|')
11095 RETURN(TOR);
11096 pungetc();
11097 RETURN(TPIPE);
11098 case ';':
11099 if (pgetc() == ';')
11100 RETURN(TENDCASE);
11101 pungetc();
11102 RETURN(TSEMI);
11103 case '(':
11104 RETURN(TLP);
11105 case ')':
11106 RETURN(TRP);
11107 default:
11108 goto breakloop;
11109 }
11110 }
11111 breakloop:
11112 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11113#undef RETURN
11114}
11115#endif /* NEW_xxreadtoken */
11116
11117static int
11118readtoken(void)
11119{
11120 int t;
11121#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011122 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011123#endif
11124
11125#if ENABLE_ASH_ALIAS
11126 top:
11127#endif
11128
11129 t = xxreadtoken();
11130
11131 /*
11132 * eat newlines
11133 */
11134 if (checkkwd & CHKNL) {
11135 while (t == TNL) {
11136 parseheredoc();
11137 t = xxreadtoken();
11138 }
11139 }
11140
11141 if (t != TWORD || quoteflag) {
11142 goto out;
11143 }
11144
11145 /*
11146 * check for keywords
11147 */
11148 if (checkkwd & CHKKWD) {
11149 const char *const *pp;
11150
11151 pp = findkwd(wordtext);
11152 if (pp) {
11153 lasttoken = t = pp - tokname_array;
11154 TRACE(("keyword %s recognized\n", tokname(t)));
11155 goto out;
11156 }
11157 }
11158
11159 if (checkkwd & CHKALIAS) {
11160#if ENABLE_ASH_ALIAS
11161 struct alias *ap;
11162 ap = lookupalias(wordtext, 1);
11163 if (ap != NULL) {
11164 if (*ap->val) {
11165 pushstring(ap->val, ap);
11166 }
11167 goto top;
11168 }
11169#endif
11170 }
11171 out:
11172 checkkwd = 0;
11173#if DEBUG
11174 if (!alreadyseen)
11175 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11176 else
11177 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11178#endif
11179 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011180}
11181
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011182static char
11183peektoken(void)
11184{
11185 int t;
11186
11187 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011188 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011189 return tokname_array[t][0];
11190}
Eric Andersencb57d552001-06-28 07:25:16 +000011191
11192/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011193 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11194 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011195 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011196static union node *
11197parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011198{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011199 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011200
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011201 tokpushback = 0;
11202 doprompt = interact;
11203 if (doprompt)
11204 setprompt(doprompt);
11205 needprompt = 0;
11206 t = readtoken();
11207 if (t == TEOF)
11208 return NEOF;
11209 if (t == TNL)
11210 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011211 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011212 return list(1);
11213}
11214
11215/*
11216 * Input any here documents.
11217 */
11218static void
11219parseheredoc(void)
11220{
11221 struct heredoc *here;
11222 union node *n;
11223
11224 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011225 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011226
11227 while (here) {
11228 if (needprompt) {
11229 setprompt(2);
11230 }
11231 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11232 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011233 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011234 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011235 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011236 n->narg.text = wordtext;
11237 n->narg.backquote = backquotelist;
11238 here->here->nhere.doc = n;
11239 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011240 }
Eric Andersencb57d552001-06-28 07:25:16 +000011241}
11242
11243
11244/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011245 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011246 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011247#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011248static const char *
11249expandstr(const char *ps)
11250{
11251 union node n;
11252
11253 /* XXX Fix (char *) cast. */
11254 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011255 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011256 popfile();
11257
11258 n.narg.type = NARG;
11259 n.narg.next = NULL;
11260 n.narg.text = wordtext;
11261 n.narg.backquote = backquotelist;
11262
11263 expandarg(&n, NULL, 0);
11264 return stackblock();
11265}
11266#endif
11267
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011268/*
11269 * Execute a command or commands contained in a string.
11270 */
11271static int
11272evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011273{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011274 union node *n;
11275 struct stackmark smark;
11276 int skip;
11277
11278 setinputstring(s);
11279 setstackmark(&smark);
11280
11281 skip = 0;
11282 while ((n = parsecmd(0)) != NEOF) {
11283 evaltree(n, 0);
11284 popstackmark(&smark);
11285 skip = evalskip;
11286 if (skip)
11287 break;
11288 }
11289 popfile();
11290
11291 skip &= mask;
11292 evalskip = skip;
11293 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011294}
11295
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011296/*
11297 * The eval command.
11298 */
11299static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011300evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011301{
11302 char *p;
11303 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011304
Denis Vlasenko68404f12008-03-17 09:00:54 +000011305 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011306 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011307 argv += 2;
11308 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011309 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011310 for (;;) {
11311 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011312 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011313 if (p == NULL)
11314 break;
11315 STPUTC(' ', concat);
11316 }
11317 STPUTC('\0', concat);
11318 p = grabstackstr(concat);
11319 }
11320 evalstring(p, ~SKIPEVAL);
11321
11322 }
11323 return exitstatus;
11324}
11325
11326/*
11327 * Read and execute commands. "Top" is nonzero for the top level command
11328 * loop; it turns on prompting if the shell is interactive.
11329 */
11330static int
11331cmdloop(int top)
11332{
11333 union node *n;
11334 struct stackmark smark;
11335 int inter;
11336 int numeof = 0;
11337
11338 TRACE(("cmdloop(%d) called\n", top));
11339 for (;;) {
11340 int skip;
11341
11342 setstackmark(&smark);
11343#if JOBS
11344 if (jobctl)
11345 showjobs(stderr, SHOW_CHANGED);
11346#endif
11347 inter = 0;
11348 if (iflag && top) {
11349 inter++;
11350#if ENABLE_ASH_MAIL
11351 chkmail();
11352#endif
11353 }
11354 n = parsecmd(inter);
11355 /* showtree(n); DEBUG */
11356 if (n == NEOF) {
11357 if (!top || numeof >= 50)
11358 break;
11359 if (!stoppedjobs()) {
11360 if (!Iflag)
11361 break;
11362 out2str("\nUse \"exit\" to leave shell.\n");
11363 }
11364 numeof++;
11365 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011366 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11367 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011368 numeof = 0;
11369 evaltree(n, 0);
11370 }
11371 popstackmark(&smark);
11372 skip = evalskip;
11373
11374 if (skip) {
11375 evalskip = 0;
11376 return skip & SKIPEVAL;
11377 }
11378 }
11379 return 0;
11380}
11381
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011382/*
11383 * Take commands from a file. To be compatible we should do a path
11384 * search for the file, which is necessary to find sub-commands.
11385 */
11386static char *
11387find_dot_file(char *name)
11388{
11389 char *fullname;
11390 const char *path = pathval();
11391 struct stat statb;
11392
11393 /* don't try this for absolute or relative paths */
11394 if (strchr(name, '/'))
11395 return name;
11396
11397 while ((fullname = padvance(&path, name)) != NULL) {
11398 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11399 /*
11400 * Don't bother freeing here, since it will
11401 * be freed by the caller.
11402 */
11403 return fullname;
11404 }
11405 stunalloc(fullname);
11406 }
11407
11408 /* not found in the PATH */
11409 ash_msg_and_raise_error("%s: not found", name);
11410 /* NOTREACHED */
11411}
11412
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011413static int
11414dotcmd(int argc, char **argv)
11415{
11416 struct strlist *sp;
11417 volatile struct shparam saveparam;
11418 int status = 0;
11419
11420 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011421 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011422
Denis Vlasenko68404f12008-03-17 09:00:54 +000011423 if (argv[1]) { /* That's what SVR2 does */
11424 char *fullname = find_dot_file(argv[1]);
11425 argv += 2;
11426 argc -= 2;
11427 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011428 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011429 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011430 shellparam.nparam = argc;
11431 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011432 };
11433
11434 setinputfile(fullname, INPUT_PUSH_FILE);
11435 commandname = fullname;
11436 cmdloop(0);
11437 popfile();
11438
Denis Vlasenko68404f12008-03-17 09:00:54 +000011439 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011440 freeparam(&shellparam);
11441 shellparam = saveparam;
11442 };
11443 status = exitstatus;
11444 }
11445 return status;
11446}
11447
11448static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011449exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011450{
11451 if (stoppedjobs())
11452 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011453 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011454 exitstatus = number(argv[1]);
11455 raise_exception(EXEXIT);
11456 /* NOTREACHED */
11457}
11458
11459#if ENABLE_ASH_BUILTIN_ECHO
11460static int
11461echocmd(int argc, char **argv)
11462{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011463 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011464}
11465#endif
11466
11467#if ENABLE_ASH_BUILTIN_TEST
11468static int
11469testcmd(int argc, char **argv)
11470{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011471 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011472}
11473#endif
11474
11475/*
11476 * Read a file containing shell functions.
11477 */
11478static void
11479readcmdfile(char *name)
11480{
11481 setinputfile(name, INPUT_PUSH_FILE);
11482 cmdloop(0);
11483 popfile();
11484}
11485
11486
Denis Vlasenkocc571512007-02-23 21:10:35 +000011487/* ============ find_command inplementation */
11488
11489/*
11490 * Resolve a command name. If you change this routine, you may have to
11491 * change the shellexec routine as well.
11492 */
11493static void
11494find_command(char *name, struct cmdentry *entry, int act, const char *path)
11495{
11496 struct tblentry *cmdp;
11497 int idx;
11498 int prev;
11499 char *fullname;
11500 struct stat statb;
11501 int e;
11502 int updatetbl;
11503 struct builtincmd *bcmd;
11504
11505 /* If name contains a slash, don't use PATH or hash table */
11506 if (strchr(name, '/') != NULL) {
11507 entry->u.index = -1;
11508 if (act & DO_ABS) {
11509 while (stat(name, &statb) < 0) {
11510#ifdef SYSV
11511 if (errno == EINTR)
11512 continue;
11513#endif
11514 entry->cmdtype = CMDUNKNOWN;
11515 return;
11516 }
11517 }
11518 entry->cmdtype = CMDNORMAL;
11519 return;
11520 }
11521
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011522/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011523
11524 updatetbl = (path == pathval());
11525 if (!updatetbl) {
11526 act |= DO_ALTPATH;
11527 if (strstr(path, "%builtin") != NULL)
11528 act |= DO_ALTBLTIN;
11529 }
11530
11531 /* If name is in the table, check answer will be ok */
11532 cmdp = cmdlookup(name, 0);
11533 if (cmdp != NULL) {
11534 int bit;
11535
11536 switch (cmdp->cmdtype) {
11537 default:
11538#if DEBUG
11539 abort();
11540#endif
11541 case CMDNORMAL:
11542 bit = DO_ALTPATH;
11543 break;
11544 case CMDFUNCTION:
11545 bit = DO_NOFUNC;
11546 break;
11547 case CMDBUILTIN:
11548 bit = DO_ALTBLTIN;
11549 break;
11550 }
11551 if (act & bit) {
11552 updatetbl = 0;
11553 cmdp = NULL;
11554 } else if (cmdp->rehash == 0)
11555 /* if not invalidated by cd, we're done */
11556 goto success;
11557 }
11558
11559 /* If %builtin not in path, check for builtin next */
11560 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011561 if (bcmd) {
11562 if (IS_BUILTIN_REGULAR(bcmd))
11563 goto builtin_success;
11564 if (act & DO_ALTPATH) {
11565 if (!(act & DO_ALTBLTIN))
11566 goto builtin_success;
11567 } else if (builtinloc <= 0) {
11568 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011569 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011570 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011571
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011572#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011573 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011574 entry->cmdtype = CMDNORMAL;
11575 entry->u.index = -1;
11576 return;
11577 }
11578#endif
11579
Denis Vlasenkocc571512007-02-23 21:10:35 +000011580 /* We have to search path. */
11581 prev = -1; /* where to start */
11582 if (cmdp && cmdp->rehash) { /* doing a rehash */
11583 if (cmdp->cmdtype == CMDBUILTIN)
11584 prev = builtinloc;
11585 else
11586 prev = cmdp->param.index;
11587 }
11588
11589 e = ENOENT;
11590 idx = -1;
11591 loop:
11592 while ((fullname = padvance(&path, name)) != NULL) {
11593 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011594 /* NB: code below will still use fullname
11595 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011596 idx++;
11597 if (pathopt) {
11598 if (prefix(pathopt, "builtin")) {
11599 if (bcmd)
11600 goto builtin_success;
11601 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011602 } else if (!(act & DO_NOFUNC)
11603 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011604 /* handled below */
11605 } else {
11606 /* ignore unimplemented options */
11607 continue;
11608 }
11609 }
11610 /* if rehash, don't redo absolute path names */
11611 if (fullname[0] == '/' && idx <= prev) {
11612 if (idx < prev)
11613 continue;
11614 TRACE(("searchexec \"%s\": no change\n", name));
11615 goto success;
11616 }
11617 while (stat(fullname, &statb) < 0) {
11618#ifdef SYSV
11619 if (errno == EINTR)
11620 continue;
11621#endif
11622 if (errno != ENOENT && errno != ENOTDIR)
11623 e = errno;
11624 goto loop;
11625 }
11626 e = EACCES; /* if we fail, this will be the error */
11627 if (!S_ISREG(statb.st_mode))
11628 continue;
11629 if (pathopt) { /* this is a %func directory */
11630 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011631 /* NB: stalloc will return space pointed by fullname
11632 * (because we don't have any intervening allocations
11633 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011634 readcmdfile(fullname);
11635 cmdp = cmdlookup(name, 0);
11636 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11637 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11638 stunalloc(fullname);
11639 goto success;
11640 }
11641 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11642 if (!updatetbl) {
11643 entry->cmdtype = CMDNORMAL;
11644 entry->u.index = idx;
11645 return;
11646 }
11647 INT_OFF;
11648 cmdp = cmdlookup(name, 1);
11649 cmdp->cmdtype = CMDNORMAL;
11650 cmdp->param.index = idx;
11651 INT_ON;
11652 goto success;
11653 }
11654
11655 /* We failed. If there was an entry for this command, delete it */
11656 if (cmdp && updatetbl)
11657 delete_cmd_entry();
11658 if (act & DO_ERR)
11659 ash_msg("%s: %s", name, errmsg(e, "not found"));
11660 entry->cmdtype = CMDUNKNOWN;
11661 return;
11662
11663 builtin_success:
11664 if (!updatetbl) {
11665 entry->cmdtype = CMDBUILTIN;
11666 entry->u.cmd = bcmd;
11667 return;
11668 }
11669 INT_OFF;
11670 cmdp = cmdlookup(name, 1);
11671 cmdp->cmdtype = CMDBUILTIN;
11672 cmdp->param.cmd = bcmd;
11673 INT_ON;
11674 success:
11675 cmdp->rehash = 0;
11676 entry->cmdtype = cmdp->cmdtype;
11677 entry->u = cmdp->param;
11678}
11679
11680
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011681/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011682
Eric Andersencb57d552001-06-28 07:25:16 +000011683/*
Eric Andersencb57d552001-06-28 07:25:16 +000011684 * The trap builtin.
11685 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011686static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011687trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011688{
11689 char *action;
11690 char **ap;
11691 int signo;
11692
Eric Andersenc470f442003-07-28 09:56:35 +000011693 nextopt(nullstr);
11694 ap = argptr;
11695 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011696 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011697 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011698 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011699
Rob Landleyc9c1a412006-07-12 19:17:55 +000011700 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011701 out1fmt("trap -- %s %s\n",
11702 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011703 }
11704 }
11705 return 0;
11706 }
Eric Andersenc470f442003-07-28 09:56:35 +000011707 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011708 action = NULL;
11709 else
11710 action = *ap++;
11711 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011712 signo = get_signum(*ap);
11713 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011714 ash_msg_and_raise_error("%s: bad trap", *ap);
11715 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011716 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011717 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011718 action = NULL;
11719 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011720 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011721 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011722 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011723 trap[signo] = action;
11724 if (signo != 0)
11725 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011726 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011727 ap++;
11728 }
11729 return 0;
11730}
11731
Eric Andersenc470f442003-07-28 09:56:35 +000011732
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011733/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011734
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011735#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011736/*
11737 * Lists available builtins
11738 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011739static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011740helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011741{
11742 int col, i;
11743
11744 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011745 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011746 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011747 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011748 if (col > 60) {
11749 out1fmt("\n");
11750 col = 0;
11751 }
11752 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011753#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011754 {
11755 const char *a = applet_names;
11756 while (*a) {
11757 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11758 if (col > 60) {
11759 out1fmt("\n");
11760 col = 0;
11761 }
11762 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011763 }
11764 }
11765#endif
11766 out1fmt("\n\n");
11767 return EXIT_SUCCESS;
11768}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011769#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011770
Eric Andersencb57d552001-06-28 07:25:16 +000011771/*
Eric Andersencb57d552001-06-28 07:25:16 +000011772 * The export and readonly commands.
11773 */
Eric Andersenc470f442003-07-28 09:56:35 +000011774static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011775exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011776{
11777 struct var *vp;
11778 char *name;
11779 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011780 char **aptr;
11781 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011782
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011783 if (nextopt("p") != 'p') {
11784 aptr = argptr;
11785 name = *aptr;
11786 if (name) {
11787 do {
11788 p = strchr(name, '=');
11789 if (p != NULL) {
11790 p++;
11791 } else {
11792 vp = *findvar(hashvar(name), name);
11793 if (vp) {
11794 vp->flags |= flag;
11795 continue;
11796 }
Eric Andersencb57d552001-06-28 07:25:16 +000011797 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011798 setvar(name, p, flag);
11799 } while ((name = *++aptr) != NULL);
11800 return 0;
11801 }
Eric Andersencb57d552001-06-28 07:25:16 +000011802 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011803 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011804 return 0;
11805}
11806
Eric Andersencb57d552001-06-28 07:25:16 +000011807/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011808 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011809 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011810static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011811unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011812{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011813 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011814
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011815 cmdp = cmdlookup(name, 0);
11816 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11817 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011818}
11819
Eric Andersencb57d552001-06-28 07:25:16 +000011820/*
Eric Andersencb57d552001-06-28 07:25:16 +000011821 * The unset builtin command. We unset the function before we unset the
11822 * variable to allow a function to be unset when there is a readonly variable
11823 * with the same name.
11824 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011825static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011826unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011827{
11828 char **ap;
11829 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011830 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011831 int ret = 0;
11832
11833 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011834 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011835 }
Eric Andersencb57d552001-06-28 07:25:16 +000011836
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011837 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011838 if (flag != 'f') {
11839 i = unsetvar(*ap);
11840 ret |= i;
11841 if (!(i & 2))
11842 continue;
11843 }
11844 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011845 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011846 }
Eric Andersenc470f442003-07-28 09:56:35 +000011847 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011848}
11849
11850
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011851/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011852
Eric Andersenc470f442003-07-28 09:56:35 +000011853#include <sys/times.h>
11854
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011855static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011856 ' ', offsetof(struct tms, tms_utime),
11857 '\n', offsetof(struct tms, tms_stime),
11858 ' ', offsetof(struct tms, tms_cutime),
11859 '\n', offsetof(struct tms, tms_cstime),
11860 0
11861};
Eric Andersencb57d552001-06-28 07:25:16 +000011862
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011863static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011864timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011865{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011866 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011867 const unsigned char *p;
11868 struct tms buf;
11869
11870 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011871 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011872
11873 p = timescmd_str;
11874 do {
11875 t = *(clock_t *)(((char *) &buf) + p[1]);
11876 s = t / clk_tck;
11877 out1fmt("%ldm%ld.%.3lds%c",
11878 s/60, s%60,
11879 ((t - s * clk_tck) * 1000) / clk_tck,
11880 p[0]);
11881 } while (*(p += 2));
11882
Eric Andersencb57d552001-06-28 07:25:16 +000011883 return 0;
11884}
11885
Denis Vlasenko131ae172007-02-18 13:00:19 +000011886#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011887static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011888dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011889{
Eric Andersened9ecf72004-06-22 08:29:45 +000011890 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011891 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011892
Denis Vlasenkob012b102007-02-19 22:43:01 +000011893 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011894 result = arith(s, &errcode);
11895 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011896 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011897 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011898 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011899 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011900 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011901 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011902 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011903 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011904 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011905
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011906 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011907}
Eric Andersenc470f442003-07-28 09:56:35 +000011908
Eric Andersenc470f442003-07-28 09:56:35 +000011909/*
Eric Andersen90898442003-08-06 11:20:52 +000011910 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11911 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11912 *
11913 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011914 */
11915static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011916letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011917{
Denis Vlasenko68404f12008-03-17 09:00:54 +000011918 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000011919
Denis Vlasenko68404f12008-03-17 09:00:54 +000011920 argv++;
11921 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011922 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000011923 do {
11924 i = dash_arith(*argv);
11925 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000011926
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011927 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011928}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011929#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011930
Eric Andersenc470f442003-07-28 09:56:35 +000011931
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011932/* ============ miscbltin.c
11933 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011934 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011935 */
11936
11937#undef rflag
11938
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011939#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011940typedef enum __rlimit_resource rlim_t;
11941#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011942
Eric Andersenc470f442003-07-28 09:56:35 +000011943/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011944 * The read builtin. Options:
11945 * -r Do not interpret '\' specially
11946 * -s Turn off echo (tty only)
11947 * -n NCHARS Read NCHARS max
11948 * -p PROMPT Display PROMPT on stderr (if input is from tty)
11949 * -t SECONDS Timeout after SECONDS (tty or pipe only)
11950 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000011951 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011952 * TODO: bash also has:
11953 * -a ARRAY Read into array[0],[1],etc
11954 * -d DELIM End on DELIM char, not newline
11955 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000011956 */
Eric Andersenc470f442003-07-28 09:56:35 +000011957static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011958readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011959{
11960 char **ap;
11961 int backslash;
11962 char c;
11963 int rflag;
11964 char *prompt;
11965 const char *ifs;
11966 char *p;
11967 int startword;
11968 int status;
11969 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011970 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011971#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011972 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000011973 int silent = 0;
11974 struct termios tty, old_tty;
11975#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011976#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011977 unsigned end_ms = 0;
11978 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011979#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011980
11981 rflag = 0;
11982 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011983 while ((i = nextopt("p:u:r"
11984 USE_ASH_READ_TIMEOUT("t:")
11985 USE_ASH_READ_NCHARS("n:s")
11986 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011987 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011988 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011989 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011990 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011991#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011992 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011993 nchars = bb_strtou(optionarg, NULL, 10);
11994 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011995 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011996 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000011997 break;
11998 case 's':
11999 silent = 1;
12000 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012001#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012002#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012003 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012004 timeout = bb_strtou(optionarg, NULL, 10);
12005 if (errno || timeout > UINT_MAX / 2048)
12006 ash_msg_and_raise_error("invalid timeout");
12007 timeout *= 1000;
12008#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012009 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012010 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012011 /* EINVAL means number is ok, but not terminated by NUL */
12012 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012013 char *p2;
12014 if (*++p) {
12015 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012016 ts.tv_usec = bb_strtou(p, &p2, 10);
12017 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012018 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012019 scale = p2 - p;
12020 /* normalize to usec */
12021 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012022 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012023 while (scale++ < 6)
12024 ts.tv_usec *= 10;
12025 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012026 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012027 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012028 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012029 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012030 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012031 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012032#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012033 break;
12034#endif
12035 case 'r':
12036 rflag = 1;
12037 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012038 case 'u':
12039 fd = bb_strtou(optionarg, NULL, 10);
12040 if (fd < 0 || errno)
12041 ash_msg_and_raise_error("invalid file descriptor");
12042 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012043 default:
12044 break;
12045 }
Eric Andersenc470f442003-07-28 09:56:35 +000012046 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012047 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012048 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012049 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012050 ap = argptr;
12051 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012052 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012053 ifs = bltinlookup("IFS");
12054 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012055 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012056#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012057 tcgetattr(fd, &tty);
12058 old_tty = tty;
12059 if (nchars || silent) {
12060 if (nchars) {
12061 tty.c_lflag &= ~ICANON;
12062 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012063 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012064 if (silent) {
12065 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12066 }
12067 /* if tcgetattr failed, tcsetattr will fail too.
12068 * Ignoring, it's harmless. */
12069 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012070 }
12071#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012072
Eric Andersenc470f442003-07-28 09:56:35 +000012073 status = 0;
12074 startword = 1;
12075 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012076#if ENABLE_ASH_READ_TIMEOUT
12077 if (timeout) /* NB: ensuring end_ms is nonzero */
12078 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12079#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012080 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012081 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012082#if ENABLE_ASH_READ_TIMEOUT
12083 if (end_ms) {
12084 struct pollfd pfd[1];
12085 pfd[0].fd = fd;
12086 pfd[0].events = POLLIN;
12087 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12088 if ((int)timeout <= 0 /* already late? */
12089 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12090 ) { /* timed out! */
12091#if ENABLE_ASH_READ_NCHARS
12092 tcsetattr(fd, TCSANOW, &old_tty);
12093#endif
12094 return 1;
12095 }
12096 }
12097#endif
12098 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012099 status = 1;
12100 break;
12101 }
12102 if (c == '\0')
12103 continue;
12104 if (backslash) {
12105 backslash = 0;
12106 if (c != '\n')
12107 goto put;
12108 continue;
12109 }
12110 if (!rflag && c == '\\') {
12111 backslash++;
12112 continue;
12113 }
12114 if (c == '\n')
12115 break;
12116 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12117 continue;
12118 }
12119 startword = 0;
12120 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12121 STACKSTRNUL(p);
12122 setvar(*ap, stackblock(), 0);
12123 ap++;
12124 startword = 1;
12125 STARTSTACKSTR(p);
12126 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012127 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012128 STPUTC(c, p);
12129 }
12130 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012131/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012132#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012133 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012134#else
12135 while (1);
12136#endif
12137
12138#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012139 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012140#endif
12141
Eric Andersenc470f442003-07-28 09:56:35 +000012142 STACKSTRNUL(p);
12143 /* Remove trailing blanks */
12144 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12145 *p = '\0';
12146 setvar(*ap, stackblock(), 0);
12147 while (*++ap != NULL)
12148 setvar(*ap, nullstr, 0);
12149 return status;
12150}
12151
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012152static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012153umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012154{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012155 static const char permuser[3] ALIGN1 = "ugo";
12156 static const char permmode[3] ALIGN1 = "rwx";
12157 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012158 S_IRUSR, S_IWUSR, S_IXUSR,
12159 S_IRGRP, S_IWGRP, S_IXGRP,
12160 S_IROTH, S_IWOTH, S_IXOTH
12161 };
12162
12163 char *ap;
12164 mode_t mask;
12165 int i;
12166 int symbolic_mode = 0;
12167
12168 while (nextopt("S") != '\0') {
12169 symbolic_mode = 1;
12170 }
12171
Denis Vlasenkob012b102007-02-19 22:43:01 +000012172 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012173 mask = umask(0);
12174 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012175 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012176
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012177 ap = *argptr;
12178 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012179 if (symbolic_mode) {
12180 char buf[18];
12181 char *p = buf;
12182
12183 for (i = 0; i < 3; i++) {
12184 int j;
12185
12186 *p++ = permuser[i];
12187 *p++ = '=';
12188 for (j = 0; j < 3; j++) {
12189 if ((mask & permmask[3 * i + j]) == 0) {
12190 *p++ = permmode[j];
12191 }
12192 }
12193 *p++ = ',';
12194 }
12195 *--p = 0;
12196 puts(buf);
12197 } else {
12198 out1fmt("%.4o\n", mask);
12199 }
12200 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012201 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012202 mask = 0;
12203 do {
12204 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012205 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012206 mask = (mask << 3) + (*ap - '0');
12207 } while (*++ap != '\0');
12208 umask(mask);
12209 } else {
12210 mask = ~mask & 0777;
12211 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012212 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012213 }
12214 umask(~mask & 0777);
12215 }
12216 }
12217 return 0;
12218}
12219
12220/*
12221 * ulimit builtin
12222 *
12223 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12224 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12225 * ash by J.T. Conklin.
12226 *
12227 * Public domain.
12228 */
12229
12230struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012231 uint8_t cmd; /* RLIMIT_xxx fit into it */
12232 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012233 char option;
12234};
12235
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012236static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012237#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012238 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012239#endif
12240#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012241 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012242#endif
12243#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012244 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012245#endif
12246#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012247 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012248#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012249#ifdef RLIMIT_CORE
12250 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012251#endif
12252#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012253 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012254#endif
12255#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012256 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012257#endif
12258#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012259 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012260#endif
12261#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012262 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012263#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012264#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012265 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012266#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012267#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012268 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012269#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012270};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012271static const char limits_name[] =
12272#ifdef RLIMIT_CPU
12273 "time(seconds)" "\0"
12274#endif
12275#ifdef RLIMIT_FSIZE
12276 "file(blocks)" "\0"
12277#endif
12278#ifdef RLIMIT_DATA
12279 "data(kb)" "\0"
12280#endif
12281#ifdef RLIMIT_STACK
12282 "stack(kb)" "\0"
12283#endif
12284#ifdef RLIMIT_CORE
12285 "coredump(blocks)" "\0"
12286#endif
12287#ifdef RLIMIT_RSS
12288 "memory(kb)" "\0"
12289#endif
12290#ifdef RLIMIT_MEMLOCK
12291 "locked memory(kb)" "\0"
12292#endif
12293#ifdef RLIMIT_NPROC
12294 "process" "\0"
12295#endif
12296#ifdef RLIMIT_NOFILE
12297 "nofiles" "\0"
12298#endif
12299#ifdef RLIMIT_AS
12300 "vmemory(kb)" "\0"
12301#endif
12302#ifdef RLIMIT_LOCKS
12303 "locks" "\0"
12304#endif
12305;
Eric Andersenc470f442003-07-28 09:56:35 +000012306
Glenn L McGrath76620622004-01-13 10:19:37 +000012307enum limtype { SOFT = 0x1, HARD = 0x2 };
12308
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012309static void
12310printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012311 const struct limits *l)
12312{
12313 rlim_t val;
12314
12315 val = limit->rlim_max;
12316 if (how & SOFT)
12317 val = limit->rlim_cur;
12318
12319 if (val == RLIM_INFINITY)
12320 out1fmt("unlimited\n");
12321 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012322 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012323 out1fmt("%lld\n", (long long) val);
12324 }
12325}
12326
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012327static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012328ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012329{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012330 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012331 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012332 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012333 const struct limits *l;
12334 int set, all = 0;
12335 int optc, what;
12336 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012337
12338 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012339 while ((optc = nextopt("HSa"
12340#ifdef RLIMIT_CPU
12341 "t"
12342#endif
12343#ifdef RLIMIT_FSIZE
12344 "f"
12345#endif
12346#ifdef RLIMIT_DATA
12347 "d"
12348#endif
12349#ifdef RLIMIT_STACK
12350 "s"
12351#endif
12352#ifdef RLIMIT_CORE
12353 "c"
12354#endif
12355#ifdef RLIMIT_RSS
12356 "m"
12357#endif
12358#ifdef RLIMIT_MEMLOCK
12359 "l"
12360#endif
12361#ifdef RLIMIT_NPROC
12362 "p"
12363#endif
12364#ifdef RLIMIT_NOFILE
12365 "n"
12366#endif
12367#ifdef RLIMIT_AS
12368 "v"
12369#endif
12370#ifdef RLIMIT_LOCKS
12371 "w"
12372#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012373 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012374 switch (optc) {
12375 case 'H':
12376 how = HARD;
12377 break;
12378 case 'S':
12379 how = SOFT;
12380 break;
12381 case 'a':
12382 all = 1;
12383 break;
12384 default:
12385 what = optc;
12386 }
12387
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012388 for (l = limits_tbl; l->option != what; l++)
12389 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012390
12391 set = *argptr ? 1 : 0;
12392 if (set) {
12393 char *p = *argptr;
12394
12395 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012396 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012397 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012398 val = RLIM_INFINITY;
12399 else {
12400 val = (rlim_t) 0;
12401
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012403 val = (val * 10) + (long)(c - '0');
12404 if (val < (rlim_t) 0)
12405 break;
12406 }
12407 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012408 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012409 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012410 }
12411 }
12412 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012413 const char *lname = limits_name;
12414 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012415 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012416 out1fmt("%-20s ", lname);
12417 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012418 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012419 }
12420 return 0;
12421 }
12422
12423 getrlimit(l->cmd, &limit);
12424 if (set) {
12425 if (how & HARD)
12426 limit.rlim_max = val;
12427 if (how & SOFT)
12428 limit.rlim_cur = val;
12429 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012430 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012431 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012432 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012433 }
12434 return 0;
12435}
12436
Eric Andersen90898442003-08-06 11:20:52 +000012437
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012438/* ============ Math support */
12439
Denis Vlasenko131ae172007-02-18 13:00:19 +000012440#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012441
12442/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12443
12444 Permission is hereby granted, free of charge, to any person obtaining
12445 a copy of this software and associated documentation files (the
12446 "Software"), to deal in the Software without restriction, including
12447 without limitation the rights to use, copy, modify, merge, publish,
12448 distribute, sublicense, and/or sell copies of the Software, and to
12449 permit persons to whom the Software is furnished to do so, subject to
12450 the following conditions:
12451
12452 The above copyright notice and this permission notice shall be
12453 included in all copies or substantial portions of the Software.
12454
12455 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12456 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12457 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12458 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12459 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12460 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12461 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12462*/
12463
12464/* This is my infix parser/evaluator. It is optimized for size, intended
12465 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012466 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012467 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012468 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012469 * be that which POSIX specifies for shells. */
12470
12471/* The code uses a simple two-stack algorithm. See
12472 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012473 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012474 * this is based (this code differs in that it applies operators immediately
12475 * to the stack instead of adding them to a queue to end up with an
12476 * expression). */
12477
12478/* To use the routine, call it with an expression string and error return
12479 * pointer */
12480
12481/*
12482 * Aug 24, 2001 Manuel Novoa III
12483 *
12484 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12485 *
12486 * 1) In arith_apply():
12487 * a) Cached values of *numptr and &(numptr[-1]).
12488 * b) Removed redundant test for zero denominator.
12489 *
12490 * 2) In arith():
12491 * a) Eliminated redundant code for processing operator tokens by moving
12492 * to a table-based implementation. Also folded handling of parens
12493 * into the table.
12494 * b) Combined all 3 loops which called arith_apply to reduce generated
12495 * code size at the cost of speed.
12496 *
12497 * 3) The following expressions were treated as valid by the original code:
12498 * 1() , 0! , 1 ( *3 ) .
12499 * These bugs have been fixed by internally enclosing the expression in
12500 * parens and then checking that all binary ops and right parens are
12501 * preceded by a valid expression (NUM_TOKEN).
12502 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012503 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012504 * ctype's isspace() if it is used by another busybox applet or if additional
12505 * whitespace chars should be considered. Look below the "#include"s for a
12506 * precompiler test.
12507 */
12508
12509/*
12510 * Aug 26, 2001 Manuel Novoa III
12511 *
12512 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12513 *
12514 * Merge in Aaron's comments previously posted to the busybox list,
12515 * modified slightly to take account of my changes to the code.
12516 *
12517 */
12518
12519/*
12520 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12521 *
12522 * - allow access to variable,
12523 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12524 * - realize assign syntax (VAR=expr, +=, *= etc)
12525 * - realize exponentiation (** operator)
12526 * - realize comma separated - expr, expr
12527 * - realise ++expr --expr expr++ expr--
12528 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012529 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012530 * - was restored loses XOR operator
12531 * - remove one goto label, added three ;-)
12532 * - protect $((num num)) as true zero expr (Manuel`s error)
12533 * - always use special isspace(), see comment from bash ;-)
12534 */
12535
Eric Andersen90898442003-08-06 11:20:52 +000012536#define arith_isspace(arithval) \
12537 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12538
Eric Andersen90898442003-08-06 11:20:52 +000012539typedef unsigned char operator;
12540
12541/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012542 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012543 * precedence. The ID portion is so that multiple operators can have the
12544 * same precedence, ensuring that the leftmost one is evaluated first.
12545 * Consider * and /. */
12546
12547#define tok_decl(prec,id) (((id)<<5)|(prec))
12548#define PREC(op) ((op) & 0x1F)
12549
12550#define TOK_LPAREN tok_decl(0,0)
12551
12552#define TOK_COMMA tok_decl(1,0)
12553
12554#define TOK_ASSIGN tok_decl(2,0)
12555#define TOK_AND_ASSIGN tok_decl(2,1)
12556#define TOK_OR_ASSIGN tok_decl(2,2)
12557#define TOK_XOR_ASSIGN tok_decl(2,3)
12558#define TOK_PLUS_ASSIGN tok_decl(2,4)
12559#define TOK_MINUS_ASSIGN tok_decl(2,5)
12560#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12561#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12562
12563#define TOK_MUL_ASSIGN tok_decl(3,0)
12564#define TOK_DIV_ASSIGN tok_decl(3,1)
12565#define TOK_REM_ASSIGN tok_decl(3,2)
12566
12567/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012568#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012569
12570/* conditional is right associativity too */
12571#define TOK_CONDITIONAL tok_decl(4,0)
12572#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12573
12574#define TOK_OR tok_decl(5,0)
12575
12576#define TOK_AND tok_decl(6,0)
12577
12578#define TOK_BOR tok_decl(7,0)
12579
12580#define TOK_BXOR tok_decl(8,0)
12581
12582#define TOK_BAND tok_decl(9,0)
12583
12584#define TOK_EQ tok_decl(10,0)
12585#define TOK_NE tok_decl(10,1)
12586
12587#define TOK_LT tok_decl(11,0)
12588#define TOK_GT tok_decl(11,1)
12589#define TOK_GE tok_decl(11,2)
12590#define TOK_LE tok_decl(11,3)
12591
12592#define TOK_LSHIFT tok_decl(12,0)
12593#define TOK_RSHIFT tok_decl(12,1)
12594
12595#define TOK_ADD tok_decl(13,0)
12596#define TOK_SUB tok_decl(13,1)
12597
12598#define TOK_MUL tok_decl(14,0)
12599#define TOK_DIV tok_decl(14,1)
12600#define TOK_REM tok_decl(14,2)
12601
12602/* exponent is right associativity */
12603#define TOK_EXPONENT tok_decl(15,1)
12604
12605/* For now unary operators. */
12606#define UNARYPREC 16
12607#define TOK_BNOT tok_decl(UNARYPREC,0)
12608#define TOK_NOT tok_decl(UNARYPREC,1)
12609
12610#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12611#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12612
12613#define PREC_PRE (UNARYPREC+2)
12614
12615#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12616#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12617
12618#define PREC_POST (UNARYPREC+3)
12619
12620#define TOK_POST_INC tok_decl(PREC_POST, 0)
12621#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12622
12623#define SPEC_PREC (UNARYPREC+4)
12624
12625#define TOK_NUM tok_decl(SPEC_PREC, 0)
12626#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12627
12628#define NUMPTR (*numstackptr)
12629
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012630static int
12631tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012632{
12633 operator prec = PREC(op);
12634
12635 convert_prec_is_assing(prec);
12636 return (prec == PREC(TOK_ASSIGN) ||
12637 prec == PREC_PRE || prec == PREC_POST);
12638}
12639
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012640static int
12641is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012642{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012643 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12644 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012645}
12646
Eric Andersen90898442003-08-06 11:20:52 +000012647typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012648 arith_t val;
12649 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012650 char contidional_second_val_initialized;
12651 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012652 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012653} v_n_t;
12654
Eric Andersen90898442003-08-06 11:20:52 +000012655typedef struct CHK_VAR_RECURSIVE_LOOPED {
12656 const char *var;
12657 struct CHK_VAR_RECURSIVE_LOOPED *next;
12658} chk_var_recursive_looped_t;
12659
12660static chk_var_recursive_looped_t *prev_chk_var_recursive;
12661
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012662static int
12663arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012664{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012665 if (t->var) {
12666 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012667
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012668 if (p) {
12669 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012670
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012671 /* recursive try as expression */
12672 chk_var_recursive_looped_t *cur;
12673 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012674
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012675 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12676 if (strcmp(cur->var, t->var) == 0) {
12677 /* expression recursion loop detected */
12678 return -5;
12679 }
12680 }
12681 /* save current lookuped var name */
12682 cur = prev_chk_var_recursive;
12683 cur_save.var = t->var;
12684 cur_save.next = cur;
12685 prev_chk_var_recursive = &cur_save;
12686
12687 t->val = arith (p, &errcode);
12688 /* restore previous ptr after recursiving */
12689 prev_chk_var_recursive = cur;
12690 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012691 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012692 /* allow undefined var as 0 */
12693 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012694 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012695 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012696}
12697
12698/* "applying" a token means performing it on the top elements on the integer
12699 * stack. For a unary operator it will only change the top element, but a
12700 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012701static int
12702arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012703{
Eric Andersen90898442003-08-06 11:20:52 +000012704 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012705 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012706 int ret_arith_lookup_val;
12707
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012708 /* There is no operator that can work without arguments */
12709 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012710 numptr_m1 = NUMPTR - 1;
12711
12712 /* check operand is var with noninteger value */
12713 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012714 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012715 return ret_arith_lookup_val;
12716
12717 rez = numptr_m1->val;
12718 if (op == TOK_UMINUS)
12719 rez *= -1;
12720 else if (op == TOK_NOT)
12721 rez = !rez;
12722 else if (op == TOK_BNOT)
12723 rez = ~rez;
12724 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12725 rez++;
12726 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12727 rez--;
12728 else if (op != TOK_UPLUS) {
12729 /* Binary operators */
12730
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012731 /* check and binary operators need two arguments */
12732 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012733
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012734 /* ... and they pop one */
12735 --NUMPTR;
12736 numptr_val = rez;
12737 if (op == TOK_CONDITIONAL) {
12738 if (! numptr_m1->contidional_second_val_initialized) {
12739 /* protect $((expr1 ? expr2)) without ": expr" */
12740 goto err;
12741 }
12742 rez = numptr_m1->contidional_second_val;
12743 } else if (numptr_m1->contidional_second_val_initialized) {
12744 /* protect $((expr1 : expr2)) without "expr ? " */
12745 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012746 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012747 numptr_m1 = NUMPTR - 1;
12748 if (op != TOK_ASSIGN) {
12749 /* check operand is var with noninteger value for not '=' */
12750 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12751 if (ret_arith_lookup_val)
12752 return ret_arith_lookup_val;
12753 }
12754 if (op == TOK_CONDITIONAL) {
12755 numptr_m1->contidional_second_val = rez;
12756 }
12757 rez = numptr_m1->val;
12758 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012759 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012760 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012761 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012762 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012763 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012764 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012765 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012766 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012767 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012768 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012769 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012770 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012771 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012772 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012773 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012774 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012775 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012776 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012777 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012778 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012779 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012780 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012781 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012782 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012783 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012784 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012785 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012786 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012787 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012788 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012789 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012790 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012791 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012792 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012793 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012794 /* protect $((expr : expr)) without "expr ? " */
12795 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012796 }
12797 numptr_m1->contidional_second_val_initialized = op;
12798 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012799 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012800 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012801 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012802 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012803 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012804 return -3; /* exponent less than 0 */
12805 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012806 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012807
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012808 if (numptr_val)
12809 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012810 c *= rez;
12811 rez = c;
12812 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012813 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012814 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012815 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012816 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012817 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012818 rez %= numptr_val;
12819 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012820 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012821 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012822
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012823 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012824 /* Hmm, 1=2 ? */
12825 goto err;
12826 }
12827 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012828#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012829 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012830#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012831 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012832#endif
Eric Andersen90898442003-08-06 11:20:52 +000012833 setvar(numptr_m1->var, buf, 0);
12834 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012835 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012836 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012837 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012838 rez++;
12839 }
12840 numptr_m1->val = rez;
12841 /* protect geting var value, is number now */
12842 numptr_m1->var = NULL;
12843 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012844 err:
12845 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012846}
12847
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012848/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012849static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012850 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12851 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12852 '<','<', 0, TOK_LSHIFT,
12853 '>','>', 0, TOK_RSHIFT,
12854 '|','|', 0, TOK_OR,
12855 '&','&', 0, TOK_AND,
12856 '!','=', 0, TOK_NE,
12857 '<','=', 0, TOK_LE,
12858 '>','=', 0, TOK_GE,
12859 '=','=', 0, TOK_EQ,
12860 '|','=', 0, TOK_OR_ASSIGN,
12861 '&','=', 0, TOK_AND_ASSIGN,
12862 '*','=', 0, TOK_MUL_ASSIGN,
12863 '/','=', 0, TOK_DIV_ASSIGN,
12864 '%','=', 0, TOK_REM_ASSIGN,
12865 '+','=', 0, TOK_PLUS_ASSIGN,
12866 '-','=', 0, TOK_MINUS_ASSIGN,
12867 '-','-', 0, TOK_POST_DEC,
12868 '^','=', 0, TOK_XOR_ASSIGN,
12869 '+','+', 0, TOK_POST_INC,
12870 '*','*', 0, TOK_EXPONENT,
12871 '!', 0, TOK_NOT,
12872 '<', 0, TOK_LT,
12873 '>', 0, TOK_GT,
12874 '=', 0, TOK_ASSIGN,
12875 '|', 0, TOK_BOR,
12876 '&', 0, TOK_BAND,
12877 '*', 0, TOK_MUL,
12878 '/', 0, TOK_DIV,
12879 '%', 0, TOK_REM,
12880 '+', 0, TOK_ADD,
12881 '-', 0, TOK_SUB,
12882 '^', 0, TOK_BXOR,
12883 /* uniq */
12884 '~', 0, TOK_BNOT,
12885 ',', 0, TOK_COMMA,
12886 '?', 0, TOK_CONDITIONAL,
12887 ':', 0, TOK_CONDITIONAL_SEP,
12888 ')', 0, TOK_RPAREN,
12889 '(', 0, TOK_LPAREN,
12890 0
12891};
12892/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012893#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000012894
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012895static arith_t
12896arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012897{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012898 char arithval; /* Current character under analysis */
12899 operator lasttok, op;
12900 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012901
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012902 const char *p = endexpression;
12903 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012904
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012905 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012906
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 /* Stack of integers */
12908 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12909 * in any given correct or incorrect expression is left as an exercise to
12910 * the reader. */
12911 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12912 *numstackptr = numstack;
12913 /* Stack of operator tokens */
12914 operator *stack = alloca((datasizes) * sizeof(operator)),
12915 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012916
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012917 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12918 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012919
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012920 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012921 arithval = *expr;
12922 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012923 if (p == endexpression) {
12924 /* Null expression. */
12925 return 0;
12926 }
12927
12928 /* This is only reached after all tokens have been extracted from the
12929 * input stream. If there are still tokens on the operator stack, they
12930 * are to be applied in order. At the end, there should be a final
12931 * result on the integer stack */
12932
12933 if (expr != endexpression + 1) {
12934 /* If we haven't done so already, */
12935 /* append a closing right paren */
12936 expr = endexpression;
12937 /* and let the loop process it. */
12938 continue;
12939 }
12940 /* At this point, we're done with the expression. */
12941 if (numstackptr != numstack+1) {
12942 /* ... but if there isn't, it's bad */
12943 err:
12944 return (*perrcode = -1);
12945 }
12946 if (numstack->var) {
12947 /* expression is $((var)) only, lookup now */
12948 errcode = arith_lookup_val(numstack);
12949 }
12950 ret:
12951 *perrcode = errcode;
12952 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012953 }
12954
Eric Andersen90898442003-08-06 11:20:52 +000012955 /* Continue processing the expression. */
12956 if (arith_isspace(arithval)) {
12957 /* Skip whitespace */
12958 goto prologue;
12959 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012960 p = endofname(expr);
12961 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012962 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012963
12964 numstackptr->var = alloca(var_name_size);
12965 safe_strncpy(numstackptr->var, expr, var_name_size);
12966 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012967 num:
Eric Andersen90898442003-08-06 11:20:52 +000012968 numstackptr->contidional_second_val_initialized = 0;
12969 numstackptr++;
12970 lasttok = TOK_NUM;
12971 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012972 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012973 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012974 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012975#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012976 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012977#else
12978 numstackptr->val = strtol(expr, (char **) &expr, 0);
12979#endif
Eric Andersen90898442003-08-06 11:20:52 +000012980 goto num;
12981 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012982 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012983 const char *o;
12984
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012985 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012986 /* strange operator not found */
12987 goto err;
12988 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012989 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012990 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012991 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012992 /* found */
12993 expr = o - 1;
12994 break;
12995 }
12996 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012997 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012998 p++;
12999 /* skip zero delim */
13000 p++;
13001 }
13002 op = p[1];
13003
13004 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013005 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13006 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013007
13008 /* Plus and minus are binary (not unary) _only_ if the last
13009 * token was as number, or a right paren (which pretends to be
13010 * a number, since it evaluates to one). Think about it.
13011 * It makes sense. */
13012 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013013 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 case TOK_ADD:
13015 op = TOK_UPLUS;
13016 break;
13017 case TOK_SUB:
13018 op = TOK_UMINUS;
13019 break;
13020 case TOK_POST_INC:
13021 op = TOK_PRE_INC;
13022 break;
13023 case TOK_POST_DEC:
13024 op = TOK_PRE_DEC;
13025 break;
Eric Andersen90898442003-08-06 11:20:52 +000013026 }
13027 }
13028 /* We don't want a unary operator to cause recursive descent on the
13029 * stack, because there can be many in a row and it could cause an
13030 * operator to be evaluated before its argument is pushed onto the
13031 * integer stack. */
13032 /* But for binary operators, "apply" everything on the operator
13033 * stack until we find an operator with a lesser priority than the
13034 * one we have just extracted. */
13035 /* Left paren is given the lowest priority so it will never be
13036 * "applied" in this way.
13037 * if associativity is right and priority eq, applied also skip
13038 */
13039 prec = PREC(op);
13040 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13041 /* not left paren or unary */
13042 if (lasttok != TOK_NUM) {
13043 /* binary op must be preceded by a num */
13044 goto err;
13045 }
13046 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013047 if (op == TOK_RPAREN) {
13048 /* The algorithm employed here is simple: while we don't
13049 * hit an open paren nor the bottom of the stack, pop
13050 * tokens and apply them */
13051 if (stackptr[-1] == TOK_LPAREN) {
13052 --stackptr;
13053 /* Any operator directly after a */
13054 lasttok = TOK_NUM;
13055 /* close paren should consider itself binary */
13056 goto prologue;
13057 }
13058 } else {
13059 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013060
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013061 convert_prec_is_assing(prec);
13062 convert_prec_is_assing(prev_prec);
13063 if (prev_prec < prec)
13064 break;
13065 /* check right assoc */
13066 if (prev_prec == prec && is_right_associativity(prec))
13067 break;
13068 }
13069 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13070 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013071 }
13072 if (op == TOK_RPAREN) {
13073 goto err;
13074 }
13075 }
13076
13077 /* Push this operator to the stack and remember it. */
13078 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013079 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013080 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013082}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013083#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013084
13085
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013086/* ============ main() and helpers */
13087
13088/*
13089 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013090 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013091static void exitshell(void) ATTRIBUTE_NORETURN;
13092static void
13093exitshell(void)
13094{
13095 struct jmploc loc;
13096 char *p;
13097 int status;
13098
13099 status = exitstatus;
13100 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13101 if (setjmp(loc.loc)) {
13102 if (exception == EXEXIT)
13103/* dash bug: it just does _exit(exitstatus) here
13104 * but we have to do setjobctl(0) first!
13105 * (bug is still not fixed in dash-0.5.3 - if you run dash
13106 * under Midnight Commander, on exit from dash MC is backgrounded) */
13107 status = exitstatus;
13108 goto out;
13109 }
13110 exception_handler = &loc;
13111 p = trap[0];
13112 if (p) {
13113 trap[0] = NULL;
13114 evalstring(p, 0);
13115 }
13116 flush_stdout_stderr();
13117 out:
13118 setjobctl(0);
13119 _exit(status);
13120 /* NOTREACHED */
13121}
13122
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013123static void
13124init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013125{
13126 /* from input.c: */
13127 basepf.nextc = basepf.buf = basebuf;
13128
13129 /* from trap.c: */
13130 signal(SIGCHLD, SIG_DFL);
13131
13132 /* from var.c: */
13133 {
13134 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013135 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136 const char *p;
13137 struct stat st1, st2;
13138
13139 initvar();
13140 for (envp = environ; envp && *envp; envp++) {
13141 if (strchr(*envp, '=')) {
13142 setvareq(*envp, VEXPORT|VTEXTFIXED);
13143 }
13144 }
13145
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013146 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013147 setvar("PPID", ppid, 0);
13148
13149 p = lookupvar("PWD");
13150 if (p)
13151 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13152 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13153 p = '\0';
13154 setpwd(p, 0);
13155 }
13156}
13157
13158/*
13159 * Process the shell command line arguments.
13160 */
13161static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013162procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013163{
13164 int i;
13165 const char *xminusc;
13166 char **xargv;
13167
13168 xargv = argv;
13169 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013170 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013171 xargv++;
13172 for (i = 0; i < NOPTS; i++)
13173 optlist[i] = 2;
13174 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013175 if (options(1)) {
13176 /* it already printed err message */
13177 raise_exception(EXERROR);
13178 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013179 xargv = argptr;
13180 xminusc = minusc;
13181 if (*xargv == NULL) {
13182 if (xminusc)
13183 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13184 sflag = 1;
13185 }
13186 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13187 iflag = 1;
13188 if (mflag == 2)
13189 mflag = iflag;
13190 for (i = 0; i < NOPTS; i++)
13191 if (optlist[i] == 2)
13192 optlist[i] = 0;
13193#if DEBUG == 2
13194 debug = 1;
13195#endif
13196 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13197 if (xminusc) {
13198 minusc = *xargv++;
13199 if (*xargv)
13200 goto setarg0;
13201 } else if (!sflag) {
13202 setinputfile(*xargv, 0);
13203 setarg0:
13204 arg0 = *xargv++;
13205 commandname = arg0;
13206 }
13207
13208 shellparam.p = xargv;
13209#if ENABLE_ASH_GETOPTS
13210 shellparam.optind = 1;
13211 shellparam.optoff = -1;
13212#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013213 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013214 while (*xargv) {
13215 shellparam.nparam++;
13216 xargv++;
13217 }
13218 optschanged();
13219}
13220
13221/*
13222 * Read /etc/profile or .profile.
13223 */
13224static void
13225read_profile(const char *name)
13226{
13227 int skip;
13228
13229 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13230 return;
13231 skip = cmdloop(0);
13232 popfile();
13233 if (skip)
13234 exitshell();
13235}
13236
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013237/*
13238 * This routine is called when an error or an interrupt occurs in an
13239 * interactive shell and control is returned to the main command loop.
13240 */
13241static void
13242reset(void)
13243{
13244 /* from eval.c: */
13245 evalskip = 0;
13246 loopnest = 0;
13247 /* from input.c: */
13248 parselleft = parsenleft = 0; /* clear input buffer */
13249 popallfiles();
13250 /* from parser.c: */
13251 tokpushback = 0;
13252 checkkwd = 0;
13253 /* from redir.c: */
13254 clearredir(0);
13255}
13256
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013257#if PROFILE
13258static short profile_buf[16384];
13259extern int etext();
13260#endif
13261
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013262/*
13263 * Main routine. We initialize things, parse the arguments, execute
13264 * profiles if we're a login shell, and then call cmdloop to execute
13265 * commands. The setjmp call sets up the location to jump to when an
13266 * exception occurs. When an exception occurs the variable "state"
13267 * is used to figure out how far we had gotten.
13268 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013269int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013270int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013271{
13272 char *shinit;
13273 volatile int state;
13274 struct jmploc jmploc;
13275 struct stackmark smark;
13276
Denis Vlasenko01631112007-12-16 17:20:38 +000013277 /* Initialize global data */
13278 INIT_G_misc();
13279 INIT_G_memstack();
13280 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013281#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013282 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013283#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013284 INIT_G_cmdtable();
13285
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013286#if PROFILE
13287 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13288#endif
13289
13290#if ENABLE_FEATURE_EDITING
13291 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13292#endif
13293 state = 0;
13294 if (setjmp(jmploc.loc)) {
13295 int e;
13296 int s;
13297
13298 reset();
13299
13300 e = exception;
13301 if (e == EXERROR)
13302 exitstatus = 2;
13303 s = state;
13304 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13305 exitshell();
13306
13307 if (e == EXINT) {
13308 outcslow('\n', stderr);
13309 }
13310 popstackmark(&smark);
13311 FORCE_INT_ON; /* enable interrupts */
13312 if (s == 1)
13313 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013314 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013315 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013316 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013317 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013318 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013319 }
13320 exception_handler = &jmploc;
13321#if DEBUG
13322 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013323 trace_puts("Shell args: ");
13324 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013325#endif
13326 rootpid = getpid();
13327
13328#if ENABLE_ASH_RANDOM_SUPPORT
13329 rseed = rootpid + time(NULL);
13330#endif
13331 init();
13332 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013333 procargs(argv);
13334
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013335#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13336 if (iflag) {
13337 const char *hp = lookupvar("HISTFILE");
13338
13339 if (hp == NULL) {
13340 hp = lookupvar("HOME");
13341 if (hp != NULL) {
13342 char *defhp = concat_path_file(hp, ".ash_history");
13343 setvar("HISTFILE", defhp, 0);
13344 free(defhp);
13345 }
13346 }
13347 }
13348#endif
13349 if (argv[0] && argv[0][0] == '-')
13350 isloginsh = 1;
13351 if (isloginsh) {
13352 state = 1;
13353 read_profile("/etc/profile");
13354 state1:
13355 state = 2;
13356 read_profile(".profile");
13357 }
13358 state2:
13359 state = 3;
13360 if (
13361#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013362 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013363#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013364 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013365 ) {
13366 shinit = lookupvar("ENV");
13367 if (shinit != NULL && *shinit != '\0') {
13368 read_profile(shinit);
13369 }
13370 }
13371 state3:
13372 state = 4;
13373 if (minusc)
13374 evalstring(minusc, 0);
13375
13376 if (sflag || minusc == NULL) {
13377#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13378 if ( iflag ) {
13379 const char *hp = lookupvar("HISTFILE");
13380
13381 if (hp != NULL)
13382 line_input_state->hist_file = hp;
13383 }
13384#endif
13385 state4: /* XXX ??? - why isn't this before the "if" statement */
13386 cmdloop(1);
13387 }
13388#if PROFILE
13389 monitor(0);
13390#endif
13391#ifdef GPROF
13392 {
13393 extern void _mcleanup(void);
13394 _mcleanup();
13395 }
13396#endif
13397 exitshell();
13398 /* NOTREACHED */
13399}
13400
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013401#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013402const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013403int main(int argc, char **argv)
13404{
13405 return ash_main(argc, argv);
13406}
13407#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013408
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013409
Eric Andersendf82f612001-06-28 07:46:40 +000013410/*-
13411 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013412 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013413 *
13414 * This code is derived from software contributed to Berkeley by
13415 * Kenneth Almquist.
13416 *
13417 * Redistribution and use in source and binary forms, with or without
13418 * modification, are permitted provided that the following conditions
13419 * are met:
13420 * 1. Redistributions of source code must retain the above copyright
13421 * notice, this list of conditions and the following disclaimer.
13422 * 2. Redistributions in binary form must reproduce the above copyright
13423 * notice, this list of conditions and the following disclaimer in the
13424 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013425 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013426 * may be used to endorse or promote products derived from this software
13427 * without specific prior written permission.
13428 *
13429 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13430 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13431 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13432 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13433 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13434 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13435 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13436 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13437 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13438 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13439 * SUCH DAMAGE.
13440 */